java基础-类加载器和反射
13-1-类加载器和反射
01-类加载器-初步了解
将编译后的class
文件加载到jvm虚拟中
02-类加载器-加载过程
类在什么时候加载到内存中呢?
类加载的时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量(静态变量),或者为该变量赋值
- 使用反射方式强制创建某个类或接口对应的
java.lang.Class
对象 - 初始化某个类的子类(创建子类对象,需要父类的字节码文件也加载到内存中)
- 直接使用java.exe命令来运行某个主类
总结一句话:用到就加载,不用不加载
03-类的加载过程-加载
加载:
- 通过一个类的全限定名来获取定义此类的二进制字节流(包名+类名,获取这个类,准备用流进行传输)
- 将这个字节流所代表的静态存储结构转化为运行时数据结构(把这个类加载到内存中)
- 在内存中生产一个代表这个类的
java.lang.Class
对象,任何类被使用时,系统都会为之创建一个java.lang.Class
对象(加载完毕,创建class对象)
04-类的加载过程-链接
链接分为三步:验证-准备-解析
验证:确保Class
文件字节流中包含的信息符合当前虚拟机的要求,不会损害虚拟机安全。(验证文件是否符合虚拟机规范,是否安全)
准备:负责为类的类变量(静态变量)分配内存,设置默认初始化值。(在刚刚创建的class对象中,初始化静态变量)
解析:将类的二进制数据流中的符号引用替换为直接引用。(如果本类用到了其他的类,此时就需要找到对应的类)
假设加载的是Student
类:
起初还不知道String
这个类在哪里,先用符号表示:
假设String
如下:
在Student
类的加载过程的解析这一步,就会将符号替换成String
的地址:
05-类的加载过程-初始化
初始化:根据程序员通过程序制定的主管计划去初始化类变量和其他资源(静态变量赋值(覆盖原来的默认值)以及初始化其他资源)
小结:
- 当类使用到的时候,才会加载到内存
- 类的加载过程:
- 加载
- 验证
- 准备
- 解析
- 初始化
06-类加载器-分类
启动类加载器(Bootstrap ClassLoader):虚拟机内置的加载器(C++实现的),虚拟机启动时自动启动。
平台类加载器(Platform ClassLoader):负责加载JDK中一些特殊的模块。
系统类加载器(SysTem ClassLoader):加载用户类路径上指定的类库。
这套课程讲得和网上资料有些不同:点击跳转,其他资料一般说的是:启动类加载器、扩展类加载器、应用程序类加载器、用户自定义类加载器。
查资料后得知:Java9将扩展类加载器改为了平台类加载器
07-类加载器-双亲委派模型
除了启动类加载器之外,其他的加载器都应该有自己的父类加载器。
这里说的父类加载器,并不是
extends
,而是逻辑上的继承
当用户类加载器收到类的加载请求,不会自己去加载,而是一层层往上委托。
这些加载器,都有各自的加载范围,当父类加载器所有的加载范围,无法完成类的加载时,就会一层一层往下返回,由子加载器尝试进行加载。
在ClassLoader
这个类中有静态方法getSystemClassLoader
可以获取到系统类加载器,再通过getParent
可以获得他的父类加载器(扩展类加载器)
public class ClassLoaderDemo01 {
public static void main(String[] args) {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("systemClassLoader = " + systemClassLoader);
// 获取系统类加载器的父类加载器 --> 扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println("extClassLoader = " + extClassLoader);
// 获取扩展类加载器的父类加载器 --> 根加载器(C/C++)
ClassLoader bootClassLoader = extClassLoader.getParent();
System.out.println("bootClassLoader = " + bootClassLoader);
// 测试当前类由哪个类加载器进行加载 --> 系统类加载器
ClassLoader classLoader = ClassLoaderDemo01.class.getClassLoader();
System.out.println("当前类是谁加载的:"+classLoader);
// 测试JDK内置的类是谁加载的 --> 根加载器
ClassLoader classLoader1 = Object.class.getClassLoader();
System.out.println("Object是谁加载的:"+classLoader1);
// 如何获得系统类加载器可以加载的路径
System.out.println("系统类加载器可以加载的路径"+System.getProperty("java.class.path"));
}
}
08-类加载器-常用方法
public class ClassLoaderDemo02 {
public static void main(String[] args) {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
InputStream resourceAsStream = systemClassLoader.getResourceAsStream("prop.properties");
Properties properties = new Properties();
try {
properties.load(resourceAsStream);
System.out.println(properties);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
09-反射-概述
运行状态中:
对任意一个类,都能够知道这个类的所有属性和方法。
对于一个对象,都能够调用它的任意属性和方法。
可以先获取配置文件中的信息,动态获取信息并创建对象和调用方法。(先写好类,在配置文件指明要使用的类和调用的方法,动态去调用)
利用反射调用它类中的属性和方法时,无视修饰符。
10-反射-获取class对象
在不同的阶段,可以使用不同的方法来获取class对象,有以下三种:
public class Student {
private String name;
private Integer age;
private void study(){
System.out.println("学生正在学习");
}
private Student(String name) {
this.name = name;
System.out.println("name = " + name);
System.out.println("只有name参数的构造");
}
private Student(Integer age) {
this.age = age;
System.out.println("age = " + age);
System.out.println("只有age参数的构造");
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("全参构造");
}
public Student() {
System.out.println("无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class RefDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
// 方法1 使用静态方法获取class对象,需要传入的参数为全类名 源码阶段
Class<?> class1 = Class.forName("com.lzc.ref.ref1.Student");
System.out.println("class1 = " + class1);
// 方法2:通过class属性来获取 内存阶段
Class<Student> class2 = Student.class;
System.out.println("class2 = " + class2);
// 方法3:对象阶段
Student student = new Student();
Class<? extends Student> class3 = student.getClass();
System.out.println("class3 = " + class3);
// 需要注意,一个类只有一个class对象,是唯一的 不管用什么方式获取的,都是同一个
System.out.println("class1==class2 = " + (class1 == class2));
System.out.println("(class2==class3) = " + (class2 == class3));
}
}
11-反射-获取Constructor对象
当一个类的字节码加载到内存中以后,内存中记录了以下信息:
通过反射就可以获取这些信息。在Java中,万物皆对象。成员变量就是Field
对象,构造方法就是Constructor
对象,成员方法就是Method
对象
获取Constrictor
对象有很多方法,下面写demo:
Student
和上一节的一样:
public class Ref02Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 1 获取所有的公共的构造器方法对象数组 获取的是public修饰的,private不会返回
Class<?> clazz = Class.forName("com.lzc.ref.ref2.Student");
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("constructor = " + constructor);
}
System.out.println("==========================================================");
// 2 获取所有的构造器方法的对象数组 获取所有的构造方法对象,返回数组,public 和 private 都能获取
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("declaredConstructor = " + declaredConstructor);
}
System.out.println("==========================================================");
// 3 获取单个公共的构造器方法对象 需要注意的是,getConstructor方法传参和构造器参数一致 只能获取public 不能获取private
// 如:获取空参构造,就不传入参数
Constructor<?> constructor1 = clazz.getConstructor();
System.out.println("constructor = " + constructor1);
// 如获取全参构造,就和类中定义的一致,传入对应的class对象即可 如果没有对应的,就会报异常
Constructor<?> constructor2 = clazz.getConstructor(String.class, Integer.class);
System.out.println("constructor2 = " + constructor2);
// 4 获取单个构造器方法对象 可以获取所有的,不管是private还是public
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(Integer.class);
System.out.println("declaredConstructor = " + declaredConstructor);
}
}
运行结果:
13-反射-利用Constructor创建对象
还是一样的Student
类
public class RefDemo03 {
public static void main(String[] args) throws Exception {
method01();
method02();
method03();
}
/**
* 获取一个私有的构造器方法对象,并用他调用方法,创建Student对象,期间报错了,因为这个是私有构造
* 报错描述: class com.lzc.ref.ref3.RefDemo03 cannot access a member of class com.lzc.ref.ref3.Student with modifiers "private"
* 如何解决: declaredConstructor.setAccessible(true);
* @throws Exception 异常
*/
private static void method03() throws Exception{
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref3.Student");
// 获取一个私有的构造器方法对象
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(Integer.class);
// 被private修饰的成员不能直接使用,如果需要使用则需要取消访问检查
declaredConstructor.setAccessible(true);
// 使用这个构造器对象创建Student对象
Object o = declaredConstructor.newInstance(25);
// 强转
Student student = (Student) o;
System.out.println("student = " + student);
}
/**
* 空参构造的简写方式,直接调用class对象的newInstance()方法,已经过时,但需要了解
* @throws Exception 异常
*/
private static void method02() throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref3.Student");
// 对于空参,可以直接调用class对象中的方法, 该方法已过时
Object newInstance = clazz.newInstance();
Student student = (Student) newInstance;
System.out.println("student = " + student);
}
/**
* 获取public修饰的构造方法对象, 并使用构造方法对象的newInstance创建对象
*
* @return {@link Class}<{@link ?}>
* @throws Exception 异常
*/
private static Class<?> method01() throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref3.Student");
// 获取全参构造
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
// 利用构造器对象创建Student对象
Object o = constructor.newInstance("赖卓成", 25);
// 强转
Student student = (Student) o;
System.out.println("student = " + student);
return clazz;
}
}
14-反射-获取Field对象
上面已经通过反射获取了Contructor
对象,并用他创建对应的类的对象,下面来获取成员变量。
- Field [] getFields():返回所有公共成员变量对象的数组
- Field[] getDeclaredFields():返回所有成员变量对象的数组
- Field getField(Sring name):返回单个公共成员变量对象
- Field getDeclareField(String): 返回单个成员变量对象
public class Student {
public String name;
public Integer age;
public String gender;
private int money = 300;
}
public class RefDemo04 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref04.Student");
// 获取所有的公共属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println("field = " + field);
}
// 获取所有的属性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("declaredField = " + declaredField);
}
// 获取指定的属性 公共的
try {
Field name = clazz.getField("name");
System.out.println("name = " + name);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取指定的属性 私有的 会报错 所以需要使用getDeclaredField
Field field = null;
try {
// field = clazz.getField("money");
field = clazz.getDeclaredField("money");
System.out.println("field = " + field);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
}
15-反射-使用Field对象取值和赋值
- void set(Object obj,Object value):给指定对象的成员变量赋值
- Object get(Object obj) 返回指定对象的Field值
实体类和15的一样:
public class RefDemo05 {
public static void main(String[] args)
throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref05.Student");
// 使用clazz创建对象
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
Student instance = (Student)declaredConstructor.newInstance();
// 设置属性name
clazz.getField("name").set(instance, "lzc");
System.out.println("instance = " + instance);
// 获取私有属性money
Field field = clazz.getDeclaredField("money");
field.setAccessible(true);
Object o = field.get(instance);
System.out.println("o = " + o);
}
}
16-反射-获取Method对象
Method[] getMethods():返回所有的公共方法对象的数组,包括继承的
Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name,Class<?> ... parameterTypes) 返回单个公共成员方法对象
Method getDeclaredMethod(String name,Class<?> ...parameterTypes) 返回单个成员方法对象
实体类:
public class Student {
private void show(){
System.out.println("private - 无参无返回值的show方法");
}
public void func1(){
System.out.println("public - 无参无返回值的func1方法");
}
public void func2(String name){
System.out.println("public - 有参无返回值的func2方法");
System.out.println("name = " + name);
}
public String func3(){
System.out.println("public - 无参有返回值的func3方法");
return "func3";
}
}
public class RefDemo06 {
public static void main(String[] args) throws Exception{
method1();
System.out.println("=====================================");
method2();
System.out.println("=====================================");
method3();
System.out.println("=====================================");
method4();
}
/**
* 获取指定的成员方法 可以获取私有的
*
* @throws Exception 异常
*/
private static void method4() throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref06.Student");
// 获取私有的成员方法
Method show = clazz.getDeclaredMethod("show");
System.out.println("show = " + show);
}
/**
* 获取指定的公共方法
*
* @throws Exception 异常
*/
private static void method3() throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref06.Student");
// 获取指定的方法(公共的) 参数1 方法名 参数2 方法的参数类型 可变长参数
Method func2 = clazz.getMethod("func2", String.class);
System.out.println("func2 = " + func2);
}
/**
* 获取所有成员方法 包括私有的 不包括继承的
*
* @throws Exception 异常
*/
private static void method2() throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref06.Student");
// 获取成员方法对象数组 包括私有的 不包括继承的
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("declaredMethod = " + declaredMethod);
}
}
/**
* 获取所有公共方法 包括继承的、不包括私有的
*
* @throws Exception 异常
*/
private static void method1() throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref06.Student");
// 获取所有的成员方法 包括继承的
Method[] methods = clazz.getMethods();
// 遍历时除了会输出Student类中的方法,还会输出Object类中的方法
for (Method method : methods) {
System.out.println("method = " + method);
}
}
}
17-反射-利用Method对象运行方法
- Object invoke(Object obj,Object ...args):运行方法
参数1:用obj对象调用该方法
参数2:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
写个demo,通过反射获取Student
类中的func4()
,运行并打印它的返回值:
public class Student {
private void show(){
System.out.println("private - 无参无返回值的show方法");
}
public void func1(){
System.out.println("public - 无参无返回值的func1方法");
}
public void func2(String name){
System.out.println("public - 有参无返回值的func2方法");
System.out.println("name = " + name);
}
public String func3(){
System.out.println("public - 无参有返回值的func3方法");
return "func3";
}
public String func4(String msg){
System.out.println("public - 有参有返回值的func4方法,参数为:"+msg);
return "func4";
}
}
public class RefDemo07 {
public static void main(String[] args) throws Exception {
// 获取class对象
Class<?> clazz = Class.forName("com.lzc.ref.ref07.Student");
// 获取function4方法
Method func4 = clazz.getDeclaredMethod("func4",String.class);
// 创建Student对象
Student student = (Student) clazz.newInstance();
// 调用方法
Object result = func4.invoke(student, "lzc");
System.out.println("result = " + result);
}
}