1.定义
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
2. 原理
反射机制(Reflection)是Java提供的一项较为高级的功能,它提供了一种动态功能,而此功能的体现在于通过反射机制相关的API就可以获取任何Java类的包括属性、方法、构造器、修饰符等信息。元素不必在JⅧ运行时进行确定,反射可以使得它们在运行时动态地进行创建或调用。反射技术在中间件领域应用得较多。
3. 反射相关的一些类
java.lang.Class
类和接口
java.lang.reflect.Field
类的成员变量
java.lang.reflect.Constructor
类的构造方法
java.lang.reflect.Method
类的方法
获取Class对象三种方式
//方式一 通过对象获取
Person person = new Person();
Class personClass1 = person.getClass();
//方式二 通过类名获取
Class personClass2 = Person.class;
//方式三 通过类名的全限定名字符串获取
Class personClass3 = Class.forName("com.d4c.reflection.demo.Person");
Class类
- 获得类相关的方法
方法 | 用途 |
---|---|
asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
- 获得类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
- 获得类中注解相关的方法
方法 | 用途 |
---|---|
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
- 获得类中构造器相关的方法
方法 | 用途 |
---|---|
getConstructor(Class...> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
- 获得类中方法相关的方法
方法 | 用途 |
---|---|
getMethod(String name, Class...> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
- 类中其他重要的方法
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
Field类
Field代表类的成员变量(成员变量也称为类的属性)。
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
Method类
Method代表类的方法。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) | 传递object对象及参数调用该对象对应的方法 |
Constructor类
Constructor代表类的构造方法。
方法 | 用途 |
---|---|
newInstance(Object... initargs) | 根据传递的参数创建类的对象 |
4. 优点
灵活性高。因为反射属于动态编译,即只有到运行时才动态创建 &获取对象实例。
编译方式说明:
静态编译:在编译时确定类型 & 绑定对象。如常见的使用new关键字创建对象
动态编译:运行时确定类型 & 绑定对象。动态编译体现了Java的灵活性、多态特性 & 降低类之间的藕合性
5. 缺点
执行效率低
因为反射的操作 主要通过JVM执行,所以时间成本会 高于 直接执行相同操作
因为接口的通用性,Java的invoke方法是传object和object[]数组的。基本类型参数需要装箱和拆箱,产生大量额外的对象和内存开销,频繁促发GC。
编译器难以对动态调用的代码提前做优化,比如方法内联。
反射需要按名检索类和方法,有一定的时间开销。
容易破坏类结构
因为反射操作饶过了源码,容易干扰类原有的内部逻辑#
6. 应用场景
动态获取 类文件结构信息(如变量、方法等) & 调用对象的方法
常用的需求场景有:动态代理、工厂模式优化、Java JDBC数据库操作等
7.访问权限问题
反射机制的默认行为受限于Java的访问控制,无法访问( private )私有的方法、字段。Java安全机制只允许查看任意对象有哪些域,而不允许读它们的值,若强制读取,将抛出异常。
解决方法
脱离Java程序中安全管理器的控制、屏蔽Java语言的访问检查,从而脱离访问控制
具体实现手段:使用Field类、Method类 & Constructor类对象的setAccessible()
void setAccessible(boolean flag)
作用:为反射对象设置可访问标志
规则:flag = true时 ,表示已屏蔽Java语言的访问检查,使得可以访问 & 修改对象的私有属性boolean isAccessible()
返回反射对象的可访问标志的值static void setAccessible(AccessibleObject[] array, boolean flag)
设置对象数组可访问标志
例子:
package com.d4c.reflection.demo;
import java.lang.reflect.*;
/**
* @Created with IntelliJ IDEA.
* @author: liangqing.zhao(zlq)
* @date: 2019/10/12 22:35
* @Description:
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//方式一 通过对象获取
Person person = new Person();
Class personClass1 = person.getClass();
//方式二 通过类名获取
Class personClass2 = Person.class;
//方式三 通过类名的全限定名字符串获取
Class personClass3 = Class.forName("com.d4c.reflection.demo.Person");
Class studentClass = Class.forName("com.d4c.reflection.demo.Student");
Student student = new Student("zlq");
//getFields 能够获取public修饰的field和父类中public修饰的field
Field[] fields = studentClass.getFields();
for (Field field : fields) {
System.out.println("field.getName() = " + field.getName());
}
System.out.println("--------------------------------------");
//getDeclaredFields 能够获取类中所有的field,但父类中的field获取不了
Field[] declaredfields = studentClass.getDeclaredFields();
for (Field declaredfield : declaredfields) {
System.out.println("declaredfield.getName() = " + declaredfield.getName());
}
System.out.println("--------------------------------------");
Field age = personClass3.getDeclaredField("age");
//为反射对象设置可访问标志,使得可以访问 & 修改对象的私有属性
age.setAccessible(true);
System.out.println("before age:" + age.get(person));
age.set(person, 100);
System.out.println("after age:" + age.get(person));
System.out.println("--------------------------------------");
Field[] declaredFields = studentClass.getDeclaredFields();
//批量给访问权限
AccessibleObject.setAccessible(declaredFields, true);
for (Field declaredField : declaredFields) {
System.out.println("declaredField.get(student) = " + declaredField.get(student));
}
}
}
8 利用反射获取和设置值
- 利用反射获取类的属性 & 赋值
package com.d4c.reflection.demo;
import java.lang.reflect.Field;
/**
* @Created with IntelliJ IDEA.
* @author: liangqing.zhao(zlq)
* @date: 2019/10/12 22:20
* @Description:
*/
public class PersonDemo {
public static void main(String[] args) throws Exception{
// 获取Person类的Class对象
Class personClass = Person.class;
// 通过Class对象创建Person类的对象
Object person = personClass.newInstance();
// 通过Class对象获取Person类的nage属性
Field field = personClass.getDeclaredField("age");
// 设置私有访问权限
field.setAccessible(true);
// 对新创建的person对象设置age值
field.set(person, 99);
// 获取新创建person对象的的age属性 & 输出
System.out.println(field.get(person));
}
}
- 利用反射调用类的构造函数
// 获取Person类的Class对象
Class personClass = Person.class;
//通过Class对象获取Constructor类对象,从而调用无参构造方法
//构造函数的调用实际上是在newInstance(),而不是在getConstructor()中调用
Object obj1 = personClass.getConstructor().newInstance();
//通过Class对象获取Constructor类对象(传入参数类型),从而调用有参构造方法
Object obj2 = personClass.getConstructor(String.class).newInstance("Carson");
Object obj3 = personClass.getConstructor(Integer.class, String.class, String.class).newInstance(99, "zlq", "hello world");
- 利用反射调用类对象的方法
// 获取Person类的Class对象
Class personClass3 = Person.class;
// 通过Class对象创建Person类的对象
Object person3 = personClass3.newInstance();
// 通过Class对象获取方法getSay的Method对象:需传入方法名
Method getSayMethod = personClass3.getMethod("getSay");
// 通过Method对象调用getSay():需传入创建的实例
getSayMethod.invoke(person3);
//通过Class对象获取方法setSay 的Method对象:需传入方法名 & 参数类型
Method setSayMethod = personClass3.getMethod("setSay",String.class);
// 通过Method对象调用setSay 需传入创建的实例 & 参数值
setSayMethod.invoke(person3,"oop");
参考文献
认识反射机制(Reflection)
Java 反射机制
一个例子让你了解Java反射机制
Java反射机制的原理及在Android下的简单应用
java中的反射机制
Android注解与反射机制
java.lang.reflect.Method
Java反射:这是一份全面 & 详细的 Java反射机制 学习指南
Java高级特性——反射
Java反射完全解析
Java反射机制的适用场景及其利与弊
Java各种反射性能对比
深入解析Java反射
Java编程中基础反射详细解析
Java基础篇:反射机制详解