1. Int[] a={}和int[] a=newint[]{}有何区别?
定义数组时经常会产生一些以为,比如说上面两种数组定义格式是否在就JVM中的实现不同,是否前者没有new所以不会在堆中分配内存?如果不了解编译器私自做了什么,很容易被这个问题困扰住,那我们从编译后的字节码中看看这两种定义形式的实现吧。其实这两种定义的字节码是一样的。比如int[] list = {888,777,999};也就是int[] list = newint[]{888,777,999};
字节码:
15:aload_0
16:iconst_3
17: newarray int
19: dup
20:iconst_0
21:sipush 888
24:iastore
25: dup
26:iconst_1
27:sipush 777
30:iastore
31: dup
32:iconst_2
33: sipush 999
--注意红色字段,都使用了newarray这个命令,说明两种形式的定义都是在内存中分配内存的。
2. 可变参数究竟是什么?
源码:
publicvoid fun(String ...strs ){
System.out.println(strs);
}
字节码:
public void fun(java.lang.String...);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_VARARGS
Code:
stack=2, locals=2, args_size=2
0: getstatic #45 // Fieldjava/lang/System.out:Ljav
a/io/PrintStream;
3: aload_1
4: invokevirtual #51 // Method java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
7: return
LineNumberTable:
line 31: 0
line 32: 7
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this LChildTest;
0 8 1 strs [Ljava/lang/String;
--注意红色部分,这是对局部变量(包括参数)的说明,通过类型签名[Ljava/lang/String可看到其中参数strs明显就是个数组。
3. 跳转类关键字的字节码实现机制
If,continue,break,for,while,goto
--这些跳转关键字本质上在字节码中都是goto指令,形式:goto xx (xx为字节码命令的偏移量)
有个例外switch命令不是用goto指令,而是用了tableswitch(通过索引访问跳转表,并跳转)
1: tableswitch { // 1 to 2
1: 24
2: 30
default: 30
}
4.从字节码角度看向上转型和向下转型
向上转型源码:
publicint fun(String str){
Object o = (Object)str;
o.toString();
return 0;
}
向上转型字节码:
Code:
stack=1, locals=3, args_size=2
0: aload_1
1: astore_2
2: aload_2
3: invokevirtual #39 // Methodjava/lang/Object.toStrin
g:()Ljava/lang/String;
--从字节码内容可以看出,由于向上转型是“安全”的,所以字节码并没有作任何转化的命令操作
向下转型源码:
publicint fun(Object str){
String o = (String)str;
o.toString();
return 0;
}
向下转型字节码:
Code:
stack=1, locals=3, args_size=2
0: aload_1
1: checkcast #39 // class java/lang/String
4: astore_2
5: aload_2
6: invokevirtual #41 // Methodjava/lang/String.toStrin
g:()Ljava/lang/String;
--注意红色部分,由于向下转型可能是不安全的,编译器增加了一个类型检查指令,避免不正确的转换。这样,运行时发现不安全的向下转换,则抛出java.lang.Long cannot be cast to java.lang.String异常。
5.从字节码角度看finalize方法和C++析构函数的区别
很多从C++转java的人会一开始把java中的finalize方法等同于C++的析构函数,但是其实两者是很不同的,finalize方法某种程度而言就是普通的成员方法,只不过这个方法被编译器固定为无参无返回的方法,至于其它的编译器不再像构造方法那样私自添加任何指令。还有一点特殊的是JVM回收该类型对象中会先调用finalize方法。除了这两点外,finalize就是一个普通方法,而非跟构造方法地位平等的析构方法。比如C++会在程序员没定义但是代码需要时让编译器自动添加析构函数,并且会先调用父类的析构函数。而java中的finalize是完全没有这个特权的,编译器不会为任何类自动生成finalize方法,并且也不会像构造方法那样在子类的finalize方法中自动调用父类的finalize方法,这些从字节码中可以显而易见。
源码:
public class test {
protectedvoid finalize(){
System.out.println("finalize");
}
}
public class ChildTest extends test {
protectedvoid finalize(){
System.out.println("ChildTestfinalize");
}
}
ChildTest字节码:
protected void finalize();
descriptor: ()V
flags: ACC_PROTECTED
Code:
stack=2, locals=1, args_size=1
0: getstatic #41 // Fieldjava/lang/System.out:Ljav
a/io/PrintStream;
3: ldc #62 // String ChildTest finalize
5: invokevirtual #64 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
8: return
--使得,可以看到,子类的finalize方法并没有任何调用父类的finalize指令。所以说finalize并不是跟构造方法同等地位的析构方法。