Take notes for this book
为了提高访问效率,每个Java对象可能都会使用方法表。
方法表是一个数组,存储所有可能会被调用的实例方法(包括从超类继承来的)的直接引用。
锁和等待/通知的数据都是在第一次使用时才会分配。
只能在回收前调用一次,但该方法被调用后,仍可继续使用该对象,但回收前不会再调用该方法。
数组也是对象。所有具有相同维度和类型的数组都是同一个类的实例,而不论数组长度是多少。
每一维用方括号'['表示,如一维int数组的类名是[I
每一个线程都有一个PC计数器,计算下一条指令。
每个Java线程都有一个Java栈,Java栈以帧为单位保存线程运行状态。
每当线程调用一个Java方法时,虚拟机都会向该线程的Java栈压入一个新帧。在执行这个方法时,使用该帧存储参数、局部变量和中间运算结果。
以2种方式返回:1).return正常返回,2).抛出异常返回。
局部变量区、操作数栈、帧数据区.
一个线程可以允许多次对同一个对象上锁,对于每一个对象来说,Java虚拟机维护一个计数器,记录对象被加了多少次锁。没有被锁的对象的计数器是0。但一个线程第一次获得锁的时候,计数器跳到1。(只有已经拥有了这个对象的锁的线程才能对该对象再次加锁。在它释放锁之前,其他线程不能对这个对象加锁。)每当线程释放锁一次,计数器就减1。当计数器跳到0的时候,锁就被完全释放了,其他线程才可以使用它。
1.实例方法使用动态(迟)绑定
2.类方法使用静态(早)绑定
例子:
public class One {
protected LinkedList list = new LinkedList();
public static void output() {
System.out.println("in One");
}
}
public class Two extends One {
public static void output() {
System.out.println("in Two");
}
public static void main(String[] args) {
One one = new Two();
one.output();
}
}
正确输出结果:in One
如果这两个方法都不是static的,那么结果将是"in Two"
public class One {
private void interestMethod() {
System.out.println("One's interestMethod()");
}
void exampleMethod() {
this.interestMethod();
}
}
public class Two extends One {
void interestMethod() {
System.out.println("Two's interestMethod()");
}
public static void main(String[] args) {
One one = new Two();
one.exampleMethod();
}
}
正确输出结果:One's interestMethod()
如果 One 的 interestMethod 修饰符改为 default, protected or public 时,结果将为:Two's interestMethod()
public class Surprise {
public static boolean surpriseProgrammer(boolean bool) {
while (bool) {
try {
return true;
} finally {
break;
}
}
return false;
}
public static void main(String[] args) {
System.out.println(Surprise.surpriseProgrammer(true));
}
}
正确输出结果:false(且不论给 bool 传入的是 true 还是 false)
正常的执行流程是先执行 finally 语句块,然后再执行 return,因在 finally 中进行了 break(return,continue,throw exception亦同样道理),跳出了这个执行流程,因此在 break 后代码将运行到 while 结束处,并继续执行下一条语句 return false。
public class Surprise {
public static int surpriseProgrammer(boolean bool) {
int i = 0;
while (bool) {
try {
return i;
} finally {
i = 3;
}
}
return i;
}
public static void main(String[] args) {
System.out.println(Surprise.surpriseProgrammer(true));
}
}
正确输出结果:0
在执行 finally 语句块之前,JVM已经把返回值(即 0 )存放到局部变量中,在执行完 finally 语句块之后,取出该局部变量值并返回。
对比上面的 break 用法,就可以发现,如果使用 break 或 return,就可以返回结果:3。
char, byte, short在JVM内部使用int进行计算
char, byte, short 在进行数学运算前,JVM会先将其转化为 int 然后再进行计算,因此
short a = 1;
short b = 2;
short c = (short) (a + b);
必须对(a+b)结果进行强制类型转换,否则其结果将是int。
JVM可以选择预先解析,也可选择迟解析(即在类首次被用到时才把符号引用解析为直接引用),但不管何时进行解析,都要在程序执行过程中第一次实际试图访问一个符号引用的时候才抛出错误。用这种方式,对用户来说看上去都是迟解析。
如果JVM使用预先解析方式,在预先解析过程中发现某个class文件无法找到,它不能抛出错误,而是直到后来程序实际访问这个class文件中的某些东西时才抛出错误。如果程序不使用这个类,错误永远不会被抛出。
Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.
垃圾收集器在把软引用对象和弱引用对象加入队列的时候,是在它们的引用目标离开相应的可触及状态(从强到软或弱)时;而把影子引用对象加入队列是在引用目标进入相应状态(进入影子可触及)时。
在垃圾收集器清除引用对象时也有差别,软引用对象和弱引用对象在加入队列之前得到了清除,而影子引用对象却不会。这就是说,垃圾收集器把软引用加入队列标志着它的引用对象刚刚离开软可触及状态;同样,垃圾收集器把弱引用加入队列标志着它的引用对象刚刚离开弱可触及状态;但是垃圾收集器把影子引用对象加入队列标志着引用目标已经进入了影子可触及状态。影子可触及对象会保持影子可触及状态,直到程序显式地清除了引用对象。
影子引用需要显式调用clear()方法?
1. 类装载器结构
2. class 文件检验器
3. 内置于 Java 虚拟机(及语言)的安全特性
4. 安全管理器及 Java API
1. 第一趟扫描是在类装载时扫描的 - class文件的结构检查
2. 第二趟和第三趟扫描是在连接过程中进行的
第二趟扫描 - 类型的语义检查
第三趟扫描 - 字节码验证
3. 第四糖扫描是在将符号引用转化为直接引用时进行的 - 符号引用的验证
装载,连接(验证-校验类文件数据格式,准备-分配内存,解析-把常量池中的符号引用转换为直接引用),初始化。
如果一个类装载器在预先装载时遇到缺失或者错误的class文件,它必须等到程序首次主动使用该类时才报告错误。
类初始化与接口初始化的不同任何一个类的初始化都要求它的所有祖先类(而不是祖先接口)预先被初始化;而一个接口的初始化,并不要求它的祖先接口预先被初始化。
所有的类变量初始化语句和类型的静态初始化器都被Java编译器收集在一起,放到类初始化方法中
针对源代码中每一个类的构造方法,Java编译器都产生一个()方法