类加载的过程
.java文件编译后产生.class字节码文件,放在磁盘。
加载
根据包名+类名在磁盘种找到这个字节码文件;
创建输入流;
把字节码文件读取到内存;
读到内存后不能随便放,在内存中创建一个java.lang.Class对象保存该类的信息。
假设类中有String型成员,这时候不知道String类加载了没、如果加载了放在哪,因此这里的String用符号代替
验证
检查文件信息是否符合当前虚拟机规范、会不会危害虚拟机自身安全;
准备
为静态变量分配内存,并初始化一个默认值;(是默认值0、null等)
解析
将类的二进制流中的符号引用替换为直接引用:(简单概括:如果用到其他类(包括继承父类),此时就要找到对应的类,没有就加载)
Class对象分配到地址0x0011,记录的信息中有String引用数据类型变量;
在第一步加载Student类的时候,String类加载了没有,如果加载了Class对象在哪里,我们不知道,因此这里的String引用是符号引用。
到解析这一步时,就会实际地去找String类在哪里,把符号换成String类的实际地址0x0022。
初始化
根据程序员在程序中制定的主观计划去初始化静态变量和其他资源:
这里就是静态成员实例化、静态块执行的时机。
类加载器的分类
每一种类加载器加载什么样的字节码文件都由JVM指定好了。
这里的子父类关系不是代码指定的extends关系,只是逻辑上的:
当调用子加载器加载某字节码文件时,子加载器不会直接加载,而是先调用父加载器加载它,如果得知父加载器加载不了,才自己加载。
获取三种类加载器并打印:
启动类加载器以null显示:
类加载器常用方法(都定义在ClassLoader类中):
类加载器加载资源一般用于加载配置文件:
注意:类加载器的getRescouceAsStream()参数是相对路径时,相对的是项目的src目录(IO流中是相对于项目根目录)。
反射
注意:利用反射调用类中的属性和方法时,无视访问修饰符。
获取Class对象的三种方式:
获取构造方法:
获取全部public构造方法:
获取全部构造方法:
获取单个构造方法:
单个构造同样也分Declared:
注意:获取构造方法Declared的区别是能获取private构造。
获取构造方法的参数类型:
反射创建实例:
通过构造方法创建实例:
通过私有静态方法创建实例:(private成员用反射机制调用时要先设置可访问)
Class对象直接创建实例(JDK10中已过时):
反射获取类的成员属性:
注意:获取成员属性Declared区别是可以获取private属性。
获取成员属性的信息:
反射为实例赋值:
注意:set()方法强行取消访问权限检查,不安全,不建议用。如果要用,应该在用完后把访问权限改回来。
建议通过反射调用setter方法来给属性赋值。
反射获取实例某属性的值:
反射获取类的成员方法:
注意:获取成员方法Declared区别是可以获取private方法、可以过滤掉父类方法。
反射运行类的成员方法: