System.out.println("Hello World")是大部分程序员入门的第一行代码,也可以说是程序员们最熟悉的一行代码。大家真的深入研究过System.out.println()么?今天就来盘一盘System.out.println()!
System是个啥?
System作为Java.lang包中一个final类,早在JDK1.0中就存在其中,可谓基石也。
再来看看out
out是System中的一个静态的数据成员,但这个成员不是基本类,而是java.io.PrintStream类的对象。out被关键字static修饰,我们可以直接通过System.out来引用,而无须先建立对象。
public final static PrintStream out = null;
out在系统类初始化时被实例化,配置输出在控制台上。
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); // 获取 FileDescriptor中的静态成员 out并创建对应的文件输出流。out是一个 FileDescriptor对象,它是"标准输出(屏幕)"的标识符。 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(); } //native方法 输出 private static native void setOut0(PrintStream out);
out对象是可以自定义的。开发人员可以在执行期间对out对象进行修改。
public static void setOut(PrintStream out) { checkIO(); setOut0(out); }
例:修改out输出路径,将结果以文档形式输出
public class ModifyOut { public static void main(String args[]) { System.setOut(new PrintStream(new FileOutputStream("ModifyOut.txt"))); System.out.println("out输出已经重定向!"); } }
最后是printl()
println()中的传入参数包含了各种各样的参数类型,枚举了一下。
java.io.PrintStream#println() java.io.PrintStream#println(boolean) java.io.PrintStream#println(char) java.io.PrintStream#println(char[]) java.io.PrintStream#println(double) java.io.PrintStream#println(float) java.io.PrintStream#println(int) java.io.PrintStream#println(long) java.io.PrintStream#println(java.lang.Object) java.io.PrintStream#println(java.lang.String)
各种参数的println()方法内容大同小异,这里以println(Object x)为例,盘一下println()中都干了些啥。
public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } }
使用println()方法中的大部分参数,在执行write(s)方法前,通通被转换成String类型。仅char[] 并没有转换成String类型。原因嘛。嘿嘿,String类型就是char[]实现的啊。
public void print(String s) { if (s == null) { s = "null"; } // 传入的参数在执行write(s)方法前,通通都被转成String类型。 write(s); }
操劳多种类型进行String类型转换,就是为了这步write(String s)方法集中统一处理
private void write(String s) { try { synchronized (this) { //检查确认当前流尚未关闭 ensureOpen(); //输出传入的字符串 textOut.write(s); // 刷新此输出流并强制写出所有缓冲的输出字节。 textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush && (s.indexOf(' ') >= 0)) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
/** * writeBuffer的大小 默认为1024 */ private static final int WRITE_BUFFER_SIZE = 1024; public void write(String str) throws IOException { write(str, 0, str.length()); } public void write(String str, int off, int len) throws IOException { synchronized (lock) { char cbuf[]; if (len <= WRITE_BUFFER_SIZE) { if (writeBuffer == null) { writeBuffer = new char[WRITE_BUFFER_SIZE]; } cbuf = writeBuffer; } else { // Don't permanently allocate very large buffers. cbuf = new char[len]; } str.getChars(off, (off + len), cbuf, 0); // 输出字符数组(说了这么多,最后处理的还是字符组) write(cbuf, 0, len); } } // 将字符数组buffer写入到输出流中,offset是从buffer中读取数据的起始偏移位置,len是读取的长度。 abstract public void write(char cbuf[], int off, int len) throws IOException;