反射
- 反射就是通过获取class字节码文件对象、Class的类对象,获取该字节码文件对象中的成员变量,构造方法,和成员方法。
- 相当于是直接去访问编译后的class字节码文件。
获取class字节码文件对象
有三种方法
Object中的getClass() 方法
- 同一个类的不同对象调用getClass()返回的是相同的class字节码对象
Student s1 = new Student();
Student s2 = new Student();
Class<? extends Student> class1 = s1.getClass();
Class<? extends Student> class2 = s2.getClass();
System.out.println(s1 == s2);// false
System.out.println(class1 == class2);// true
任何数据类型的静态属性.class
Class<Student> c2=Student.class;
Class<Integer> c3=Integer.class;
Class类中的方法
- forName(String className),这里的参数需要填写一个全路径名
- 可以在类名上右击选Copy Qualified Name来获取全路径:包名.类名
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> name = Class.forName("reflect.Test");//包名.类名
}
}
Class类
方法
Constructor:
Constructor<?>[] getConstructors()
获取当前类中所有公共的构造方法,返回的是一个数组。
Constructor<?>[] getDeclaredConstructors()
获取所有的构造方法,包括私有的,受保护的。返回的是一个数组。
Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定参数的公共的构造方法,如果访问无参构造就不需要指定参数,指定的参数是参数类型.class
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定参数的构造方法,可以获取任意权限的构造方法。
Field:
Field getField(String name)
获取指定的公共的成员变量,需要指定成员变量的变量名
Field[] getFields()
获取所有的公共的成员变量。返回值是数组。
Field getDeclaredField(String name)
获取指定的成员变量,需要指定成员变量的变量名。可以获取任意权限的成员变量。
Field[] getDeclaredFields()
获取所有的成员变量
Method:
Method[] getMethods()
获取所有的公共的成员方法的数组,包括当前类中继承的公共的成员方法。
Method[] getDeclaredMethods()
获取当前类中的所有的成员方法的数组。不包含继承来的成员方法。
Method getMethod(String name, Class<?>... parameterTypes)
- name:需要获取的方法的方法名
- parameterTypes参数类型,对于有参方法,需要填写参数类型.class,对于无参方法则只需指定方法名即可,对于参数为泛型的可以写
Object.class
。
获取当前类中指定的公共的方法。以及当前类的父类中公共的访问方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取当前类中任意的方法,不包括父类中的。
相关类
Field
- 代表成员变量,提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
- 反射的字段可能是一个类(静态)字段或实例字段。
方法:
set(Object obj, Object value)
- obj:准备被修改的对象
- value:需要修改的值
对指定对象添加当前Field实例代表的成员变量。
setAccessible(boolean flag)
跳过Java权限检查
Constructor
- 代表构造方法,提供关于类的单个构造方法的信息以及对它的访问权限。
方法:
T newInstance(Object... initargs)
使用指定的参数对类进行初始化并创建新的实例,返回值是Object类对象。
setAccessible(boolean flag)
参数为true时表示跳过Java权限检查。
Method
- 代表成员方法,类或接口上单独某个方法(以及如何访问该方法)的信息
方法:
Object invoke(Object obj, Object... args)
obj:绑定的对象
args:传入的参数
- 执行当前Method实例引用的方法,需要指定一个当前类的引用对象去执行。
- 对于静态方法,指定的
obj
对象将被忽略也可以为null。 - 对于需要传入参数的方法,还需要给传入相应类型的参数
- 对于有返回值的方法,该方法的返回值就是
invoke
方法的返回值。 invoke
方法的返回值都是Object
类型
setAccessible(boolean flag)
参数为true时跳过Java的权限检查
案例
- 以下案例中使用相同的自定义标准类:
/**
* 自定义一个标准类
*/
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 私有的构造方法
private Student(String name) {
super();
this.name = name;
}
public Student() {
super();
}
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
//带参数有返回值的方法
public String show(int a, int b) {
return "a+b=" + (a + b);
}
//没有返回值不带参数的方法
public void show1() {
System.out.println("show1");
}
//带参数有返回值私有的方法
private int show2(int a) {
System.out.println("私有show2");
return a;
}
}
通过反射访问构造方法
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
// 获取字节码文件
Class<?> c = Class.forName("reflect.Student");
// 获取公共的有参,需要添加参数类型.class,如果需要访问无参构造则不用写
Constructor<?> con = c.getConstructor(String.class, int.class);
System.out.println(con);
// public reflect.Student(java.lang.String,int)
// 初始化创建实例
Object o1 = con.newInstance("张三", 22);
System.out.println(o1);// Student [name=张三, age=22]
// 获取私有的有参构造
Constructor<?> con2 = c.getDeclaredConstructor(String.class);
// 访问私有的有参构造需要跳过权限检查
con2.setAccessible(true);
// 初始化
Object o2 = con2.newInstance("李四");
System.out.println(o2);// Student [name=李四, age=0]
}
}
访问成员变量
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("reflect.Student");
// 访问私有的有参构造
Constructor<?> con2 = c.getDeclaredConstructor(String.class);
con2.setAccessible(true);
Object o2 = con2.newInstance("李四");
System.out.println(o2);// Student [name=李四, age=0]
//给私有的成员变量赋值
Field f1 = c.getDeclaredField("age");
//跳过权限检查
f1.setAccessible(true);
//赋值
f1.set(o2, 21);
System.out.println(o2);//Student [name=李四, age=21]
}
}
访问成员方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("reflect.Student");
Constructor<?> con = c.getConstructor();
Object o = con.newInstance();
// 执行show1方法
Method show1 = c.getMethod("show1");
show1.invoke(o);//show1
// 有返回值有参数的show方法
Method show = c.getMethod("show", int.class, int.class);
//参数为方法名,参数类型.class
Object invoke = show.invoke(o, 5, 6);
//参数为指定对象,参数值
System.out.println(invoke);//a+b=11
//私有的有返回值有参数的show2方法
Method show2 = c.getDeclaredMethod("show2", int.class);
//解除权限限制
show2.setAccessible(true);
Object invoke2 = show2.invoke(o, 5);//私有show2
System.out.println(invoke2);//5
}
}
自定义方法,可将obj对象中名为propertyName的属性的值设置为value。
import java.lang.reflect.Field;
public class Test {
public static void setProperty(Object obj, String propertyName, Object value) throws Exception {
// 获取对象字节码文件
Class<? extends Object> c = obj.getClass();
// 获取指定的字段
Field f = c.getDeclaredField(propertyName);
// 取消权限检查
f.setAccessible(true);
// 修改信息
f.set(obj, value);
}
/**
* 测试
*/
public static void main(String[] args) throws Exception {
Student s1 = new Student("张三", 18);
System.out.println(s1);//Student [name=张三, age=18]
Test.setProperty(s1, "name", "李四");
System.out.println(s1);//Student [name=李四, age=18]
}
}
给指定Integer泛型的ArrayList数组添加String类型数据
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
// 创建一个泛型为Integer类型的ArrayList数组
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
// 获取Class字节码文件
Class<? extends ArrayList> c = list.getClass();
// 获取add方法,add方法参数类型为泛型,可以指定为Object.class
Method m = c.getMethod("add", Object.class);
// 执行方法
m.invoke(list, "你好");
m.invoke(list, "hello");
System.out.println(list);// [100, 200, 你好, hello]
}
}