深入理解JVM(学习过程) (3)

将Java源文件动态编译为.class文件(动态代理,web开发jsp转成servlet)

/* 举例说明: 对于静态字段来说,只有直接定义了该字段的类才会被初始化; 当一个类在初始化是,要求其父类全部都已经初始化完毕了; -XX:+TraceClassLoading,用于追种类的加载信息并打印出来。 所有的参数都是: -XX:+<option> , 表示开启option选项 -XX:+<option> ,表示关闭option选项 -XX:+<option>=<value> 表示将option选项的值设置为value */ public class MyTest1 { public static void main (String[] args){ System.out.println(MyChild1.str2); } } class MyParent1{ public static String str = "hello world"; static { System.out.println("MyParent1 static block"); } } class MyChild1 extends MyParent1{ public static String str2 = "welcome"; static{ System.out.println("MyChild1 static block"); } } 输出结果: > Task :MyTest1.main() MyParent1 static block MyChild1 static block welcome

查看类的加载信息,并打印出来。

jvm 参数介绍:
-XX:+ , 表示开启option选项
-XX:+ ,表示关闭option选项
-XX:+= 表示将option选项的值设置为value

image-20200208173021317

类加载基础概念 常量池的概念 /* 常量在编译阶段会存入到调用这个常量的方法所在的类的常量池中,本质上,调用类并没有直接用用到定义常量的类,因此并不会触发定义常量的类的初始化。 注意:这里指的是将常量存放到了MyTest2的常量池中,之后MyTest2与MyParent2就没有任何关系了。 甚至,我们可以将MyParent2的Class文件删除 */ public class MyTest2{ public static void main(String[] args){ System.out.println(MyParent2.str); } } class MyParent2{ public static final String str = "hello world"; public static final short s = 127; public static final int a = 3; public static final int m = 6; static { System.out.println("Myparent2 static block");// 这一行能输出吗?不会 } } 反编译

javap -c com.erwa.jvm.class.mytest2

反编译之后会有助记符。

助记符

ldc表示将int、float或是String类型的常量值从常量池中推送至栈顶。

bipush表示将单字节(-128~127)的常量值推送至栈顶。

sipush表示将短整型(-32767~32768)的常量值推送至栈顶。

inconst_1表示将int类型1推送至栈顶 (inconst_m1 ~inconst_5)。

anewarray 表示创建一个引用类型(如类,接口,数组)的数组,并将其引用值推至栈顶。

newarray 表示创建一个指定的原始类型(如int,float,char等)的数组,并将其引用值推至栈顶。

类的初始化规则 /* * 当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中, 这是在程序运行时,会导致主动使用这个常量所在的类,显然就会导致这个类被初始化。 */ public class MyTest3{ public static void main(String[] args){ System.out.println(MyParent3.str); } } class MyParent3{ public static final String str = UUID.randomUUID().toString(); static { System.out.println("Myparent3 static block"); // 这一行能输出吗?会 } } 为什么第二个例子不会输出,第三个例子就输出了呢? 因为第三个例子的值,是只有当运行期才会被确定的值。而第二个例子的值,是编译时就能被确定的值。 public class MyTest4{ public static void main(String[] args){ MyParent4 myParent4 = new MyParent4(); } } class MyParent4{ static { System.out.println("Myparent4 static block"); // 这一行能输出吗?会 } } 因为MyParent4 myParent4 = new MyParent4(); 属于主动使用的第一条,类的使用。 /** 对于数组实例来说,其类型是由JVM在运行期间动态生成的,表示为 [Lcom.erwa.MyTest4 这种形式,动态生成的类型,其父类型就是Object */ public class MyTest4{ public static void main(String[] args){ MyParent4[] myParent4s = new MyParent4[1]; System.out.println(myParent4s.getClass()); System.out.println(myParent4s.getClass().getSuperclass()); } } class MyParent4{ static { System.out.println("Myparent4 static block"); // 这一行能输出吗?不会 } } 因为 MyParent4[] myParent4s = new MyParent4[1]; 并不属于主动使用的方式。 > Task :MyTest4.main()输出结果为: class [Lcom.erwa.jvm.MyParent4; class java.lang.Object int[] ints = new int[1]; System.out.println(ints.getClass()); boolean[] booleans = new boolean[1]; System.out.println(booleans.getClass()); short[] shorts = new short[1]; System.out.println(shorts.getClass()); double[] doubles = new double[1]; System.out.println(doubles.getClass()); class [I class [Z class [S class [D 接口的初始化规则

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfdfy.html