5. 常量的本质含义与反编译及助记符详解

目录

跟踪加载

例1

JVM参数选项

常量的本质含义

例2

反编译及助记符详解

助记符:

idc

bipush 

sipush

iconst_5


 

跟踪加载

例1

package com.study.classloader;

public class MyTest1 {

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

class Parent1 {

    public static String content1 = "parent";
    static {
        System.out.println("parent static block");
    }
}

class Child1 extends Parent1{

    public static String content2 = "child";
    static {
        System.out.println("child static block");
    }
}

结果:

parent static block

parent

分析:

参见第4篇文章解释

这里Child1没有被初始化,那它有没有被加载呢?

为了研究这个问题,我们添加-XX:+TraceClassLoading参数用于追踪类的加载信息

控制台输出:

[0.003s][warning][arguments] -XX:+TraceClassLoading is deprecated. Will use -Xlog:class+load=info instead.
[0.013s][info   ][class,load] opened: /Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/lib/modules
[0.026s][info   ][class,load] java.lang.Object source: shared objects file
[0.026s][info   ][class,load] java.io.Serializable source: shared objects file

...
[0.153s][info   ][class,load] com.study.classloader.MyTest1 source: file:/Users/kevin/Documents/study/github/jvm/target/classes/

...

[0.154s][info   ][class,load] java.lang.invoke.VarHandle$AccessType source: shared objects file
[0.154s][info   ][class,load] java.lang.invoke.VarHandle$AccessMode source: shared objects file
[0.154s][info   ][class,load] com.study.classloader.Parent1 source: file:/Users/kevin/Documents/study/github/jvm/target/classes/
[0.154s][info   ][class,load] com.study.classloader.Child1 source: file:/Users/kevin/Documents/study/github/jvm/target/classes/
[0.154s][info   ][class,load] java.util.concurrent.atomic.Striped64 source: shared objects file
parent static block[0.154s][info   ][class,load] java.util.concurrent.atomic.LongAdder source: shared objects file

...

分析:

第一个被加载的类Object类,因为它是所有类的父类

最先加载了MyTest1类(根据主动使用的第6条,虚拟机启动时被标明为启动类的类,MyTest1会被初始化),然后加载了Parent1类,再加载了Child1类

结论:

Child1类会被加载,但不会被初始化

JVM参数选项

Jvm参数开始固定都会有-XX:

  • -XX:+<option>,表示开启默认关闭的选项
  • -XX:-<option>,表示关闭默认开启的选项
  • -XX:<option>=<value>,表示将option选项的值设置为value

常量的本质含义

例2

package com.study.classloader;

public class MyTest2 {

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

class MyParent2{

    public static final String content = "parent";

    static {
        System.out.println("parent static block");
    }
}

结果:

parent

分析:

为什么MyParent2没有初始化,执行static代码块?

属性加上final就是常量,在编译阶段常量就会被存在调用常量的方法所在的类的常量池当中

本质上,调用类MyTest2并没有直接引用到定义常量的类MyParent2,因此并不会触发定义常量的类的初始化

注意:这里指的是将常量content存放到了MyTest2的常量池中,之后MyTest2与MyParent2就没有任何关系了

甚至,我们可以将MyParent2的class文件删除

测试1

将MyTest2.class删除,并执行运行

结果:

错误: 找不到或无法加载主类 com.study.classloader.MyTest2
原因: java.lang.ClassNotFoundException: com.study.classloader.MyTest2

测试2:

重新build项目class文件,然后只删除MyParent2.class,并运行

结果:

parent

分析:

可见编译完成后,MyParent2中的常量就已经被保存在了MyTest2的常量池中了,与MyParent2再无关系

反编译及助记符详解

助记符:

助记符本身由相关的类来操作,如ldc对应com.sun.org.apache.bcel.internal.generic.LDC

  • ldc表示将int,float或者是String类型的常量值从常量池中推送至栈顶。
  • bipush表示将单字节(-128~127)的常量值推送至栈顶
  • sipush表示将一个短整型常量值(-32768~32767)推送至栈顶
  • iconst_1表示将int类型1推送至栈顶(只有iconst_m1 和 iconst_0 ~ iconst_5,jvm觉得这几个数字最常用,特殊定义)

ICONST类doc文档介绍

com.sun.org.apache.bcel.internal.generic public class ICONST
extends Instruction
implements ConstantPushInstruction
ICONST - Push value between -1, ..., 5, other values cause an exception
Stack: ... -> ...,

ICONST代码实现 

    public ICONST(final int i) {
        super(com.sun.org.apache.bcel.internal.Const.ICONST_0, (short) 1);
        if ((i >= -1) && (i <= 5)) {
            super.setOpcode((short) (com.sun.org.apache.bcel.internal.Const.ICONST_0 + i)); // Even works for i == -1
        } else {
            throw new ClassGenException("ICONST can be used only for value between -1 and 5: " + i);
        }
        value = i;
    }

idc

➜  jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
  public com.study.classloader.MyTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String parent
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

bipush 

package com.study.classloader;

public class MyTest2 {

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

class MyParent2{

    public static final String content = "parent";

    public static final short s = 127;

    static {
        System.out.println("parent static block");
    }
}

结果:

➜  jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
  public com.study.classloader.MyTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: bipush        127
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       8: return
}

sipush

package com.study.classloader;

public class MyTest2 {

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

class MyParent2{

    public static final String content = "parent";

    public static final short s = 127;

    public static final int i = 128;

    static {
        System.out.println("parent static block");
    }
}

结果:

➜  jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
  public com.study.classloader.MyTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: sipush        128
       6: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       9: return
}

iconst_5

package com.study.classloader;

public class MyTest2 {

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

class MyParent2{

    public static final String content = "parent";

    public static final short s = 127;

    public static final int i = 128;

    public static final int m = 5;

    static {
        System.out.println("parent static block");
    }
}

结果:

➜  jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
  public com.study.classloader.MyTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: iconst_5
       4: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       7: return
}

猜你喜欢

转载自blog.csdn.net/Cheng_Kohui/article/details/93238085