• 不懂这些,你敢说自己知道Java标准输入输出流?


    哈喽,大家好,我是强哥。

    今天我们来讲讲Java的标准输入输出流,哈哈,虽然是基础,但是往深了挖挖,还是有许多干货的。

    我们先来看个面试题。

    说说获取用键盘输入常用的两种方法

    方法 1:通过 Scanner

    Scanner input = new Scanner(System.in); 
    String s = input.nextLine(); 
    input.close();
    

    方法 2:通过 BufferedReader

    BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); 
    String s = input.readLine();
    

    问题很简答,初学Java的在控制台写简单main方法的时候,都是用的System.in这个方式来获取键盘输入的内容。

    可是,强哥想问问,大家既然会用System.in,但是真的熟悉System这个类吗?

    System类中的IO知识

    Java遵循标准I/O的模型,提供了Syetem.in,System.out,以及System.err。System.out是一个已经预先处理过的,被包装成PrintStream的对象。和System.out一样,System.err也是一个PrintStream,但是System.in不是,它是一个未经处理的InputStream。也就是说,可以直接使用System.out和System.err打印输出到控制台,但是使用System.in直接读取数据不可以,必须先做处理。

    我们看看System的三个成员变量定义:

    //标准输入流,一般接收键盘输入
    public final static InputStream in = null;
    //标准输出流,向控制台输出正确信息
    public final static PrintStream out = null
    //标准输出流,向控制台输出错误信息
    public final static PrintStream err = null;
    

    从上面的代码我们可以看出,这些对象被final修饰且赋值为null,那么为什么上面获取输入输出的时候又可以用而不会报空指针呢?

    我们看下面代码就知道了:

    static {
        registerNatives();
    }
    private static native void registerNatives();
    

    没错是在native方法中做的处理,具体在VM中会执行这个方法

    private static void initializeSystemClass() {
        props = new Properties();
        initProperties(props);  // initialized by the VM
        sun.misc.VM.saveAndRemoveProperties(props);
        lineSeparator = props.getProperty("line.separator");
        sun.misc.Version.init();
        FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
        FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
        FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
        setIn0(new BufferedInputStream(fdIn));
        setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
        setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));
        loadLibrary("zip");
        Terminator.setup();
        sun.misc.VM.initializeOSEnvironment();
        Thread current = Thread.currentThread();
        current.getThreadGroup().add(current);
        setJavaLangAccess();
        sun.misc.VM.booted();
    }
    

    setIn0(new BufferedInputStream(fdIn));设置标准输入流,也就是创建in对象,追溯到源头也就是创建了FileDescriptor.in对象,这个对象用于操作标准输入流,再往深了就到了虚拟机内部实现细节了。

    那么到底什么是标准的输入输出流呢?

    什么是标准输入、标准输出(stdin、stdout)?

    要弄清什么是标准输入输出。首先需要弄懂什么是IO。
    I:从外部设备输入到内存;
    O:从内存输出到外部设备;

    而标准输入和标准输出是干什么的?它们是用于 IO 的。
    属于IO的外部设备部分(逻辑上的外部设备,为什么?接着看)。

    在linux操作系统中,外部设备用什么表示?linux中一切设备皆是文件!
    因此标准输入和输出更具体的含义是文件。

    它们是哪两个文件?
    它们是/dev/stdin这个文件和/dev/stdout这个文件。
    也就是说所谓的标准输入和标准输出其实就是两个linux下的文件。

    那么所谓的从标准输入读是什么意思?
    逻辑上来看:就是打开/dev/stdin这个文件,然后把这个文件里的内容读进来。

    输出到标准输出是什么意思?
    逻辑上来看:就是打开/dev/stdout这个文件,然后把内容输出到这个文件里去。

    为什么是从逻辑上来看?因为它们不是设备文件!!!
    所以它们不代表一个设备。linux里一切皆是文件,设备是文件,但是文件不一定是设备!

    那它们是什么文件?他们是链接文件。(可以用 ls -l /dev 来查看 l 开头的就是链接文件。也可以用 file 命令查看其文件类型,命令:file /dev/stdin)

    什么是链接文件?文件内容是另一个文件的地址的文件称为链接文件。
    因此,打开、读或者写/dev/stdin和/dev/stdout实际上是打开、读或者写这两个文件存放的地址对应的设备文件。

    那么,既然是这样,Java是不是可以对标准输入输出设备进行修改呢?答案是肯定的。继续看。

    点击关注强哥,查看更多精彩文章呀

    修改Java标准输入输出设备

    System类中有如下方法可以进行对标准输入输出设备进行修改

    setIn:

    public static void setIn(InputStream in) {
        checkIO();
        setIn0(in);
    }
    //对setIo进行安全检查
    private static void checkIO() {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setIO"));
        }
    }
    //给System.in重新赋值,内部实现。
    private static native void setIn0(InputStream in);
    

    给System.in对象重新赋值。即当调用了些方法后,我们再使用System.in的时候接收的信息不再是从键盘录入了,而是我们指定的文件或设备。点开函数可以看到,第一个函数是进行安全检查的,第二个函数是调用的本地方法。有人疑惑System.in变量不是被final修饰了吗,为什么还可以被修改。额...这个...它调用的是本地方法,由虚拟机内部实现,他是老大能不能改变他说了算。

    setOut:

    public static void setOut(PrintStream out) {
        checkIO();
        setOut0(out);
    }
    

    给System.out重新赋值,原理同setIn。比如我们想让System.out.println()把内容输出到文件,就可以调用这个方法,指定输出文件。

    setErr:

    public static void setErr(PrintStream err) {
        checkIO();
        setErr0(err);
    }
    

    给System.err重新赋值,原理同setOut。

    写在最后

    综上,就是强哥通过一个面试问题,收集的关于Java标准输入输出相关的知识。

    强哥觉得,很多问题,从表面上回答,很简单,很多初学者都会。可是,如果往深了聊,你是否能够也回答个所以然来,这个是你与其他人的不同。多学习多思考,多关注强哥的公众号推送。

    强哥只讲干货啦~

  • 相关阅读:
    编码和字符集
    【机器学习】模型泛化
    asp.net GridView控件的列属性
    asp.net截取指定长度的字符串内容
    asp.net 对数据库表增加,删除,编辑更新修改
    asp.net 链接数据库ADO.NET
    常用正则表达式 验证电子邮件网址邮政编码等
    ASP.NET获取文件的相关知识
    C#获取picturebox图片路径
    C# dataGridView根据数据调整列宽
  • 原文地址:https://www.cnblogs.com/breakingdawn/p/15925444.html
Copyright © 2020-2023  润新知