本文章为学习极客时间-郑雨迪《深入拆解java虚拟机》的学习笔记。
1.boolean类型的变量编译后实际是int类型.
在java中,boolean 类型编译后字节码中实际存的是int类型。0表示flase,1表示true
有如下代码:
boolean flag = true;
If(flag) System.out.print(“hello java”);
反编译编译后的class文件可以看到:
Int I = 1;
If(I != 0){
System.out.print(“hello java”);
}
那么如果我这样写:
boolean flag = true;
If(flag == true) System.out.print(“hello java”);
反编译后的class代码是什么呢?
Int I = 1;
If(I == 1){
System.out.print(“hello java”);
}
2.类的初始化仅会被执行一次,因此这个特性被用来实现单例的延迟初始化。
例:public class Singleton {
private Singleton() {}
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
那么类何时会被初始化呢?JVM规范枚举了下述多种触发情况:
1.当虚拟机启动时,初始化用户指定的主类
2.当遇到用以新建目标类实例的new指令时,初始化new指令的目标类
3.当遇到调用静态方法的指令时,初始化该静态方法所在的类
4.当遇到调用静态字段的指令时,初始化该静态字段所在的类
5.了类的初始货会触发父类的初始化
6.如果一个接口定义了default方法,那么直接实现或间接实现该接口的类的初始化,会触发该接口的初始化
7.使用反射API对某个类进行反射调用时,初始化这个类
8.当初次调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类。
看下边代码:
$ echo '
public class Singleton {
private Singleton() {}
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
static {
System.out.println("LazyHolder.<clinit>");
}
}
public static Object getInstance(boolean flag) {
if (flag) return new LazyHolder[2];
return LazyHolder.INSTANCE;
}
public static void main(String[] args) {
getInstance(true);
System.out.println("----");
getInstance(false);
}
}' > Singleton.java
$ javac Singleton.java
$ java -verbose:class Singleton
问题 1:新建数组(第 11 行)会导致 LazyHolder 的加载吗?会导致它的初始化吗?
答:会加载但不会初始化
3.java 字节码中与调用相关的指令共有五种
1.invokestatic:用于调用静态方法
2.invokespecial:用于调用私有方法、构造器,以及使用super关键字调用父类的实例方法或构造器,和所实现接口的默认方法。
3.invokevirtual:用于调用非私有实例方法
4.invokeinterface:用于调用接口方法
5.invokedynamic:用于调用动态方法
重载的方法在编译过程中即可完成识别。具体到每一个方法调用,Java 编译器会根据所传入参数的声明类型(注意与实际类型区分)来选取重载方法。选取的过程共分为三个阶段:
在不考虑对基本类型自动装拆箱(auto-boxing,auto-unboxing),以及可变长参数的情况下选取重载方法;
如果在第 1 个阶段中没有找到适配的方法,那么在允许自动装拆箱,但不允许可变长参数的情况下选取重载方法;
如果在第 2 个阶段中没有找到适配的方法,那么在允许自动装拆箱以及可变长参数的情况下选取重载方法。
如果 Java 编译器在同一个阶段中找到了多个适配的方法,那么它会在其中选择一个最为贴切的,而决定贴切程度的一个关键就是形式参数类型的继承关系。
如果子类定义了与父类中非私有方法同名的方法,且参数相同:
如果这两个方法都是静态的,那么子类中的方法隐藏了父类中的方法
如果这两个方法都不是静态的,那么子类的方法重写了父类的方法。
java虚拟机识别方法的关键在于类名、方法名以及方法描述符
Java虚拟机和java语言不同,java语言不允许相同名字和参数的方法同时出现,但java虚拟机允许。对于调用这些方法的字节码来说,由于字节码所附带的方法描述符包含了返回类型,因此java虚拟机能够准备的识别目标方法。
4.java虚拟机是如何处理异常的
在编译生成的字节码中,每个方法都附带一个异常表,由from、to、target、type构成。from和to表示try代码块的开始和结束位置,target表示异常处理开始位置,type则指的是异常的类型。
后续本文会持续更新~
参考文献:
1.极客时间 郑雨迪 深入拆解java虚拟机。