搜索

首页  >  问答  >  正文

通过Java IO流的形式实现键盘录入的疑问

巴扎黑巴扎黑2804 天前542

全部回复(2)我来回复

  • ringa_lee

    ringa_lee2017-04-18 10:56:47

    因为System.in就是一个静态的InputStream实例,你可以在java api文档上看看

    回复
    0
  • 天蓬老师

    天蓬老师2017-04-18 10:56:47

    首先,System.in是一个InputStream类型的对象,在源码里是这样的:

    /**
     * The "standard" input stream. This stream is already
     * open and ready to supply input data. Typically this stream
     * corresponds to keyboard input or another input source specified by
     * the host environment or user.
     */
    public final static InputStream in = null;

    可见System.in属于标准输入,可以通过键盘或其他方式输入数据。
    但在源码里,该对象并没有(显式)初始化的方法,通过阅读源码,可发现下面这个方法:

    /**
     * Reassigns the "standard" input stream.
     *
     * <p>First, if there is a security manager, its <code>checkPermission</code>
     * method is called with a <code>RuntimePermission("setIO")</code> permission
     *  to see if it's ok to reassign the "standard" input stream.
     * <p>
     *
     * @param in the new standard input stream.
     *
     * @throws SecurityException
     *        if a security manager exists and its
     *        <code>checkPermission</code> method doesn't allow
     *        reassigning of the standard input stream.
     *
     * @see SecurityManager#checkPermission
     * @see java.lang.RuntimePermission
     *
     * @since   JDK1.1
     */
    public static void setIn(InputStream in) {
        checkIO();
        setIn0(in);
    }
    
    private static native void setIn0(InputStream in);

    阅读注释可见该方法用于设定
    研究setIn0(in),可见该方法是通过调用底层接口来实现in的设定,那么在软件运行时,是如何初始化的呢?
    System类中有如下代码:

    /* register the natives via the static initializer.
     *
     * VM will invoke the initializeSystemClass method to complete
     * the initialization for this class separated from clinit.
     * Note that to use properties set by the VM, see the constraints
     * described in the initializeSystemClass method.
     */
    private static native void registerNatives();
    static {
        registerNatives();
    }
    
    /**
     * Initialize the system class.  Called after thread initialization.
     */
    private static void initializeSystemClass() {
    ...
    FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
    FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
    FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
    setIn0(new BufferedInputStream(fdIn));
    setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
    setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
    ...
    }

    由此段代码可知,软件运行时,先运行静态代码块,调用registerNatives()这个底层方法对System类进行初始化,该方法则是调用initializeSystemClass()方法来初始化System类的,这两步都是通过VM实现的,然后阅读initializeSystemClass()这个方法,可见到调用setIn0,setOut0,steErr0三段代码,分别初始化了标准输入,标准输出,标准错误三种输出流,至此System.in、out、err的初始化流程就都弄清楚了。
    标记为native的方法是JVM调用其他代码实现的功能,这个和底层有关系,我觉得这个感觉就像是Bootstrap Classloader实际上是C实现,但由JVM调用来加载各个基础JAR classes相似吧。
    另外你说InputStream是抽象类(接口),实际上接口类抽象类都可以作为参数,但实现肯定不是他们实现的,他们不能被实现,但可以作为参数,毕竟父类出现的地方子类都可以替代,所以这里不存在问题

    回复
    0
  • 取消回复