反射
(详解都在代码注释里)
1. 何为反射
相信大家都 new 过对象,然后去操作类内的成员方法,成员变量,构造方法,不过这些操作都是静态的,今天我们要了解的反射就是获取类,通过普通手段或暴力手段获或变量和方法,再实例化,进行操作,这些都是可以在程序运行时操作,为热加载。
2. 反射的作用
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
3. 反射获取Class的三种方式
3.1 先定义一个Worker 类
package com.xcx.reflect;
// 这里只写了一部分方法
public class Worker {
// 成原变量 Field
private int id;
private String name;
//构造方法 Constructor
public Worker() {}
//成员方法 Method
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
3.2 main 函数实现获取
package com.xcx.reflect;
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式 这里我们会遇到报错,只需要抛出异常即可
Class<?> cls1 = Class.forName("com.xcx.reflect.Worker");
// 第二种方式
Class<Worker> cls2 = Worker.class;
// 第三种方式,需要实例化 Worker 对象
Worker worker = new Worker();
Class<? extends Worker> cls3 = worker.getClass();
// 直接展示,得到的反射对象是否一样
System.out.println(cls1 == cls2);
System.out.println(cls2 == cls3);
System.out.println(cls1 == cls3);
// 输出结果是三个true
}
}
4. 通过 Class 对象获取 Constructor 构造方法对象
getConstructors,getDeclaredConstructors, getConstructor, getDeclaredConstructor
注:以 s 结尾为获取多个方法,以 Declared 开头为暴力获取私有方法
// 此方法获取类内所有的构造方法不包含私有构造方法
Constructor<?>[] constructors = cls1.getConstructors();
// (暴力获取)此方法获取类内,包括私有构造方法在内的所有构造方法
Constructor<?>[] declaredConstructors = cls1.getDeclaredConstructors();
// (★ 最常用)此方法获取了公共的有参构造方法,这里会有异常,抛出即可
Constructor<?> constructor = cls1.getConstructor(int.class, String.class);
// (暴力获取)此方法获取了私有的有参构造方法
Constructor<?> declaredConstructor = cls1.getDeclaredConstructor(String.class);
4.1 Constructor 对象实例化对象操作
对非私有构造方法实例化
/* 实例化对象操作,这里会报错,同样抛出即可,本来得到 Object 类,需要强制类型转换一下,不强转也没事,这里强转就是为了告知实际类型就是 Worker 类
*/
Worker o1 = (Worker)constructor.newInstance(10, "张三");
// 输出展示 Person [id=10, name=张三, test=10]
对私有化构造方法实例化
私有化构造方法对象 实例化需要调用 void setAccessible(boolean flag);
参数设置为 true;
// 设置权限,可以对私有化构造方法进行实例化
declaredConstructor.setAccessible(true);
// 得到权限后,实例化
Worker o2 = (Worker)declaredConstructor.newInstance("李四");
// 输出展示 Person [id=0, name=李四, test=10]
// 若没有给权限则报错 Exception in thread "main" java.lang.......
5. 反射操作核心 - Method
这里获取方法Method与获取Constructor构造方法类似,主要注意方法内部参数形式
4.1 通过 Class 对象获取对应的 Method 成员方法对象
// 此方法获取类内所有公共成员方法
Method[] methods = cls1.getMethods();
// 【暴力获取】 此方法获取类内,包括私有成员方法的所有成员方法
Method[] declaredMethods = cls1.getDeclaredMethods();
/*
【★ 核心方法】
此方法获取类内, 一个公共成员方法(必须提供方法名,参数类型,参数类型,...... )
public void game(String name) {
System.out.println("玩" + name);
}
*/
Method method = cls1.getMethod( "game", String.class);
/*
【暴力获取】
此方法获取类内, 一个私有成员方法(必须提供给方法名,参数类型, 参数类型,......)
private void testPrivate(String str) {
System.out.println("吃" + str);
}
*/
Method declaredMethod = cls1.getDeclaredMethod("testPrivate" , String.class);
4.2 Method 成员方法对象执行对应方法操作
注重 : 方法名,参数类型,参数个数
// 实例化对象
Object o = cls1.getConstructor().newInstance();
// 非私有化成员方法 ,输出结果: 玩吃鸡
method.invoke(o,"吃鸡");
System.out.println();
// 私有化成员方法赋予操作权限之后操作,这里与 Constructor内容 执行相似
declaredMethod.setAccessible(true);
// 输出结果: 吃饺子
declaredMethod.invoke(o, "饺子");
5. 反射操作数据存储和获取 - Field
注重: 成员变量名
5.1 通过 Class 对象获取对应的 Field 成员变量对象
// 获取类内所有非私有化成员变量对象数组
Field[] fields = cls1.getFields();
// 【暴力反射】获取类内所有成员变量对象数组,包括私有化成员变量对象
Field[] declaredFields = cls1.getDeclaredFields();
// 根据指定的成员变量名称,获取对应的非私有化成员变量对象,这里会报错,直接抛出异常
Field name = cls1.getField("name");
//【★ 重点方法】【暴力反射】根据指定的成员变量名称,获取对应的成员变量对象,包括私 有化成员变量
Field age = cls1.getDeclaredField("age");
5.2 Field 对象操作获取数据和赋值数据
// 实例化操作
Object o3 = cls1.getConstructor().newInstance();
// 给公有 test 赋值,不需要赋予权限
test.set(o3,20);
System.out.println(test.get(o3));
// 这里用了另一种赋予权限操作 也可以 age.setAccessible(true); 下面这一种可以同时赋予多个权限
AccessibleObject.setAccessible(new AccessibleObject[]{id, name}, true);
// 【★ 重点方法】 调用 void setAccessible(boolean flag); 给 私有化 id 赋值
id.set(o3, 1);
// 给获取权限的 私有化 name 赋值
name.set(o3, "青轴侠");
System.out.println(id.get(o3));
System.out.println(name.get(o3));
6. 再提一嘴暴力反射权限问题解决
对应方法
void setAccessible(boolean flag); // 重点方法 ★
反射操作中 Constructor, Method ,Field 对象都可以调用该方法解决 私有化反射对象权限操作问题
参数提供为 true 当前私有化构造方法对象有对应的操作权限。
public static void setAccessible(AccessibleObject[] array, boolean flag) // 重点方法 ★
AccessibleObject 类工具方法,所需参数是 AccessibleObject 数组和对应的权限标记,flag 通常为 true。
Field Method Constructor 都是 AccessibleObject 子类
7.小结
三种方式获取Class
get(变量,构造方法,成员方法)// 普通获取
get Declear (变量,构造方法,成员方法)// 暴力获取
实例化
用方法