四、深入JVM-- Class文件详解

Class文件详解

通过javap反编译和直接反编译成java文件的区别?
==》通过javap反编译只是得到汇编的指令而已,而利用反编译文件编译成的java源文件,是源代码。
==》javap是 JDK自带的一个工具,可以将 class文件反编译成字节码,它并没有将class文件反编译成 java文件,但是依然反编译成程序员能读的格式。

Class文件设计理念

只要符合类文件的规范都可以被JVM虚拟机执行。java、groovy、jruby、scala都可以被自己的编译期编译成字节码文件,由JVM运行。
在这里插入图片描述

Class文件结构
Class是以一组以8位字节为基础单位的二进制流。
节省空间,数据之间不掺杂任何分隔符紧凑排列。
效率高,二进制存储,不需要文件编解码过程。

Class文件只有两种基本数据类型:无符号数和表

在这里插入图片描述

在这里插入图片描述

字节码指令集

字节码文件是一组以8位为基础单位的二进制流。每1个字节代表一个操作指令。所以操作指令最多不会超多256个。JVM总共有200多个操作指令,但是指令后面可以跟着操作数。

加载和存储指令
加载和存储指令作用:将数据在局部变量表和操作数栈中进行来回传输。
加载指令(将数据压到操作数栈中):
将局部变量表数据加载到操作数栈:iload 、lload、fload 、dload、aload
将常量加载到操作数栈:ldc 、bipush

存储指令(操作数从操作数栈出栈,保存到局部变量表):istore

类型转换指令
l2b 、i2c、i2s、l2i

类加载机制

在这里插入图片描述

在这里插入图片描述

成员变量
类编译时,会把成员变量的声明提到类的最前端。
成员变量的初始化与成员代码块的初始化都是在构造方法中执行的。构造方法中的代码最后执行。
成员变量的初始化与成员代码块在构造方法中会按照显示顺序执行。

成员静态变量
成员静态变量的初始化会在静态构造方法中执行。

常量池
https://blog.csdn.net/u013096088/article/details/83047282

return 和finally谁先执行?
reutrn先执行,finally最后执行。 return变量时,会把变量存储到方法返回参数中,return返回的是方法返回参数变量而不是直接return的那个变量,具体例子可见demo2。

Demo1:
java代码:

public static int f1() {
		int i=0;
		try {
			return i;
		}finally {
			i=4;
		}
		
	}

反编译方法:

 public static int f1();
   descriptor: ()I
   flags: ACC_PUBLIC, ACC_STATIC
   Code:
     stack=1, locals=3, args_size=0
        0: iconst_0
        1: istore_0
        2: iload_0
        3: istore_2
        4: iconst_4
        5: istore_0
        6: iload_2
        7: ireturn
        8: astore_1
        9: iconst_4
       10: istore_0
       11: aload_1
       12: athrow
     Exception table:
        from    to  target type
            2     4     8   any
     LineNumberTable:
       line 11: 0
       line 13: 2
       line 15: 4
       line 13: 6
       line 14: 8
       line 15: 9
       line 16: 11
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           2      11     0     i   I```


使用String类与StringBuilder类字符串拼接,哪一种性能更好?
String每次在拼接字符串时,都会新建一个StringBuilder对象。然后调用append方法拼接。而StringBuilder是只新建一次对象,然后调用append方法。所以StirngBuilder比String的效率要高的多。

原代码:
```java
public class App {
 
	
	public static void f1() {
		String str="1";
		str+="12";
		str+="14";
	}
	
	public static void f2() {
		StringBuilder sb=new StringBuilder();
		sb.append("12");
		sb.append("14");
	}
}

反编译字节码文件命令:
javap -verbose App.class 。

看f1方法,会发现String每次在拼接字符串时,都会新建StringBuilder对象,然后才进行拼接。
看f2方法,会发现StringBuilder只会创建一次对象,然后进行拼接。

E:\hlp\eclipse\work\my-spring-framework\target\classes\com\gupao\spring\bean>
javap -verbose App.class
Classfile /E:/hlp/eclipse/work/my-spring-framework/target/classes/com/gupao/spri
ng/bean/App.class
  Last modified 2019-9-25; size 836 bytes
  MD5 checksum 1829d2082cd26b02ce71f8dd19cc2d55
  Compiled from "App.java"
public class com.gupao.spring.bean.App
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/gupao/spring/bean/App
   #2 = Utf8               com/gupao/spring/bean/App
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/gupao/spring/bean/App;
  #14 = Utf8               f1
  #15 = String             #16            // 1
  #16 = Utf8               1
  #17 = Class              #18            // java/lang/StringBuilder
  #18 = Utf8               java/lang/StringBuilder
  #19 = Methodref          #20.#22        // java/lang/String.valueOf:(Ljava/lan
g/Object;)Ljava/lang/String;
  #20 = Class              #21            // java/lang/String
  #21 = Utf8               java/lang/String
  #22 = NameAndType        #23:#24        // valueOf:(Ljava/lang/Object;)Ljava/l
