常量的本质含义与反编译及助记符详解
引入第五课中的例1,如下面所示:
例1:
public class MyTest1 {
public static void main(String[] args) {
System.out.println(MyChild1.str);
}
}
class MyParent1 {
public static String str = "hello world";
static {
System.out.println("MyParent static block");
}
}
class MyChild1 extendsMyParent1 {
static {
System.out.println("MyChild static block");
}
}
运行结果:
MyParentstatic block
hello world
程序分析:
这里虽然没有初始化MyChild1,但是有没有加载呢?
下面通过JVM参数TraceClassLoading (-XX:+TraceClassLoading),用于追踪类的加载信息,并打印出来,来查看类的加载情况。
配置方式:
1、开发工具idea的配置如下所示:
Run ---> Edit Configurations…
2、操作之后,弹出一个对话框,然后在其中的VM Option中填写-XX:+TraceClassLoading,如下图所示:
注:在运行的参数(VM Option: -XX:+TraceClassLoading)写错的时候,IDE会运行报错,如下图所示:
根据以上方法是配置JVM参数。
配置好JVM参数之后,进行查i看
运行结果:
案例总结:
通过这个参数可以查看到MyParent1和MyChild1都已经被加载到内存里。
JVM参数
开头:-XX:,这个是固定的
后面:+<option>,后面的+、-,代表开启、关闭option选项
例:
-XX:+<option>参数,代表开启option选项
-XX:-<option>参数,代表关闭option选项
还有一种是设置值的方式:
-XX:<option>=<value>,将option选项的值设置为value。
例2_1:
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str);
}
}
class MyParent2 {
public static String str = "hello world";
static {
System.out.println("MyParent2 static block");
}
}
添加JVM参数方式
运行结果:
下面例子中,在MyParent2中str成员变量前面添加关键字final
例2_2:
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str);
}
}
class MyParent2 {
public static final String str = "hello world";
static {
System.out.println("MyParent2 static block");
}
}
添加JVM参数方式
运行结果:
程序分析:
加final之后为什么MyParent2没有初始化?
在编译阶段,这个常量就会存放到调用这个方法所在的类的常量池中。以上例中,这个str会被放入MyTest2类的常量池中。MyTest2并没有直接引用MyParent2的常量,所以MyParent2的静态代码块并不会执行。
案例总结:
常量在编译阶段会存入到调用这个常量的方法所在的类的常量池中。
本质上,调用类并没有直接引用到定义常量的类,因此并不会触发
定义常量的类的初始化
注意:这里指的是将常量存放到了MyTest2的常量池当中,之后的MyTest2与MyParent2就没有任何关系了。甚至我们可以将MyParent2的class文件删除。
反编译
下面反编译MyTest2类
使用命令:javap 文件名
使用参数:
-c:显示概要信息
例3_1:
反编译例2_2的MyTest2.class文件结果:
例3_2:
public class MyTest2 {
public static void main(String[] args) {
//System.out.println(MyParent2.str);
System.out.println(MyParent2.a);
}
}
class MyParent2 {
public static final String str = "hello world";
public static final short a = 7;
static {
System.out.println("MyParent2 static block");
}
}
添加JVM参数方式
运行结果:
反编译例3_2的MyTest2.class文件结果:
例3_3:
public class MyTest2 {
public static void main(String[] args) {
//System.out.println(MyParent2.str);
//System.out.println(MyParent2.a);
System.out.println(MyParent2.r);
}
}
class MyParent2 {
public static final String str = "hello world";
public static final short a = 7;
public static final int r = 128;
static {
System.out.println("MyParent2 static block");
}
}
添加JVM参数方式
运行结果:
反编译运行结果:
例3_4:
public class MyTest2 {
public static void main(String[] args) {
//System.out.println(MyParent2.str);
//System.out.println(MyParent2.a);
//System.out.println(MyParent2.r);
System.out.println(MyParent2.m);
}
}
class MyParent2 {
public static final String str = "hello world";
public static final short a = 7;
public static final int r = 128;
public static final int m = 1;
static {
System.out.println("MyParent2 static block");
}
}
添加JVM参数方式
运行结果:
反编译运行结果:
笔记
助记符部分内容
ldc:表示将int、float或是String类型的常量值从常量池推送至栈顶
bipush:表示将单字节(-128~127)的推送至栈顶
sipush:表示将一个短整型的常量值(-128~127)的推送至栈顶
iconst_1:表示将int类型推送至栈顶(iconst_1~iconst_5,最多到5的常量,6以上就是bipush)