概要
Java 反射是造很多轮子必要的技能,比如依赖注入,Json和类的实例的转换等等。本文将介绍Java反射的一些常用类和API
Java 反射常用类
- Class
包含了类T的所有信息,Java的每个类都有一个Class实例,比如你的项目有一个Student.java 文件,首先你需要把.java文件通过编译器(javac)编译成.class文件,JVM读取Student.class文件会生成一个Class 的实例,如果你需要new一个Studen实例时,JVM就会使用Class 实例里的信息创造一个Student实例,如果不存在Class 实例,就会去classpath找Student.class把它加载进来,如果找不到就会报错。 - Constructor 构造器类,包含构造器方法信息,可以使用它构造一个实例
- Field 属性类
- Method 方法类
- Annotation 注解类
- 一个共性: 不Declared的方法只返回public访问权限的部分,包含Declared的方法返回所有访问权限
Class 类
反射通常都是从Class类开始的,所以我们着重讲Class类
先创建一个项目,导入一些工具包方便我们写代码,依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
- 编写一个Student类
// Student.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
public static String grade = "一年级";
int id;
double score;
private String name;
private int age;
private String phone;
}
- 获取Student类的Class实例
// 直接.class
Class<?> stuClass1 = Student.class;
// 使用ClassLoader
Class<?> stuClass2 = ClassLoader.getSystemClassLoader().loadClass("com.yeyeck.Student");
// 通过一个Student实例
Student student = new Student();
Class<?> stuClass3 = student.getClass();
log.info("类名(包含包名): {}", stuClass1.getName());
log.info("类名(不包含包名):{}", stuClass1.getSimpleName());
// 15:58:55.946 [main] INFO com.yeyeck.ClassTest - 类名(包含包名): com.yeyeck.Student
// 15:58:55.946 [main] INFO com.yeyeck.ClassTest - 类名(不包含包名):Student
- 获取Student的属性
log.info("所有属性:");
Field[] fields = stuClass1.getDeclaredFields();
for(Field field: fields) {
log.info(field.toString());
}
log.info("public属性");
Field[] fields2 = stuClass1.getFields();
for (Field field : fields2) {
log.info(field.toString());
}
// 15:58:55.955 [main] INFO com.yeyeck.ClassTest - 所有属性:
// 15:58:55.955 [main] INFO com.yeyeck.ClassTest - public static java.lang.String com.yeyeck.Student.grade
// 15:58:55.966 [main] INFO com.yeyeck.ClassTest - int com.yeyeck.Student.id
// 15:58:55.967 [main] INFO com.yeyeck.ClassTest - double com.yeyeck.Student.score
// 15:58:55.967 [main] INFO com.yeyeck.ClassTest - private java.lang.String com.yeyeck.Student.name
// 15:58:55.968 [main] INFO com.yeyeck.ClassTest - private int com.yeyeck.Student.age
// 15:58:55.968 [main] INFO com.yeyeck.ClassTest - private java.lang.String com.yeyeck.Student.phone
// 15:58:55.968 [main] INFO com.yeyeck.ClassTest - public属性
// 15:58:55.969 [main] INFO com.yeyeck.ClassTest - public static java.lang.String com.yeyeck.Student.grade
- 获取Student的方法
Method[] methods = stuClass1.getMethods();
log.info("方法:");
for (Method method: methods) {
log.info(method.toString());
}
15:58:55.970 [main] INFO com.yeyeck.ClassTest - 方法:
// 15:58:55.980 [main] INFO com.yeyeck.ClassTest - public boolean com.yeyeck.Student.equals(java.lang.Object)
// 15:58:55.981 [main] INFO com.yeyeck.ClassTest - public java.lang.String com.yeyeck.Student.toString()
// 15:58:55.982 [main] INFO com.yeyeck.ClassTest - public int com.yeyeck.Student.hashCode()
// 15:58:55.982 [main] INFO com.yeyeck.ClassTest - public java.lang.String com.yeyeck.Student.getName()
// 15:58:55.983 [main] INFO com.yeyeck.ClassTest - public void com.yeyeck.Student.setName(java.lang.String)
// 15:58:55.983 [main] INFO com.yeyeck.ClassTest - public int com.yeyeck.Student.getId()
// 15:58:55.984 [main] INFO com.yeyeck.ClassTest - public void com.yeyeck.Student.setId(int)
// 15:58:55.984 [main] INFO com.yeyeck.ClassTest - public void com.yeyeck.Student.setScore(double)
// 15:58:55.985 [main] INFO com.yeyeck.ClassTest - public int com.yeyeck.Student.getAge()
// 15:58:55.985 [main] INFO com.yeyeck.ClassTest - public double com.yeyeck.Student.getScore()
// 15:58:55.985 [main] INFO com.yeyeck.ClassTest - public void com.yeyeck.Student.setPhone(java.lang.String)
// 15:58:55.986 [main] INFO com.yeyeck.ClassTest - public java.lang.String com.yeyeck.Student.getPhone()
// 15:58:55.986 [main] INFO com.yeyeck.ClassTest - public void com.yeyeck.Student.setAge(int)
// 15:58:55.986 [main] INFO com.yeyeck.ClassTest - public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
// 15:58:55.986 [main] INFO com.yeyeck.ClassTest - public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
// 15:58:55.987 [main] INFO com.yeyeck.ClassTest - public final void java.lang.Object.wait() throws java.lang.InterruptedException
// 15:58:55.999 [main] INFO com.yeyeck.ClassTest - public final native java.lang.Class java.lang.Object.getClass()
// 15:58:56.000 [main] INFO com.yeyeck.ClassTest - public final native void java.lang.Object.notify()
// 15:58:56.000 [main] INFO com.yeyeck.ClassTest - public final native void java.lang.Object.notifyAll()
- 获取Student的构造器
log.info("构造器:");
Constructor[] constructors = stuClass1.getConstructors();
for (Constructor constructor : constructors) {
log.info(constructor.toString());
}
// 使用默认构造器初始化一个实例
Constructor constructor = stuClass1.getDeclaredConstructor();
Object obj = constructor.newInstance();
log.info(obj.toString());
// 15:58:56.001 [main] INFO com.yeyeck.ClassTest - 构造器:
// 15:58:56.001 [main] INFO com.yeyeck.ClassTest - public com.yeyeck.Student()
// 15:58:56.001 [main] INFO com.yeyeck.ClassTest - public com.yeyeck.Student(int,double,java.lang.String,int,java.lang.String)
// 15:58:56.002 [main] INFO com.yeyeck.ClassTest - Student(id=0, score=0.0, name=null, age=0, phone=null)
- 获取Student类的注解
Annotation[] annotations = stuClass1.getAnnotations();
Constructor类
- 带Declared的方法会获取所有声明的构造器,不带Declared的方法只会获取public修饰的构造器,我们先给Student添加一个private的构造器,可以看到两个方法的区别
Class<?> stuClass = Student.class;
log.info("非Declared: ");
Constructor[] constructors1 = stuClass.getConstructors();
for (Constructor constructor : constructors1) {
log.info(constructor.toString());
}
log.info("Declared");
Constructor[] constructors2 = stuClass.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
log.info(constructor.toString());
}
// 16:26:52.959 [main] INFO com.yeyeck.AppTest - 非Declared:
// 16:26:52.961 [main] INFO com.yeyeck.AppTest - public com.yeyeck.Student(int,double,java.lang.String,int,java.lang.String)
// 16:26:52.961 [main] INFO com.yeyeck.AppTest - public com.yeyeck.Student()
// 16:26:52.961 [main] INFO com.yeyeck.AppTest - Declared
// 16:26:52.961 [main] INFO com.yeyeck.AppTest - public com.yeyeck.Student(int,double,java.lang.String,int,java.lang.String)
// 16:26:52.961 [main] INFO com.yeyeck.AppTest - private com.yeyeck.Student(java.lang.String,int,java.lang.String)
// 16:26:52.961 [main] INFO com.yeyeck.AppTest - public com.yeyeck.Student()
- 根据参数获取构造器
// 获取无参构造器并初始化一个实例
Constructor noArgsConstructor = stuClass.getConstructor();
Object stu1 = noArgsConstructor.newInstance();
log.info(stu1.toString());
// 获取有参构造器并初始化一个实例
Constructor withArgsConstructor = stuClass.getConstructor(int.class, double.class, String.class, int.class, String.class);
Object stu2 = withArgsConstructor.newInstance(1, 90.0, "张三", 20, "133333333333");
log.info(stu2.toString());
// 16:41:08.301 [main] INFO com.yeyeck.AppTest - Student(id=0, score=0.0, name=null, age=0, phone=null)
// 16:41:08.304 [main] INFO com.yeyeck.AppTest - Student(id=1, score=90.0, name=张三, age=20, phone=133333333333)
Field类
- 根据名称获取属性
Class<?> stuClass = Student.class;
// 根据名称获取一个public属性
Field field1 = stuClass.getField("grade");
log.info(field1.toString());
// 根据名称获取一个非public属性
Field field2 = stuClass.getDeclaredField("name");
log.info(field2.toString());
log.info(field2.getName()); // 访问属性的名称
log.info(field2.getType().toString()); // 访问属性的类型
// 16:53:04.366 [main] INFO com.yeyeck.FieldTest - public static java.lang.String com.yeyeck.Student.grade
// 16:53:04.366 [main] INFO com.yeyeck.FieldTest - private java.lang.String com.yeyeck.Student.name
// 16:53:04.374 [main] INFO com.yeyeck.FieldTest - name
// 16:53:04.375 [main] INFO com.yeyeck.FieldTest - class java.lang.String
- 访问一个对象的属性值
Field field2 = stuClass.getDeclaredField("name");
Object instance = Student.class.getDeclaredConstructor().newInstance();
log.info("name是否可以访问: {}", field2.canAccess(instance)); // name是private属性不允许访问
// 强制访问private属性
field2.setAccessible(true);
field2.set(instance, "张三");
log.info(instance.toString());
// 17:01:36.639 [main] INFO com.yeyeck.FieldTest - name是否可以访问: false
// 17:01:36.643 [main] INFO com.yeyeck.FieldTest - Student(id=0, score=0.0, name=张三, age=0, phone=null)
Method 类
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MethodTest {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<?> stuClass = Student.class;
// 根据方法名获取一个无参方法
Method methodGetName = stuClass.getMethod("getName");
// 查看返回类型
log.info(methodGetName.getReturnType().toString());
// 调用该方法,调用的时候需要一个Student实例
Object instance = Student.class.getConstructor().newInstance();
Object res = methodGetName.invoke(instance);
log.info("getName返回: {}", String.valueOf(res));
// 根据方法名和参数获取一个有参方法
Method methodSetName = stuClass.getMethod("setName", String.class);
// 调用该方法
methodSetName.invoke(instance, "张三");
// 查看效果
log.info("name: {}", String.valueOf(methodGetName.invoke(instance)));
}
}