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()源码解析以后分析