一、类加载器
1. 类加载器(Classloader):
- 是Java运行时环境的一部分,负责动态加载Java类到Java虚拟机的内存空间中
2. 类的加载时机
- 创建该类对象时
- 使用该类的静态成员时
- 通过反射创建对象时
- 创建该类的子类对象时
- 使用java命令,直接运行该类
3. 类的加载过程
加载:
将硬盘中的.class文件,由类加载器,通过该类的全类名(包名+类名)以字节的形式,加载到内存中的一个过程。
验证:
通过javac编译java文件,会进行第一个安全验证。
再次验证字节码文件中,是否有威胁JVM安全的代码。
准备:
对静态成员进行默认初始化的工作。 【静态属于类】
非静态成员,在创建对象时,才会默认初始化。 【非静态属于对象】
解析:
将类中的符号引用,替换成直接引用。
初始化:
就静态成员的默认初始化,替换成显示初始化。
4.类加载器的分类
- 跟类加载器(Botstrap ClassLoader):加载JDK核心类
- 扩展类加载器(Ext ClassLoader):加载JDK扩展类库的类
- 应用城西类加载器(Application ClassLoader):加载用户自定义的类
- 用户自定义类加载器(User ClassLoader)
5. 双亲委派机制
当某个类被加载时,入口是 Application ClassLoader,但是该类加载器不会直接加载,而是向其父类加载器(Ext ClassLoader)询问,该类也不会直接加载,而是再次向其父类加载器(Bootstrap ClassLoader)发起询问,如果该类是由跟类加载器负责,则有跟类加载器加载;否则回到其子类(Ext ClassLoader)判断是否为其负责,如果是则有该类加载器加载;否则回到其子类(Application ClassLoader)加载。
- **目的:**防止用户自定义类覆盖JDK核心类
- 方法:
- getClassLoader() 获取类加载器对象
- getResourceAsStream(String name) 以流的形式加载src下的配置文件name
二、反射
- Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键
- 反射操作的是字节码文件
- 字节码文件:是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件,是一种中间码;其中包含了该类的所有成员(包括私有成员)
- 程序阶段
- 源码阶段 --> 编译阶段 --> 【运行阶段】
- 源码阶段 --> 编译阶段 --> 【运行阶段】
1. 构造方法(Constructor)
- 可以用来创建对象、给成员变量赋值
- Constructor getConstructor(Class…clazz) 获取被public修饰的构造方法对象。
- Constructor[] getConstructors(); 获取所有被public修饰的构造方法对象。
- Constructor getDeclaredConstructor(Class…clazz); 获取被任意权限修饰符修改的构造方法对象(包括private修饰的)。
- Object newInstance(); 创建对象
2. 成员变量、静态变量 Field
- 可以给成员变量赋值、取值
- Field getField(String name); 获取被public修饰的指定的字段对象
- Field[] getFields(); 获取被public修饰的指定的字段对象
- Field getDeclaredField(String name); 获取被任意权限修饰符,修饰的指定的字段对象
- set(Object obj,Object value); 给指定对象的field字段赋值。
- get(Object obj); 获取指定对象field字段的值。
3. 成员方法、静态方法 Method
- Method getMethod(String name,Class…clazz); 获取被public修饰的指定的方法对象
- Method[] getMethods(); 获取被public修饰符,修饰的指定的方法对象
- Method getDeclaredMethod(String name,Class…clazz); 获取被public修饰的指定的方法对象
- Object invoke(Object obj,Object…obj); 调用方法,并返回结果
setAccessible(true)
- 开启暴力反射,开启后才可以操作private修饰的成员,但这种方式破坏了Java的封装性原则,【不建议】使用
- 注意:静态成员(变量、方法),同上面的操作基本相同,唯一不同的是,不需要传入对象,直接给null;
4. 获取字节码对象
- Class clazz = Class.forName(“全类名”)
- Class clazz = 类名.class
- Class clazz = 对象名.getClass();
套路:
1. 获取该类的字节码对象
2. 获取构造方法对象,创建该类的对象
3. 获取变量对象
4. 获取方法对象
举个栗子
package com.st.Demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Student {
public String name;
private int age;
public void saySomething(){}
public Student(){}
private Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Demo {
public static void main(String[] arg) throws Exception {
// 1. 获取该类的字节码对象
Class<?> clazz = Class.forName("com.st.Demo.Student");
// 2. 获取构造方法对象,创建该类的对象
Constructor<?> cons = clazz.getConstructor();
Object stu = cons.newInstance();
// 3. 获取变量对象
Field name = clazz.getField("name");
// 4. 获取方法对象
Method method = clazz.getMethod("saySomething");
method.invoke(stu); //参数为调用 stu 的method(saySomething)方法
}
}