JVM学习 类的使用

JAVA程序对类的使用(可以从字节码层面来看)分为
1)主动使用
2) 被动使用
所有的java虚拟机实现必须在每个类或接口被java程序‘首次主动使用时’才初始化。
主动使用
单独的地址链接:https://blog.csdn.net/qq_34149223/article/details/106540866
1.创建类的实例 如 : Object obj = new Object
2.访问某个类或者接口的静态变量(getStatic),或对静态变量赋值(putStatic)。
3.调用类的静态变量(invoke_static 助记符)
4.反射 如: Class.forName("com.test.Test”)
5.初始化一个类的子类(会主动初始化其父类)
6.jdk7新加入的动态语言支持,jvava.lang.invoke,MethodHandle实例最后的解析结果为REF_getStatic,REF_putStatic,REF_invokeStatic,REF_newInvokeSpecial四中类型句柄( ),且这个方法的句柄对应的类没有进行初始化,则需要初始化。
7.java虚拟机启动时被标明为Main的主类。
被动使用
除了上面的主动使用的情况,其他都是被动使用,不会导致类的第三个阶段初始化。
被动使用案例1

package other;

public class MyTest01 {

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

	}

}

class MyParent1{
	public static String str = "hello world";
	static {
		System.out.println("Myparent1 static  block");
	}
}
class MyChild1 extends MyParent1{
	static {
		System.out.println("MyChild1 static block");
	}
}

先进行理论判断结果:
应该会打印 ,猜测依据,加载类时会初始化其static修饰的内容。
HelloWorld!
Myparent1 static block
MyChild1 static block
实际上不是上述的猜测,而是:
JVM学习 类的使用_第1张图片真的是实力和现实打脸,为啥 没有MyChild1 static block ,而且hello world的顺序也不一样?

先分析这个代码,三个类,MyTest01,MyParent1, MyChild1
当运行这段代码时: 首先会进行主动使用,jvm会寻找包含mian方法的主类,所以MyTest01第一个加载,这一点没有疑问。
然后 时机问题,加载的时候由于调用 "MyChild1.str"是在 在这个类的加载之后,属于类的使用阶段(一个类没有被加载到内存中,怎么使用?所以先把这个类的二进制文件放到内存中),所以按理来说会中加载初始化MyChild1类,但是它有一个父类,所以优先第二个加载的类初始化的是MyParent1,所以会执行MyParent1类中的静态代码块。(注意:当一个类加载的时候,如果有静态变量,会为其分配内存空间(变量初始化),主动使用的加载时,有静态代码块会执行(初始化))。所以第一个打印的应该是
MyParant static block
好了,接下来,加载初始化完父类,就应该初始化子类了吧 ,main方法中不是使用了 MyChild1.str吗 ,但是实际上对于静态字段来说,只有直接定义了该字段的类才会被初始化。 由于MyChild1.str中的str是父类的成员,所以只会初始化父类,所以这个子类是没有初始化的,没有主动使用初始化子类,这个就是被动使用(没有初始化就是被动使用)。
然后就是调用了,直接打印Hello world。

疑问,类的加载过程,分为 "加载->连接(验证,准备,解析)->初始化->使用->卸载"五大过程,那么调用MyChild1.str这段代码上述证明是没有初始化,没有调用打印MyChild1 static block的,那么MyChild1这个类有没有进行过加载哪?
虚拟机参数-XX:+TraceClassLoading 参数用于追踪类的加载信息并打印出来
点击这个主类,右键run as-> run configurations
添加参数
JVM学习 类的使用_第2张图片重新运行:
JVM学习 类的使用_第3张图片第一个红框,加载main所在的主类,这个和分析一致,
下面的是加载的父类和子类。实际上证明,这个子类是加载了的,只是没有初始化,所以没有执行子类的静态代码块,所以没有打印MyChild1 static block。
其他分析: 发现除了编写的三个类加载了,其实还加载了其他的类,很多其第一个类是Object类
JVM学习 类的使用_第4张图片

代码修改:

package other;

/**
 * 对于静态字段来说,只有直接定义了该字段的类才会被初始化
 * 1.jvm参数类别
 1)前缀都是" -XX :"
   +-参数:
    -XX:+<option>,表示开启option选项
    -XX:-<option>,表示关闭option选项
    这两个都是设置boolean值, +,-代表 true,false
    赋值参数:
    -xx:<option>=<value>,  表示将option选项的值设置为value

 *
 */
public class MyTest01 {

	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 = "child";
	static {
		System.out.println("MyChild1 static block");
	}
}

在子类中增加静态成员,且在mian方中调用,一共三个类,MyTest01,MyParent1, MyChild1,此时推测, 第一个加载的类仍然是main方法所在的类,这个和上述一致,第二个加载的是MyParant父类(满足主动使用条件,.初始化一个类的子类(会主动初始化其父类)),然后看调用,调用的是子类的成员对吧,满足主动使用条件-调用类的静态变量,所以会加载初始化子类, 最后打印调用结果。如下
初始化父类: 打印Myparent1 static block
初始化子类: 打印MyChild1 static block
调用使用: 打印child
查看运行结果:
JVM学习 类的使用_第5张图片果然如此。分析的结果是对的。
小结: 静态变量是谁定义的,就是对谁的主动使用,谁就会被初始化。

你可能感兴趣的:(Jvm学习,java)