Java基础之反射
一、什么是反射
JAVA机制反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象,总的来说:反射就是把java类中的各种成分映射成一个个的Java对象。
二、反射机制的相关工具包
java.lang.reflect.*;
三、反射机制相关的重要的类
类 | 含义 |
---|---|
java.lang.Class | 代表整个字节码。代表一个类型,代表整个类。 |
java.lang.reflect.Method | 代表字节码中的方法字节码。代表类中的方法。 |
java.lang.reflect.Constructor | 代表字节码中的构造方法字节码。代表类中的构造方法。 |
java.lang.reflect.Field | 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。 |
四、讲解前准备
public class Person {
/*变量:姓名*/
public String name;
/*变量:年龄*/
private Integer age;
/*构造方法*/
public Person(){}
public Person(String name,Integer age){
this.name=name;
this.age=age;
}
/*getter setter方法*/
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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*通用方法*/
public String sayHello(){
return "Hello,my name is "+this.name;
}
/*私有方法*/
private String privateMethod(){
return "this is private method";
}
}
五、获取Class字节码
(1)为什么要获取Class字节码
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
(2)如何获取Class字节码
以下三种方式:
- Class.forName(“完整类名带包名”)
- 对象.getClass()
- 任何类型.class
六、通过反射实例化对象
ClassObject.newInstance()
注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。否则会抛出java.lang.InstantiationException异常。
例子:
@Test
public void ReflectTest01() throws ClassNotFoundException, InstantiationException, IllegalAccessException {//实例初始化
//通过反射机制获取class,并用其进行实例化
Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
// 通过class获取实例
Object personObject = personClass.newInstance();
System.out.println(personObject); // ===>com.xunmeng.reflect.Person@1540e19d
}
七、Class.forName注意点
如果你只是希望一个类的静态代码块执行,其它代码一律不执行,可以使用这个方法,它执行会导致类加载,类加载时,静态代码块执行。
八、反射对类的属性操作
(1)Class类方法
方法名 | 备注 |
---|---|
public T newInstance() | 创建对象 |
public String getName() | 返回完整类名带包名 |
public String getSimpleName() | 返回类名 |
public Field[] getFields() | 返回类中public修饰的属性 |
public Field[] getDeclaredFields() | 返回类中所有的属性 |
public Field getDeclaredField(String name) | 根据属性名name获取指定的属性 |
public native int getModifiers() | 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】 |
public Method[] getDeclaredMethods() | 返回类中所有的实例方法 |
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 根据方法名name和方法形参获取指定方法 |
public Constructor<?>[] getDeclaredConstructors() | 返回类中所有的构造方法 |
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 根据方法形参获取指定的构造方法 |
public native Class<? super T> getSuperclass() | 返回调用类的父类 |
public Class<?>[] getInterfaces() | 返回调用类实现的接口集合 |
… | … |
例子:
@Test
public void ReflectTest02() throws ClassNotFoundException, InstantiationException, IllegalAccessException {//Class类方法
//通过反射机制获取class,并用其进行实例化
Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
// 通过class获取实例
Object personObject = personClass.newInstance();
System.out.println(personObject); // ===>com.xunmeng.reflect.Person@1540e19d
//获取完整类名带包名
String personAllClassName = personClass.getName();
System.out.println(personAllClassName); //===>com.xunmeng.reflect.Person
//获取类名
String simpleName = personClass.getSimpleName();
System.out.println(simpleName);// ===>Person
//获取中public修饰的属性
Field[] fields = personClass.getFields();
Arrays.stream(fields).forEach(field -> {
System.out.println(field.getName());
});// ===>[name]
}
(2)Field类方法【属性操作方法】
方法名 | 备注 |
---|---|
public String getName() | 返回属性名 |
public int getModifiers() | 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】 |
public Class<?> getType() | 以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】 |
public void set(Object obj, Object value) | 设置属性值 |
public Object get(Object obj) | 读取属性值 |
例子:
@Test
public void ReflectTest03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {//属性操作方法
//通过反射机制获取class,并用其进行实例化
Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
//获取所有的属性
Field[] declaredFields = personClass.getDeclaredFields();
Arrays.stream(declaredFields).forEach(field -> {
System.out.println(field.getName());
});// ===>[name,age]
Arrays.stream(declaredFields).forEach(field -> {
System.out.println(field.getType());
});// ===>["class java.lang.String","class java.lang.Integer"]
//属性赋值
// 1.先创建一个实例对象
// 通过class获取实例
Object personObject = personClass.newInstance();
//2.设置相关属性
Field nameField = personClass.getDeclaredField("name");
nameField.set(personObject,"xunmeng");
//3.获取相关属性值
Object name = nameField.get(personObject);
System.out.println(name);// ===>xunmeng
}
(3)解决set()访问私有属性问题
- 正常情况下不可以用
set()
为私有属性赋值,需要打破封装,才可以。 - Fidle方法:
方法 | 备注 |
---|---|
public void setAccessible(boolean flag) | 默认false,设置为true为打破封装 |
- 例子
@Test
public void ReflectTest03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {//属性操作方法
//通过反射机制获取class,并用其进行实例化
Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
//......
//属性赋值
// 1.先创建一个实例对象
// 通过class获取实例
Object personObject = personClass.newInstance();
//......
//私有属性操作
Field ageField = personClass.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(personObject,21);
Object age = ageField.get(personObject);
System.out.println(age); // ===>21
}
九、反射对方法的操作
API:
方法名 | 备注 |
---|---|
public String getName() | 返回方法名 |
public int getModifiers() | 获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】 |
public Class<?> getReturnType() | 以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】 |
public Class<?>[] getParameterTypes() | 返回方法的参数列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】 |
public Object invoke(Object obj, Object… args) | 调用方法 |
例子:
@Test
public void ReflectTest04() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {//反射Method
//通过反射机制获取class,并用其进行实例化
Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
// 通过class获取实例
Object personObject = personClass.newInstance();
// 获取所有的Method(包括私有的!)
Method[] declaredMethods = personClass.getDeclaredMethods();
Arrays.stream(declaredMethods).forEach(method -> {
System.out.println(method.getName());
}); // ===>[getName,setName,setAge,sayHello,getAge]
//修饰符列表
Arrays.stream(declaredMethods).forEach(method -> {
System.out.println(Modifier.toString(method.getModifiers()));
}); // ===>[public,public,public,public,public,private]
//获取参数列表
Method setNameMethod = personClass.getMethod("setName", String.class);
Class<?>[] parameterTypes = setNameMethod.getParameterTypes();
Arrays.stream(parameterTypes).forEach(type->{
System.out.println(type.getName());
});// ===>java.lang.String
//调用方法 getDeclaredMethod可获取私有和公有方法
Method privateMethod = personClass.getDeclaredMethod("privateMethod", null);
// *打破封装
privateMethod.setAccessible(true);
Object invoke = privateMethod.invoke(personObject, null);
System.out.println(invoke); // ===>this is private method
}
十、反射对构造函数的操作
API:
方法名 | 备注 |
---|---|
public String getName() | 返回构造方法名 |
public int getModifiers() | 获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】 |
public Class<?>[] getParameterTypes() | 返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】 |
public T newInstance(Object … initargs) | 创建对象【参数为创建对象的数据】 |
例子:
@Test
public void ReflectTest05() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//通过反射机制获取class,并用其进行实例化
Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
//获取构造函数
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
Arrays.stream(declaredConstructors).forEach(constructor -> {
System.out.print("构造函数名:"+constructor.getName()+"===》参数:");
Class<?>[] parameterTypes = constructor.getParameterTypes();
Arrays.stream(parameterTypes).forEach(parameterType->{
System.out.print(parameterType.getName());
});
System.out.println();
});// 构造函数名:com.xunmeng.reflect.Person===》参数:null; 构造函数名:com.xunmeng.reflect.Person===》参数:java.lang.Stringjava.lang.Integer
//创建对象
Constructor<?> declaredConstructor = personClass.getDeclaredConstructor(String.class, Integer.class);
Object xunmeng = declaredConstructor.newInstance("xunmeng", 21);
System.out.println(xunmeng); // ===>Person{name='xunmeng', age=21}
}