提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、概述
1.1 字节码文件的跨平台性
透过字节码指令看代码细节
1.2 Java的前端编译器
1.3 透过字节码指令看代码细节
案例1:
public class IntegerTest {
public static void main(String[] args) {
Integer x = 5;
int y = 5;
System.out.println(x == y);//true
Integer i1 = 10;
Integer i2 = 10;
System.out.println(i1 == i2);//true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);//false
}
}
main()的字节码:
0 iconst_5 //往操作数栈中放一个5
1 invokestatic #2 <java/lang/Integer.valueOf> //执行Integer的valueOf方法
4 astore_1
5 iconst_5
6 istore_2
7 getstatic #3 <java/lang/System.out>
10 aload_1
11 invokevirtual #4 <java/lang/Integer.intValue>
14 iload_2
15 if_icmpne 22 (+7)
18 iconst_1
19 goto 23 (+4)
22 iconst_0
23 invokevirtual #5 <java/io/PrintStream.println>
26 bipush 10
28 invokestatic #2 <java/lang/Integer.valueOf>
31 astore_3
32 bipush 10
34 invokestatic #2 <java/lang/Integer.valueOf>
37 astore 4
39 getstatic #3 <java/lang/System.out>
42 aload_3
43 aload 4
45 if_acmpne 52 (+7)
48 iconst_1
49 goto 53 (+4)
52 iconst_0
53 invokevirtual #5 <java/io/PrintStream.println>
56 sipush 128
59 invokestatic #2 <java/lang/Integer.valueOf>
62 astore 5
64 sipush 128
67 invokestatic #2 <java/lang/Integer.valueOf>
70 astore 6
72 getstatic #3 <java/lang/System.out>
75 aload 5
77 aload 6
79 if_acmpne 86 (+7)
82 iconst_1
83 goto 87 (+4)
86 iconst_0
87 invokevirtual #5 <java/io/PrintStream.println>
90 return
分析字节码:
2 invokestatic #2 <java/lang/Integer.valueOf> //执行Integer的valueOf方法
查看integer的valueOf源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
从valueOf()方法可以看出,只要是在(i >= IntegerCache.low && i <= IntegerCache.high)这个范围内,就取 IntegerCache.cache[i + (-IntegerCache.low)]值 ,否则就新创建一个new Integer(i)
在进入IntegerCache.cache[i + (-IntegerCache.low)]这个方法,继续获取信息
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
原来IntegerCache 是 Integer的一个静态的内部类
其中:
关键代码:
static final int low = -128;
static final int high;
staitic{
int h = 127;
high = h;
cache = new Integer[(high - low) + 1];
}
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
这个内部类创建了一个数组catch,大小是【(high - low) + 1 】:【127-(-128)+1】 =266,并且给数组赋上了值,从low到high ,也就是范围是[-128,127]。
我们先从微观的角度分析了valueOf()和内部类IntegerCache 做了什么,那么在宏观的角度思考一下,其实也明白了,Integer类加载的时候,就创建一个大小是266的数组,范围是【-128,+127】。第一行字节码iconst_5 ,往操作数栈放了一个5,然后以5 为参数调用Integer的valueOf()方法时,5在【-128,+127】内所以就直接使用数组catch中的5
所以这就解释清除了为什么 System.out.println(i1 == i2)输出是true,而System.out.println(i3 == i4)是false了。
Integer x = 5;
int y = 5;
System.out.println(x == y);//true,其实就是Integer的拆箱。
案例2:
public class StringTest {
public static void main(String[] args) {
String str = new String("hello") + new String("world");
String str1 = "helloworld";
System.out.println(str == str1); //fasle
String str2 = new String("helloworld");
System.out.println(str == str2); //false
}
}
0 new #2 <java/lang/StringBuilder>
3 dup
4 invokespecial #3 <java/lang/StringBuilder.<init>>
7 new #4 <java/lang/String>
10 dup
11 ldc #5 <hello>
13 invokespecial #6 <java/lang/String.<init>>
16 invokevirtual #7 <java/lang/StringBuilder.append>
19 new #4 <java/lang/String>
22 dup
23 ldc #8 <world>
25 invokespecial #6 <java/lang/String.<init>>
28 invokevirtual #7 <java/lang/StringBuilder.append>
31 invokevirtual #9 <java/lang/StringBuilder.toString>
34 astore_1
35 ldc #10 <helloworld>
37 astore_2
38 getstatic #11 <java/lang/System.out>
41 aload_1
42 aload_2
43 if_acmpne 50 (+7)
46 iconst_1
47 goto 51 (+4)
50 iconst_0
51 invokevirtual #12 <java/io/PrintStream.println>
54 new #4 <java/lang/String>
57 dup
58 ldc #10 <helloworld>
60 invokespecial #6 <java/lang/String.<init>>
63 astore_3
64 getstatic #11 <java/lang/System.out>
67 aload_1
68 aload_3
69 if_acmpne 76 (+7)
72 iconst_1
73 goto 77 (+4)
76 iconst_0
77 invokevirtual #12 <java/io/PrintStream.println>
80 return
案例3:
package com.atguigu.java;
/*
成员变量(非静态的)的赋值过程: ① 默认初始化 - ② 显式初始化 /代码块中初始化 - ③ 构造器中初始化 - ④ 有了对象之后,可以“对象.属性”或"对象.方法"
的方式对成员变量进行赋值。
*/
class Father {
int x = 10;
public Father() {
this.print();
x = 20;
}
public void print() {
System.out.println("Father.x = " + x);
}
}
class Son extends Father {
int x = 30;
// float x = 30.1F;
public Son() {
this.print();
x = 40;
}
public void print() {
System.out.println("Son.x = " + x);
}
}
public class SonTest {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.x);
}
}
结果
Son.x = 0
Son.x = 30
20
先把SonTest改成这样
public class SonTest {
public static void main(String[] args) {
Father f = new Father();
System.out.println(f.x);
}
}
class Father {
int x = 10;
public Father() {
this.print();
x = 20;
}
public void print() {
System.out.println("Father.x = " + x);
}
}
此时main方法的字节码为:
0 aload_0
1 invokespecial #1 <java/lang/Object.<init>>
4 aload_0
5 bipush 10
7 putfield #2 <com/atguigu/java/Father.x>
10 aload_0
11 invokevirtual #3 <com/atguigu/java/Father.print>
14 aload_0
15 bipush 20
17 putfield #2 <com/atguigu/java/Father.x>
20 return
由字节码可以看出在执行print()方法之前,先把10赋值给了Father.x。然后再执行的print(),后面在赋值20给Father.x
因此打印结果为:
Father.x = 10
20
再把代码修改过来:
public class SonTest {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.x);
}
}
0 aload_0
1 invokespecial #1 <com/atguigu/java/Father.<init>>
4 aload_0
5 bipush 30
7 putfield #2 <com/atguigu/java/Son.x>
10 aload_0
11 invokevirtual #3 <com/atguigu/java/Son.print>
14 aload_0
15 bipush 40
17 putfield #2 <com/atguigu/java/Son.x>
20 return
由字节码可以看出 Father f = new Son();先调用父类的构造方法:
public Father() {
this.print();
x = 20;
}
但是此时这个this是son,所以 this.print();就是son.print(),此时Son.x还未赋值(bipush 30
在后面才执行),所以会打印出
Son.x = 0
Son.x = 30
20
二、虚拟机的基石
javaP:
把txt文件放到桌面上后,用记事本打开
三 、class文件结构
public class Demo {
private int num = 1;
public int add(){
num = num + 2;
return num;
}
}
找到编译后的文件,用javap生成一个txt文件,复制一份这个txt文件,放桌面上用记事本打开
为了方便,我们把内容放到excel中:
01-魔数:Class文件的标志
02-Class文件版本号
03-常量池:存放所有常量
1、常量计数器
2、常量池表
字面量和符号引用
常量类型和结构
04-访问标识
05-类索引、父类索引、接口索引集合
06-字段表集合
1-字段计数器
2-字段表
根据上面的例子,我们来实际分析一下,如下图:
07-方法表集合
1-方法计数器
2-方法表
08-属性表集合
1-属性计数器
2-属性表
或者(查看官网)
小结
四 、使用javap指令解析Class文件
1-解析字节码的作用
2-javac -g操作
3-javap的用法
注意:①-v相当于-c -l ②-v也不会输出私有的字段、方法等信息,所以如果想输出私有的信息,那需要在-v后面加上-p才行
4-使用举例
1、代码:
public class JavapTest {
private int num;
boolean flag;
protected char gender;
public String info;
public static final int COUNTS = 1;
static {
String url = "www.atguigu.com";
}
{
info = "java";
}
public JavapTest() {
}
private JavapTest(boolean falg) {
this.flag = flag;
}
private void methodPrivate() {
}
int getNum(int i) {
return num + i;
}
protected char showGender() {
return gender;
}
public void showInfo() {
int i = 100;
System.out.println(info + i);
}
}
2、字节码文件分析
private io.renren.JavapTest(boolean); // 单个参数构造器方法信息
descriptor: (Z)V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String java
7: putfield #3 // Field info:Ljava/lang/String;
10: aload_0
11: aload_0
12: getfield #4 // Field flag:Z
15: putfield #4 // Field flag:Z
18: return
LineNumberTable:
line 19: 0
line 14: 4
line 20: 10
line 21: 18
LocalVariableTable:
Start Length Slot Name Signature
0 19 0 this Lio/renren/JavapTest;
0 19 1 falg Z
MethodParameters:
Name Flags
falg
private void methodPrivate();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 24: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lio/renren/JavapTest;
int getNum(int);
descriptor: (I)I
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: getfield #5 // Field num:I
4: iload_1
5: iadd
6: ireturn
LineNumberTable:
line 26: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lio/renren/JavapTest;
0 7 1 i I
MethodParameters:
Name Flags
i
protected char showGender();
descriptor: ()C
flags: ACC_PROTECTED
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #6 // Field gender:C
4: ireturn
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lio/renren/JavapTest;
public void showInfo();
descriptor: ()V // 方法的描述符:方法的形参列表、返回值类型
flags: ACC_PUBLIC // 方法的访问标识
Code: // 方法的Code属性
stack=3, locals=2, args_size=1 // stack:操作数栈的最大深度 locals:局部变量表的长度 args_size:方法接受参数的个数
// 偏移量 操作码 操作数
0: bipush 100
2: istore_1
3: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
6: new #8 // class java/lang/StringBuilder
9: dup
10: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
13: aload_0
14: getfield #3 // Field info:Ljava/lang/String;
17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: iload_1
21: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
24: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
// 行号表:指明字节码指令的偏移量与java源代码中代码的行号的一一对应关系
LineNumberTable:
line 32: 0
line 33: 3
line 34: 30
// 局部变量表:描述内部局部变量的相关信息
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 this Lio/renren/JavapTest;
3 28 1 i I
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=1, args_size=0
0: ldc #14 // String www.atguigu.com
2: astore_0
3: return
LineNumberTable:
line 11: 0
line 12: 3
LocalVariableTable:
Start Length Slot Name Signature
}
SourceFile: "JavapTest.java" // 附加属性:指明当前字节码文件对应的源程序文件名
3、jclasslib展示的内容: