中:第1章 class文件结构

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、概述

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文件结构

在这里插入图片描述
xasgjca
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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展示的内容:
在这里插入图片描述

5-总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值