java systemoutprint,Java源码:1.详解System.out.print源码分析<1>

public class HelloWorld {

public static void main(String[] args) {

System.out.print("Hello World!");

}

}

我们一般直接调用System.out.print方法来打印内容

System在java.lang包

通过源码我们看到out为PrintStream类打印流对象,是System类的静态成员变量,所以能直接使用,但是初始值为null,

打印流out是什么时候赋值的呢?我们继续看System类源码,System类中static{}代码块

调用registerNatives()方法,这是native方法,我们没有办法看到源码,

我们来看注释:

VM will invoke the initializeSystemClass method to complete the initialization for this class separated from clinit.

翻译是:VM将调用initializeSystemClass方法来完成类的初始化,

public final class System {

public final static PrintStream out = null;

/* 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();

}

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")));

......

}

public static void setOut(PrintStream out) {

checkIO();

setOut0(out);

}

}

//FileDescriptor是"文件描述符".

//FileDescriptor可以被用来表示开放的文件,开放的套接字等.

//当FileDescriptor表示文件来说,当FIleDescriptor表示某文件时,我们可以通俗的将FIleDescriptor看成该文件.但是,我们不能直接通过FIleDescriptor对该文件进行操作;若需要通过FIleDescriptor对该文件进行操作,则需要创建FileDescriptor对应的FileOutputStream,再对文件进行操作.

public final class FileDescriptor {

private int fd;

// in:标准输入(键盘)的描述符

//out:标准输出(屏幕)的描述符

//err:标准错误输出(屏幕)的描述符

public static final FileDescriptor in = standardStream(0);

public static final FileDescriptor out = standardStream(1);

public static final FileDescriptor err = standardStream(2);

private static FileDescriptor standardStream(int fd) {

FileDescriptor desc = new FileDescriptor();

desc.handle = set(fd);

return desc;

}

public FileOutputStream(FileDescriptor fdObj) {

....

this.fd = fdObj;

this.append = false;

this.path = null;

fd.attach(this);

}

}

initializeSystemClass()方法中

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);

此时fdOut是文件输出流

后面调用setOut0(PrintStream out)方法,该方法也是native方法,看不到源码,那怎么知道out对象什么时候赋值的呢?

我们继续看到System类中有静态方法setOut(PrintStream out)方法,我们知道setout方法是重置输出流的对象的,setout调用了setout0,我们可以大致猜到setout0是通过调用底层的代码实现对out的流的重定位的,而initializeSystemClass()这个函数也调用了setout0将fd为1的文件封装成文件流,再封装成缓冲流,再封装成打印流,最后通过setout0将out与这个流绑定。

最后调用了out打印流对象的println()方法进行输出。

out打印流对象println()源码解析以后分析