在Java中输出调试信息:
在Java中输出调试信息:
而在System这个类中out和err都是定义为静态的:
而被static修饰的变量都是属于类本身的,就像这样:
可以直接被类名所调用,而不需要用对象来调用,就像这样:
要注意无论是 out 或 err 都属于 (类)PrintStream 的变量,这里就有个问题搞不懂了,比如以下这个例子:
输出结果是:空指针异常
再回头看看 System 中的out和err赋值:
API中out是被final修饰符所修饰的,而且是赋值给null啊!这不是空对象吗?这样子的话弹出空指针异常就不奇怪了,
回头看System源码,我发现其中有一个setOut()方法,如下:
out不是被final修饰符所修饰吗?怎么可以更改呢?像这个样子:
报错:,被final修饰的a不能被赋值。
那同样被final修饰的out是怎么被修改的呢?查看源码:
setOut0(out) 如下:
我发现,它们都被 native 所修饰,据我所知,Java是用C和C++实现的底层,用native修饰也就是说这个是用C和C++编写的最底层了。所以在运行 out 的时候 setOut0(PrintStream out) 方法肯定被调用过,在System源码中查找,发现在一个initializeSystemClass() 方法中setOut0 被调用了,如下:
注意备注内容:Initialize the system class. Called after thread initialization.(初始化系统类。线程初始化后调用。)
到这里已经很明白了,System 初始化之前会调用 initializeSystemClass() 方法,然后方法 initializeSystemClass() 给 out 赋值一个 新的PrintStream 对象;
所以说白了就类似与一下 out 和 err 的输出也就类似于以下内容:
public class StaticTest {
static A a;
static {a = new A();
}
public static void main(String[] args) {StaticTest.a.println();
}
}
class A {
public void println() {
System.out.println("Hello World");
}
}
其中 static 块中的代码会在类初始化之前就会被调用,这里用 out 输出,像这样:
输出结果:
还有一个问题就是,当 out 和 err 一起用的时候,它们并不会按着顺序来输出,比如以下:
输出结果:
再来一次,输出结果:
由于这里 println(String x) 方法是属于类 printStream 源码,以下:
这里用到了一个关键词 synchronized , 表示这是一个同步代码块,当这个同步代码块执行时,cpu是被独占的,而这里的监听器是 this ,也就是说是类本身;所以当类被调用时,先是给 out 和 err 赋值一个新的printStream对象,它们两个的对象是不同的;
所以,当我调用 mian 方法时,它们的监听器已经都生成好了,它们会一起抢占线程池,它们的先后顺序是不固定的,看谁先抢到cpu了。
但是,无论执行多少次程序,同一个监听器下的顺序是固定的,比如 out 的顺序,或者 err 的顺序,所以当我们单独使用它们其中一个的时候,就不用担心它们打架了。