目录
类加载器主要流程:类的使用方式:什么情况是主动使用:被动使用:举例:主动使用/被动使用: 案例一案例二案例三加载>连接(校验,准备,解析)>初始化>使用>卸载
主动使用
被动使用
所有的java 虚拟机实现必须是在Java程序首次主动使用类、接口的时候才初始化它们;
换句话说被动使用就不会初始化
首次初始化:也就是只会初始化一次;
除了以上七种,都不会进行类的初始化,但是可以进行加载连接...
类的加载:把类class的二进制文件加载内存中,放到运行时数据区的方法区,在内存中创建java.lang.Class对象,用来封装类的Class对象,唯一只有一份。
可以看成一面镜子,可以反映出类的所有内容。是用来描述这个class对象的数据结构。
加载后放在哪? jvm规范都没有规定放置的位置,hotspot是放在方法区;
类加载来源:类的class文件可以从各种途径去加载,本地系统,还可以通过网络下载,jar,zip,从专有数据库中获取。。。
还有一种从Java源文件中动态的编译class ,动态代理。。。在web开发中jsp页面转化成servlet对应的class文件。
jvm并没有规定具体来源。
结果分析:
test001:
MyParent1 静态代码块执行 hello world子类主动调用父类静态属性,父类执行初始化,并打印属性信息;
那么子类有没有进行初始化?可以通过打印类加载过程信息查看;
[Loaded jvm.classloader.MyParent1 from file:/F:/gitPro/java_study/out/production/java_study/] [Loaded jvm.classloader.MyChild1 from file:/F:/gitPro/java_study/out/production/java_study/] MyParent1 静态代码块执行 hello world ....... 可以看出即使MyChild1没有初始化也是先加载了的test002:
MyParent1 静态代码块执行 MyChild1 静态代码块执行 hello world child子类调用自己的静态属性,子类执行初始化,并且在初始化之前初始化所有的父类;
结果分析:
只打印出 hello world,在引用了父类的静态常量之后,只打印了属性信息,并没有父类初始化。
为什么?
final :本身表示常量,编译器也知道,所以在编译阶段这个常量就会被存入到调用这个方法所在类的常量池当中; 也就是 "hello world" 在编译阶段就在ClassLoaderTest2中的类的常量池中,本质上并没有引用到定义常量的这个类,因此也不会触发这个类的初始化;那么静态代码块也就不会执行;*注意: 在这个字符串在放入到ClassLoaderTest2的常量池中之后就跟MyParent2没有任何关系了,甚至可以删除编译后的MyParent2的字节码文件(在out路径下删除字节码文件依然可以运行)
结果分析:
MyParent3 静态代码块执行 b2a4e712-4df1-4bfd-a667-6d13ead5eaba跟案例二的区别在于静态变量的值是在运行期间才能确定;
静态成员变量,编译阶段是无法知道str的值的,怎么办?
那就只能在运行期间进行类初始化并执行方法;
如果像上面把MyParent3编译后的类字节码删除会抛出classNotFound 异常;在一个常量在编译期间不能确定的话就不会放在调用类的常量池中,从class文件的对比情况也可以发现。
本文版权归作者本人拥有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载于:https://www.cnblogs.com/jie3615/p/11495543.html