反射
反射:把Java类中的各种成分映射成单独的Java对象进行操作。
类的对象:基于某个类new出来的对象,也称为实例对象。
类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
反射的优缺点
- 优点:提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力允许程序创建和控制任何类的对象,无需提前硬编码目标类
- 缺点:反射降低了性能,代码维护难度增加
获取类对象
推荐使用forName获取类对象
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
// 1 通过对象获取
Student student = new Student("机器人", 21, "男");
Class<? extends Student> studentClass = student.getClass();
System.out.println(studentClass.hashCode());
// 2 类名.Class
System.out.println(Student.class.hashCode());
// 3 forName获取
Class<?> forName = Class.forName("com.robot.reflect.Student");
System.out.println(forName.hashCode());
// 获取父类对象
Class<?> superclass = studentClass.getSuperclass();
System.out.println(superclass);
}
}
获取父类对象
Class<?> superclass = studentClass.getSuperclass();
只要元素类型与维度一样时,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
/*
结果
1580066828
1580066828
*/
获取名字
public static void reflectOp2() throws Exception {
Class<?> aClass = Class.forName("com.robot.reflect.Student");
// 获取类的全限定名
System.out.println(aClass.getName());
// 获取类的简单名字
System.out.println(aClass.getSimpleName());
// 获取包名
System.out.println(aClass.getPackage().getName());
}
获取类构造方法
public static void getConstructor() throws Exception {
Class<?> aClass = Class.forName("com.robot.reflect.Student");
// 获取构造方法,并使用构造方法创建对象
Constructor<?> constructor;
constructor = aClass.getConstructor(String.class, int.class, String.class);
Student robot = (Student) constructor.newInstance("机器人1号", 20, "男");
// 获取所有构造方法
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor1 : constructors) {
System.out.println(constructor1);
}
// 直接创建对象
Student student1 = (Student) aClass.newInstance();
}
获取类方法
public static void getMethod() throws Exception {
Class<?> aClass = Class.forName("com.robot.reflect.Student");
// 获取方法: 本类以及继承的public方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 获取方法: 本类的所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
Student student = (Student) aClass.newInstance();
// 调用无参方法
Method method1 = aClass.getMethod("show");
method1.invoke(student);
// 调用有参方法
Method method2 = aClass.getMethod("show", String.class);
method2.invoke(student, "上海");
// 调用带返回值的方法
Method getName = aClass.getMethod("getName");
String result = (String) getName.invoke(student);
System.out.println(result);
// 调用静态方法
Method sleep = aClass.getMethod("sleep");
sleep.invoke(null);
// 调用私有方法
Method run = aClass.getDeclaredMethod("run");
run.setAccessible(true); // 设置访问权限
run.invoke(student);
}
一个通用方法,可以调用任意类的方法
传入参数:类对象,方法名,参数类型数组,参数
public static void invokeAny(Object obj, String methodName, Class<?>[] paramsType, Object... args) throws Exception {
Class<?> aClass = obj.getClass();
Method method = aClass.getDeclaredMethod(methodName, paramsType);
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(obj, args);
}
调用
invokeAny(student, "show", new Class<?>[]{String.class}, "北京");
invokeAny(student, "run", null);
获取类属性
public static void getFiled() throws Exception{
Class<?> aClass = Class.forName("com.robot.reflect.Student");
// 获取类的指定属性
Field name = aClass.getDeclaredField("name");
System.out.println(name);
// 获取类的所有public属性
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
// 获取类的所有属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
反射实现插件开发
创建接口
public interface CarService {
/**
* 汽车行驶
*/
void run();
/**
* 汽车转弯
*/
void turn(String direction);
}
创建2个实现类
public class BWM implements CarService{
@Override
public void run() {
System.out.println("宝马 run...");
}
@Override
public void turn(String direction) {
System.out.println("宝马 turn to " + direction);
}
}
public class Benz implements CarService{
@Override
public void run() {
System.out.println("奔驰 run...");
}
@Override
public void turn(String direction) {
System.out.println("奔驰 turn to " + direction);
}
}
创建一个文档,存储实现类的路径(cars.txt)
com.robot.reflect.BWM
com.robot.reflect.Benz
创建测试类
使用流的方式读取文件,并解析其中实现类的路径,然后使用反射创建对象
好处:可以只写一个测试类,就实现对任意实现类的测试(该类实现指定接口,并提供文档)
public class CarTest {
public static void main(String[] args) throws Exception {
FileReader fileReader = new FileReader("cars.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String data;
while ((data = bufferedReader.readLine()) != null) {
Class<?> aClass = Class.forName(data);
CarService car = (CarService) aClass.newInstance();
car.run();
car.turn("北");
}
fileReader.close();
}
}
注:本测试使用txt文档存储实现类路径,但实际开发中都是解析jar包
内省
采用反射机制实现对属性进行操作的一种机制。
- PropertyDescriptor:属性描述符,代表一个属性
- BeanInfo:实体类信息,包含类的信息
- Introspector:工具类
使用反射来访问其私有属性,并为其赋值
public class IntrospectorDemo {
public static void main(String[] args) throws Exception{
Class<?> class1 = Class.forName("com.robot.reflect.Student");
Student student = (Student) class1.newInstance();
// 获取类的属性
PropertyDescriptor pdName = new PropertyDescriptor("name", class1);
PropertyDescriptor pdAge = new PropertyDescriptor("age", class1);
PropertyDescriptor pdGender = new PropertyDescriptor("gender", class1);
// 获取属性类型
System.out.println(pdName.getPropertyType());
System.out.println(pdAge.getPropertyType());
System.out.println(pdGender.getPropertyType());
// 调用其Setter方法,为其赋值
Method setName = pdName.getWriteMethod();
setName.invoke(student, "Robot");
Method setAge = pdAge.getWriteMethod();
setAge.invoke(student, 21);
Method setGender = pdGender.getWriteMethod();
setGender.invoke(student, "男");
System.out.println(student);
// 调用其getter方法,获取属性值
Method getName = pdName.getReadMethod();
System.out.println(getName.invoke(student));
Method getAge = pdAge.getReadMethod();
System.out.println(getAge.invoke(student));
Method getGender = pdGender.getReadMethod();
System.out.println(getGender.invoke(student));
}
}