Java反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意对象都能调用他的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制。
Java反射机制主要用于编写一些通用性较高的代码或框架时使用。
当编写好的Java文件进行编译形成class文件,再将class文件放入JVM中通过ClassLoad(类的加载器)加载运行称为运行状态。因此我们可以在JVM中获取一个类的对象(Class)、类的构造方法(Constructor)、类的属性(Field)、类的方法(Method)。
首先创建一个实体类Employee.java
package com.reflect.entity;
public class Employee {
private String name;
private Integer age;
public String desc;
public Employee() {
System.out.println("创建无参构造函数!");
}
public Employee(String name, Integer age, String desc) {
this.name = name;
this.age = age;
this.desc = desc;
System.out.println("创建有参构造函数!");
}
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 getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", desc='" + desc + '\'' +
'}';
}
public void work() {
System.out.println("employee work");
}
private String talk(String name) {
return "Hello " + name;
}
}
Class
获取Class对象:
- Object.class
- object.getClass()
- Class.forName(String objectUrl)
package com.reflect;
import com.reflect.entity.Employee;
import org.junit.Test;
public class ReflectTest {
@Test
public void getClazz() {
// 1. .class获取类
Class clazz = Employee.class;
// 2. 调用getClass()方法
Employee employee = new Employee();
employee.getClass();
// 3. 调用Class的forName()方法
try {
Class clazz1 = Class.forName("com.reflect.entity.Employee");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
因为第1种和第2种方法都会使用被调用的类,所以一般情况下使用第三种方法调用forName()函数
Construction
在获取了类的Class对象后,可以调用getConstructor(Class<?>... parameterTypes)
来获取类的构造方法。如果不填写参数,则会获取类的无参构造方法,填写对应个数的参数的Class类,可以获取不同的构造方法。
Class类还提供了getConstructors()
方法,返回Constructor数组用来存储类的所有构造方法,再去遍历获取对应的构造方法。
调用newInstance()
方法创建对象
示例:
package com.reflect;
import com.reflect.entity.Employee;
import org.junit.Test;
public class ReflectTest {
@Test
public void getConstruction() {
try {
Class clazz = Class.forName("com.reflect.entity.Employee");
// 获取无参构造方法
Constructor constructor = clazz.getConstructor();
// 使用构造方法创建对象
Employee employee = (Employee) constructor.newInstance();
// 获取有参构造方法
Constructor constructor1 = clazz.getConstructor(String.class, Integer.class, String.class);
// 使用构造方法创建对象
Employee employee1 = (Employee) constructor1.newInstance("张三", 30, "开发工程师");
System.out.println(employee1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Field
Class类提供了几种方法用于获取类的属性
- getFields() :获取所有的公有属性,返回一个Field类型的集合
- getDeclaredFields() :获取所有的私有属性,返回一个Field类型的集合
- getField(String arg0) :获取指公有定属性名的属性,返回一个Field类型
- getDeclaredField(String arg0) :获取指定私有属性名的属性,返回一个Field类型
获取的类的属性包括继承自父类的属性
获取了类的属性之后,Field类提供了两个方法设置和获取属性的值
- get(Object obj) :获取类的属性值
- set(Object obj, String val) :设置类的属性值
需要注意的是,私有属性需要先获取访问权限,调用Field类的
setAccessible(Bolean boolean)
,设置参数为true将其设置为允许访问状态,只有设置了访问权限才能对该属性进行赋值
package com.reflect;
import com.reflect.entity.Employee;
import org.junit.Test;
public class ReflectTest {
@Test
public void getField() throws Exception {
Class clazz = Class.forName("com.reflect.entity.Employee");
Constructor constructor = clazz.getConstructor(String.class, Integer.class, String.class);
Employee e = (Employee) constructor.newInstance("张三", 30, "开发工程师");
// 获取私有属性name
Field fieldName = clazz.getDeclaredField("name");
// 获取公有属性desc
Field fieldDesc = clazz.getField("desc");
// 设置私有属性的访问权限
fieldName.setAccessible(true);
// 获取属性值
String name = (String) fieldName.get(e);
String desc = (String) fieldDesc.get(e);
System.out.println("name:" + name + ";desc:" + desc);
// 修改属性值
fieldName.set(e, "李四");
fieldDesc.set(e, "销售专员");
System.out.println("==============================");
// 再次获取属性值
String name1 = (String) fieldName.get(e);
String desc1 = (String) fieldDesc.get(e);
System.out.println("name:" + name1 + ";desc:" + desc1);
}
}
如果没有设置私有属性的访问权限就直接为属性获取和赋值会直接报错
Method
与类的属性类似,Class类提供了几种方式获取类的方法,也会分为私有和公有的获取方法
- getMethods() :获取类的所有公有方法,返回为Method类的集合
- getDeclaredMethods() :获取类的所有私有方法,返回Method类的集合
- getMethod(String name, Class<?>… parameterTypes) :获取指定的公有方法,参数中name为方法的名称,parameterTypes为参数的数据类型
- getDeclaredMethod(String name, Class<?>… parameterTypes) :获取指定的私有方法,参数中name为方法的名称,parameterTypes为参数的数据类型
获取了Method类的对象后,调用invoke(Object obj, Object… arg0)方法对方法进行调用,参数obj为要执行的类的对象,arg0为方法的参数
需要注意的是,私有方法需要先获取访问权限,调用Method类的
setAccessible(Bolean boolean)
,设置参数为true将其设置为允许访问状态,只有设置了访问权限才能调用该方法
package com.reflect;
import com.reflect.entity.Employee;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest {
@Test
public void getMethod() throws Exception {
Class clazz = Class.forName("com.reflect.entity.Employee");
Constructor constructor = clazz.getConstructor(String.class, Integer.class, String.class);
Employee e = (Employee) constructor.newInstance("张三", 30, "开发工程师");
// 获取方法
Method work = clazz.getMethod("work");
Method talk = clazz.getDeclaredMethod("talk", String.class);
// 获取方法的访问权限
talk.setAccessible(true);
// 调用方法
work.invoke(e);
String result = (String) talk.invoke(e, "Tom");
System.out.println(result);
}
}