反射
一. 类加载
- 类加载的描述
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
- 类的加载
- 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
- 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
- 类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
- 类的初始化
- 在该阶段,主要就是对类变量进行初始化
- 类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
- 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
- 类的初始化时机
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
二. JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
ClassLoader中的两个方法
- Static ClassLoader getSystemClassLoader(): 返回用于委派的系统类加载器
- ClassLoader getParent(): 返回父类加载器进行委派
三. 反射
java反射机制:
是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译器就完成确定,在运行期仍然可以扩展
获取Class类的对象
概述:
我们要通过反射去使用一个类,首先要获取该类的字节码文件现象,有三种方式:
- 类名.class
- 对象.getClass()
- Class.forName(包名)
// 通过类名 获得class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
System.out.println("--------");
// 两次获得的 class对象是同一个
Class c2 = Student.class;
System.out.println(c1 == c2);
System.out.println("--------");
// 通过对象获得 class对象
Class<? extends Student> c3 = new Student().getClass();
System.out.println(c3 == c1);
System.out.println("--------");
// 通过包路径获得 class对象
Class<?> c5 = Class.forName("com.itheima_02.Student");
System.out.println(c5 == c1);
输出
class com.itheima_02.Student
--------
true
--------
true
--------
true
反射获取构造方法
// 获取类
Class<?> s = Class.forName("com.itheima_02.Student");
// 获取公共的构造方法
Constructor<?>[] constructors = s.getConstructors();
for (Constructor<?> con: constructors) {
System.out.println(con);
}
System.out.println("--------");
// 获取所有的构造方法,包括私有的和默认的
Constructor<?>[] declaredConstructors = s.getDeclaredConstructors();
for (Constructor<?> declareCon: declaredConstructors) {
System.out.println(declareCon);
}
System.out.println("--------");
Object o = s.getDeclaredConstructor().newInstance();
System.out.println(o);
案例一
/*
通过反射实现如下的操作:
Student s = new Student("林青霞",30,"西安");
System.out.println(s);
*/
public class ReflectDemo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取class
Class<?> cla = Class.forName("com.itheima_02.Student");
// 获取构造器对象
Constructor<?> con = cla.getConstructor(String.class, int.class, String.class);
// 通过构造器,生成对象
Object o = con.newInstance("林青霞", 30, "西安");
System.out.println(o);
}
}
通过反射访问私有的构造器方法
declaredConstructor.setAccessible(true);
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 类
Class<?> aClass = Class.forName("com.itheima_02.Student");
// 私有构造器
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
// 暴力反射
declaredConstructor.setAccessible(true);
// 创建对象
Object o = declaredConstructor.newInstance("林青霞");
System.out.println(o);
}
通过反射给对象的 成员变量、私有成员变量 赋值
Field.setAccessible(true);
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 类
Class<?> aClass = Class.forName("com.itheima_02.Student");
// 获取所有的成员变量
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("--------");
// 获取特定的成员变量
Field address = aClass.getField("address");
// 获取对象
Object o = aClass.getConstructor().newInstance();
// 给对象的address成员变量赋值
address.set(o, "北京");
System.out.println(o);
}
私有成员变量
// 获取 Class 对象
Class<?> c1 = Class.forName("com.itheima_02.Student");
// 获取它的成员变量
Field addresss = c1.getDeclaredField("name");
// 获取一个对象,并给这个对象的 地址 属性赋值
// 无参的构造器 创建对象
Object o = c1.getConstructor().newInstance();
// 暴力反射
addresss.setAccessible(true);
// 赋值
addresss.set(o,"林青霞");
System.out.println(o);
案例二
/*
练习:通过反射实现如下操作
Student s = new Student();
s.name = "林青霞";
s.age = 30;
s.address = "西安";
System.out.println(s);
*/
public class ReflectDemo02 {
public static void main(String[] args) throws Exception {
// 创建 Class 对象
Class<?> c1 = Class.forName("com.itheima_02.Student");
// Student s = new Student();
Object o = c1.getConstructor().newInstance();
// s.name = "林青霞";
// 获取属性对象
Field name = c1.getDeclaredField("name");
// 暴力反射
name.setAccessible(true);
// 设置
name.set(o,"林青霞");
// s.age = 30;
Field age = c1.getDeclaredField("age");
age.setAccessible(true);
age.set(o,1);
// s.address = "西安";
Field address = c1.getDeclaredField("address");
address.setAccessible(true);
address.set(o,"河南");
// System.out.println(s);
System.out.println(o);
}
}
通过反射给对象的 成员方法
// 类
Class<?> aClass = Class.forName("com.itheima_02.Student");
// 拿到方法
Method method1 = aClass.getMethod("method1");
Method method2 = aClass.getMethod("method2", String.class);
// 生成对象
Object o = aClass.getConstructor().newInstance();
// 调用method
method1.invoke(o);
method2.invoke(o,"林青霞");
反射获取私有成员方法并使用
public class ReflectDemo01 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException, ClassNotFoundException {
// 创建 Class 对象
Class<?> c1 = Class.forName("com.itheima_02.Student");
// 获取成员方法
// public String method3(String s, int i) {return s + "," + i;}
Method method3 = c1.getDeclaredMethod("method3", String.class, int.class);
method3.setAccessible(true);
// 使用
Object o = c1.getConstructor().newInstance();
Object s = method3.invoke(o, "zhangsan", 18);
System.out.println(s);
}
}
案例三 强行给集合添加元素
/*
我有一个ArrayList<Integer>集合,现在我想在这个集合中添加一个字符串数据,如何实现?
*/
public class ReflectTest01 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
ArrayList<Integer> arr = new ArrayList<>();
arr.add(10);
arr.add(20);
// 获得字节码文件
Class<? extends ArrayList> c1 = arr.getClass();
// 获得 add方法
Method add = c1.getMethod("add", Object.class);
// 条件 字符串
add.invoke(arr,"hello");
add.invoke(arr,"world");
add.invoke(arr,"java");
// 打印
System.out.println(arr);
}
}
案例四 读取配置文件
/*
练习2:通过配置文件运行类中的方法
*/
public class ReflectTest02 {
public static void main(String[] args) throws Exception{
// 写
BufferedWriter Writer = new BufferedWriter(new FileWriter("myReflect/ith.txt"));
Writer.write("className=com.itheima_06.Student");
Writer.newLine();
Writer.write("method=study");
Writer.close();
// 读
BufferedReader reader = new BufferedReader(new FileReader("myReflect/ith.txt"));
Properties properties = new Properties();
properties.load(reader);
// 解析
String className = properties.getProperty("className");
String method = properties.getProperty("method");
// 反射
Class c1 = Class.forName(className);
Method method1 = c1.getMethod(method);
// 执行
Object o = c1.getConstructor().newInstance();
method1.invoke(o);
}
}