一、基本概念
Java反射机制是指在运行时检查、获取和操作类、方法、字段等结构的能力。通过反射,可以在运行时动态地创建对象、调用方法、访问字段等,而不需要在编译时知道类的具体信息。这种机制使得Java程序具有更高的灵活性和动态性。
一句话总结:反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
二、核心类与接口
Java反射机制主要依赖于java.lang.reflect
包中的类和接口,这些类和接口提供了丰富的API来操作类、接口、字段、方法和构造器等。主要的核心类包括:
- Class:代表一个类的元数据,可以获取类的名称、字段、方法等信息。
- Constructor:用于描述类的构造方法。
- Method:用于描述类的方法。
- Field:用于描述类的字段。
注:必须先获得Class才能获取Method、Constructor、Field。
三、为什么要用反射?
Java Reflection功能非常强大,并且非常有用,比如:
- 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
- 获取任意对象的属性,并且能改变对象的属性
- 调用任意对象的方法
- 判断任意一个对象所属的类
- 实例化任意一个类的对象
- 通过反射我们可以实现动态装配,降低代码的耦合度,动态代理等。
四、获取Class的三方式
要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?
-
Class.forName(String className): 通过类的完全限定名获取 Class 对象。(静态方法)
-
ClassName.class: 通过类的字面常量获取 Class 对象。
-
object.getClass(): 通过对象实例获取 Class 对象。
注:以上三种方式返回值都是Class类型。
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
/*获取class对象的三种方式:
1. class.forName("全类名");
2.类名.class
3.对象.getclass( );*/
//第一种方式:
//全类名 : 包名 + 类名
//最为常用
Class<?> clazz1 = Class.forName("com.wedu.myreflect1.Student");
System.out.println(clazz1);
//第二种方式
//一般更多的是当做参数进行传递
Class<Student> clazz2 = Student.class;
System.out.println(clazz2);
//第三种方式
//当我们已经有了这个类的对象时,才可以使用
Student s = new Student();
Class clazz3 = s.getClass();
五、通过反射实例化对象
对象.newInstance()
注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
否则会抛出java.lang.InstantiationException异常。
eg.
Class<?> clazz1 = Class.forName("com.wedu.myreflect1.Student");
Object o = clazz1.newInstance();
System.out.println(o);
如今class.newInstance()在java9时被@Deprecated
六、反射Filed【反射/反编译一个类的属性】
6.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() | 返回调用类实现的接口集合 |
eg.这里我举例了几个
//1.获取class字节码文件的对象
Class clazz = Class.forName("com.wedu.myreflect3.Student");
//2.获取所有的成员变量
/*Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}*/
//获取单个的成员变量
Field name = clazz.getDeclaredField("name");
System.out.println(name);
//获取权限修饰符
int modifiers = name.getModifiers();
System.out.println(modifiers);
6.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) | 读取属性值 |
eg.反编译一个类的属性Field
//1.获取class字节码文件的对象
Class clazz = Class.forName("com.wedu.myreflect3.Student");
//获取成员变量名字
String n = name.getName();
System.out.println(n);
//获取成员变量数据类型
Class<?> type = name.getType();
System.out.println(type);
//获取成员变量记录值
Student s = new Student("张三", 12, "男");
name.setAccessible(true);
String value = (String) name.get(s);
System.out.println(value);
//修改对象里面的值
name.set(s,"lisi");
System.out.println(s);
set()可以访问私有属性嘛?
不可以,需要打破封装,才可以。
Fidle方法:
方法 | 备注 |
---|---|
public void setAccessible(boolean flag) | 默认false,设置为true为打破封装 |
七、反射Method【反射/反编译一个类的方法】
方法名 | 备注 |
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) | 调用方法 |
注: Method类中invoke()使用注意事项:
方法.invoke(对象, 实参);
八、反射Constructor【反射/反编译一个类的构造方法】
方法名 | 备注 |
public String getName() | 返回构造方法名 |
public int getModifiers() | 获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】 |
public Class<?>[] getParameterTypes() | 返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】 |
public T newInstance(Object … initargs) | 创建对象【参数为创建对象的数据】 |
反射机制创建对象两步骤
- 先获取到这个有参数的构造方法【用ClassgetDeclaredConstructor()方法获取】
- 调用构造方法new对象【用Constructor类的newInstance()方法new对象】
九、获取一个类的父类以及实现的接口
两个方法【Class类中的】
- public native Class<? super T> getSuperclass()
- public Class<?>[] getInterfaces()
// String举例
Class vipClass = Class.forName("java.lang.String");
// 获取String的父类
Class superclass = vipClass.getSuperclass();
// 获取String类实现的所有接口(一个类可以实现多个接口。)
Class[] interfaces = vipClass.getInterfaces();
System.out.println(superclass.getName());
for (Class i : interfaces) {
System.out.println(i.getName());
}