1.反射的概念
Java 的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
通过上面的概念,可以看出有两个意义:
- 任何一个类,都能够知道这个类的所有属性和方法
- 任何一个对象,都能够调用它的任意一个方法
下面我们就根据上述这两个意义,来说明下反射。
2.实验的基本代码组成
主要是创建了Student.java和App.java两个文件,其中Student代表学生类,App用作测试类。基本代码如下:
package org.example;
/**
* @createTime 2023-02-06 10:58
* @description
*/
public class Student {
private String name;
private Integer age;
private String address;
public Student() {
}
public Student(String name) {
this.name = name;
}
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void like() {
System.out.println("我的爱好是打篮球");
}
public void reading(String book) {
System.out.println("我正在读" + book);
}
private String getFatherName() {
System.out.println("我的父亲叫张三");
return "张三";
}
}
package org.example;
public class App {
public static void main(String[] args) {
}
}
3.任何一个类,都能够知道这个类的所有属性和方法
下面我们对任何一个类,如何知道这个类的所有属性和方法,代码如下:
public class App {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName("org.example.Student");
//获得所有访问权限为public的属性
// Field[] fields = clazz.getFields();
//获取类中声明的所有属性
// Field[] fields = clazz.getDeclaredFields();
// for (Field field : fields) {
// System.out.println(field.getName());
// }
//获取所有访问权限为public的方法
// Method[] methods = clazz.getMethods();
//获取类中声明的所有方法
// Method[] methods = clazz.getDeclaredMethods();
// for (Method method : methods){
// System.out.println(method.getName());
// }
}
}
4.任何一个对象,都能够调用它的任意一个方法
package org.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class App {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName("org.example.Student");
//根据方法名或者其中一个方法
Method method = clazz.getDeclaredMethod("getFatherName");
//将该方法设置为可访问
method.setAccessible(true);
//获取无参构造器,然后通过构造器生成实例对象,这种方式可以使用无参或者有参的构造器
Constructor<?> constructor = clazz.getConstructor();
//生成对象
Object o = constructor.newInstance();
//直接生成无参的方式生成实例对象
// Object o = clazz.newInstance();
//方法调用
Object invoke = method.invoke(o);
System.out.println(invoke);
}
}
5.Class类对象
通过上面的两段代码我们看到,这其中最关键的就是要获取类的Class实例对象。每个类都有一个Class对象,用于在运行时提供或获得某个对象的类型信息,每当编译一个新类就产生一个Class对象。比如上面代码中创建一个Student类,那么JVM就会创建一个Student对应的Class类的Class对象,该Class对象保存了Student类相关的类型信息。
反射中生成Class对象的方式主要有以下三种:
- 使用Class.forName静态方法
Class<?> clazz = Class.forName("org.example.Student");
- 使用类的.class方法
Class clazz = Student.class;
- 使用实例对象的getClass对象
Student student = new Student();
Class clazz = student.getClass();
6.反射基本的API使用
6.1通过反射创建对象
- 通过Class的newInstance()方法创建对象
Class<?> clazz = Class.forName("org.example.Student");
//通过Class的newInstance()方法创建对象,这种方式其实是利用无参构造器创建的对象
Object o = clazz.newInstance();
- 通过Constructor的newInstance()方法创建对象,这种情况又可以分为无参和有参构造器
Class<?> clazz = Class.forName("org.example.Student");
//获取无参构造器
Constructor<?> constructor = clazz.getConstructor();
Object o = constructor.newInstance();
//获取有参的构造器
Constructor<?> constructor1 = clazz.getConstructor(String.class);
Object o1 = constructor1.newInstance("张二");
6.2通过反射获取类的构造器
Class<?> clazz = Class.forName("org.example.Student");
//获取所有公共的构造方法,即以public修饰的
Constructor[] constructors = clazz.getConstructors();
//获取所有公共的构造方法,不论修饰符是什么
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
//获取一个无参构造函数
Constructor<?> constructor = clazz.getConstructor();
//获取一个参数类型为String的构造函数
Constructor<?> constructor1 = clazz.getConstructor(String.class);
6.3通过反射获取类的成员变量
Class<?> clazz = Class.forName("org.example.Student");
//获取类的所有的公共属性,即以public来修饰的属性
Field[] fields = clazz.getFields();
//获取类的所有属性,不论以何种修饰符修饰的属性
Field[] declaredFields = clazz.getDeclaredFields();
//根据变量名,返回一个具体的具有public属性的成员变量
Field name = clazz.getField("name");
//根据变量名,返回一个具体的属性
Field name1 = clazz.getDeclaredField("name");