zoukankan      html  css  js  c++  java
  • System.out.print实现原理猜解

    我们往往在main中直接调用System.out.print方法来打印,但是其实就这简单的一步里面有很多的玄机,因为main是static的,所以只能调用static的函数,那么print是static的吗?我一直有这个疑问,今天专门查阅了下源码,说下我的理解:(源码只贴出来部分对理解有用的)

    源码里面:public final class System 直接在lang包里面。所以可以直接不通过包名就直接调用system类。里面还有:

    public final static PrintStream out = nullPrintStream();
    ............
    ............

    private static PrintStream nullPrintStream() throws NullPointerException {
    if (currentTimeMillis() > 0) {
    return null;
    }
    throw new NullPointerException();
    }

    可以看出out是system的静态成员,所以可以通过system.out直接访问到,但是这时候又有问题了,因为 nullPrintStream()这个函数返回值是null的,怎么可以调用printstream的方法呢。看out的注释:
    /**
    * The following two methods exist because in, out, and err must be
    * initialized to null. The compiler, however, cannot be permitted to
    * inline access to them, since they are later set to more sensible values
    * by initializeSystemClass().
    */

    大概翻译后知道刚开始的时候确实是都是null的,那么什么时候能把out指向标准输出的呢?注释说看initializeSystemClass()这个函数。

    /**
    * Initialize the system class. Called after thread initialization.
    */
    private static void initializeSystemClass() {
    props = new Properties();
    initProperties(props);
    sun.misc.Version.init();

    FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
    .................
    setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
    ...............

    ...............

     

    接着找到FileDescriptor这个类中的静态成员 out:(用自身定义自身)

    public static final FileDescriptor out = standardStream(1);

    以及standardStream()方法:

    private static FileDescriptor standardStream(int fd) {
    FileDescriptor desc = new FileDescriptor();
    desc.handle = set(fd);
    return desc;
    }

    这时候返回了 handle为1的FileDescriptor; 在传统的unix的系统中,文件描述符为0,1,2分别表示为标准输入,标准输出和错误输出。

    最后调用了setout0方法:

    private static native void setOut0(PrintStream out);

     

    到这里,setout0是native方法,所以再也追踪不到以后的细节了,而到这时候,out的出路似乎还没有找到。但是。在system类中有setout方法即:

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

    调用了setout0()方法,而我们知道setout方法是重置输出流的对象的,因此虽然我们看不到setout0的细节,但是因为setout调用了setout0,我们可以大致猜到setout0是通过调用底层的代码实现对out的流的重定位的,而initializeSystemClass()这个函数也调用了setout0将fd为1的文件封装成文件流,再封装成缓冲流,再封装成打印流,最后通过setout0将out与这个流绑定。这样一切就都说通了。

     

    原文链接:http://www.cnblogs.com/zr-714/archive/2012/03/22/2411926.html

  • 相关阅读:
    Cannot load php5apache2_4.dll into server
    goroutine,channel
    为什么 Go 标准库中有些函数只有签名,没有函数体?
    PHP编码风格规范
    etcd压测造成数据目录过大恢复
    高可用kubernetes集群查看kube-scheduler和kube-controller-manager哪个是leader节点
    kubeadm join添加节点,新加节点夯在not ready(cni config uninitialized)
    一次.dockerignore设置错误导致的docker build排查
    通过开源插件实现sonarqube区分不同分支显示代码扫描结果
    python脚本,调用接口清理镜像多余tag
  • 原文地址:https://www.cnblogs.com/flying607/p/3419240.html
Copyright © 2011-2022 走看看