ang/String;
  #23 = Utf8               valueOf
  #24 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
  #25 = Methodref          #17.#26        // java/lang/StringBuilder."<init>":(L
java/lang/String;)V
  #26 = NameAndType        #5:#27         // "<init>":(Ljava/lang/String;)V
  #27 = Utf8               (Ljava/lang/String;)V
  #28 = String             #29            // 12
  #29 = Utf8               12
  #30 = Methodref          #17.#31        // java/lang/StringBuilder.append:(Lja
va/lang/String;)Ljava/lang/StringBuilder;
  #31 = NameAndType        #32:#33        // append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
  #32 = Utf8               append
  #33 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #34 = Methodref          #17.#35        // java/lang/StringBuilder.toString:()
Ljava/lang/String;
  #35 = NameAndType        #36:#37        // toString:()Ljava/lang/String;
  #36 = Utf8               toString
  #37 = Utf8               ()Ljava/lang/String;
  #38 = String             #39            // 14
  #39 = Utf8               14
  #40 = Utf8               str
  #41 = Utf8               Ljava/lang/String;
  #42 = Utf8               f2
  #43 = Methodref          #17.#9         // java/lang/StringBuilder."<init>":()
V
  #44 = Utf8               sb
  #45 = Utf8               Ljava/lang/StringBuilder;
  #46 = Utf8               SourceFile
  #47 = Utf8               App.java
{
  public com.gupao.spring.bean.App();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>
":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/gupao/spring/bean/App;

  public static void f1();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=0
         0: ldc           #15                 // String 1
         2: astore_0
         3: new           #17                 // class java/lang/StringBuilder
         6: dup
         7: aload_0
         8: invokestatic  #19                 // Method java/lang/String.valueOf
:(Ljava/lang/Object;)Ljava/lang/String;
        11: invokespecial #25                 // Method java/lang/StringBuilder.
"<init>":(Ljava/lang/String;)V
        14: ldc           #28                 // String 12
        16: invokevirtual #30                 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #34                 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
        22: astore_0
        23: new           #17                 // class java/lang/StringBuilder
        26: dup
        27: aload_0
        28: invokestatic  #19                 // Method java/lang/String.valueOf
:(Ljava/lang/Object;)Ljava/lang/String;
        31: invokespecial #25                 // Method java/lang/StringBuilder.
"<init>":(Ljava/lang/String;)V
        34: ldc           #38                 // String 14
        36: invokevirtual #30                 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        39: invokevirtual #34                 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
        42: astore_0
        43: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 23
        line 10: 43
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            3      41     0   str   Ljava/lang/String;

  public static void f2();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=0
         0: new           #17                 // class java/lang/StringBuilder
         3: dup
         4: invokespecial #43                 // Method java/lang/StringBuilder.
"<init>":()V
         7: astore_0
         8: aload_0
         9: ldc           #28                 // String 12
        11: invokevirtual #30                 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        14: pop
        15: aload_0
        16: ldc           #38                 // String 14
        18: invokevirtual #30                 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: pop
        22: return
      LineNumberTable:
        line 13: 0
        line 14: 8
        line 15: 15
        line 16: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            8      15     0    sb   Ljava/lang/StringBuilder;
}
SourceFile: "App.java"

Demo2:
return返回的变量会直接赋给方法返回参数变量。所以实际上return返回的是方法返回参数变量。 reutrn user,返回一个对象引用地址。该地址会被赋给方法返回参数。然后在finaly块中改变对象的内容。由于方法返回参数也是指向该对象。所以可以看见,对象的内容发生变化。
代码:

public class App2 {

	
	public static void main(String[] args) {
		System.out.println(doI());
	}

	private static User doI() {
		User u=new User();
		try {
			u.setAge(12);
			u.setName("sdf");
			return u;
		}finally {
			u.setAge(11111111);
		}
	}
}

控制台打印:

User [name=sdf, age=11111111]

++i 和i++由什么区别?
这两种方式没有任何区别,性能完全一样。原因:根据字节码文件,可以得知,反编译出的代码完全一样。只不多++i是运算在使用,而i++,是先使用在运算。

public class App {
 
	/**
	 *  public static void f1();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
	    stack=1, locals=1, args_size=0
	       0: iconst_0
	       1: istore_0
	       2: iinc          0, 1
	       5: return
	    LineNumberTable:
	      line 7: 0
	      line 8: 2
	      line 9: 5
	    LocalVariableTable:
	      Start  Length  Slot  Name   Signature
	          2       4     0     i   I
	 */
	public static void f1() {
		int i=0;
		i++;
	}
	
	/**
	 * public static void f1();
	   descriptor: ()V
	   flags: ACC_PUBLIC, ACC_STATIC
	   Code:
	   stack=1, locals=1, args_size=0
	       0: iconst_0
	       1: istore_0
	       2: iinc          0, 1
	       5: return
	    LineNumberTable:
	      line 7: 0
	      line 8: 2
	      line 9: 5
	    LocalVariableTable:
	      Start  Length  Slot  Name   Signature
	          2       4     0     i   I
	 */
	public static void f2() {
		int i=0;
		++i;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值