1. 反射
-
通过程序来访问任意类的能力
Provides classes and interfaces for obtaining reflective information about classes and objects. - 反射是程序运行时获取自身信息的机制,使用反射可以获得一个完整的类结构
1.1 反射应用场景
- copy DAO Entity -> PO
- Spring IOC & DI
- 监控APM --> class file --> Class --> Object --> 调用监控方法
1.2 优缺点
- 灵活,运行时可以做动态的改变
- 性能损耗分析
- 反射调用会进行一系列的安全性校验
- 反射需要调用一系列的 native 方法
- 寻找字节码的过程,比如通过 ClassName 找到对应的字节码Class,然后进行加载、解析,也会比较慢,而 new 的方式无需寻找,因为在 Linking的解析阶段已经将符号引用转为了直接引用
- 入参校验
2. 反射相关的类
- java.lang.Class
- java.lang.reflect.Field
- java.lang.reflect.Method
- java.lang.reflect.Constructor
- Class, Method, Field 都实现了 AnnotatedElement 接口
3. 示例
反射技术:就是一套用来描述类的API
一个类被加载后,类的整个结构都被封装在 Class 对象中
// this.getClass().getClassLoader()
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> clazz = classLoader.loadClass("com.example.pojo.Person");
3.1 反射获取属性
不能直接操作私有属性,需关闭程序的安全检测 filed.setAccessible(true);
/**
* all the accessible public fields
* including all its superclasses, all its superinterfaces
*/
Field[] fields = clazz.getFields();
/**
* This includes public, protected, default (package) access, and private fields
* but excludes inherited fields
*/
Field[] declaredFields = clazz.getDeclaredFields();
Class<?> clazz = Student.class;
Student student = (Student) clazz.newInstance();
Field filed = clazz.getDeclaredField("age");
// 允许访问私有属性
filed.setAccessible(true);
filed.set(student, 20);
3.2 反射获取方法
/**
* all the public methods
* including inherited from superclasses and superinterfaces.
*/
Method[] methods = clazz.getMethods();
/**
* all the declared methods, including public, protected, default (package) access, and private methods,
* but excluding inherited methods.
*/
Method[] declaredMethods = clazz.getDeclaredMethods();
// demo
Method[] methods = clazz.getMethods();
for (Method method : methods) {
// 打印看看
System.out.println(method.toString());
}
// java.lang.reflect.Method#invoke(对象,"方法的参数值")
public Object invoke(Object obj, Object... args) {
}
反射获取方法,要加参数,因为方法有重载
public static void main(String[] args) throws Exception {
Class<?> clazz = Student.class;
Student student = (Student) clazz.newInstance();
Method publicMethod = clazz.getMethod("setName", String.class);
publicMethod.invoke(student, "boy");
/**
* with modifiers "private"
* 调用私有方法要设置访问权限
*/
Method privateMethod = clazz.getDeclaredMethod("test");
privateMethod.setAccessible(true);
privateMethod.invoke(student);
// 调用静态方法不需要传对象,直接写 null
Method staticMethod = clazz.getDeclaredMethod("demo", String.class);
staticMethod.setAccessible(true);
staticMethod.invoke(null, "boy");
}
3.3 获取构造方法
public class Student extends Person implements Serializable {
private String lesson;
public Student() {}
public Student(String lesson) {
this.lesson = lesson;
}
@Override
public String toString() {
return "Student [lesson=" + lesson +"]";
}
}
public static void main(String[] args) throws Exception {
Class<?> clazz = Student.class;
Constructor<?>[] constructors = clazz.getConstructors();
System.out.println(Arrays.toString(constructors));
Constructor<?> constructor = clazz.getConstructor();
// Constructor.newInstance(Object ... initargs) 不传参数表示调用无参构造方法
Student student = (Student) constructor.newInstance();
System.out.println(student.toString());
Constructor<?> constructor2 = clazz.getConstructor(String.class);
Student student2 = (Student) constructor2.newInstance("English");
System.out.println(student2.toString());
}
3.4 操作对象实例
一旦某个类型的Class对象已经被加载到内存,就可以用它来产生该类型的所有对象
// ----------- 静态方法和非静态方法调用是不一样的 ------------
// main() 静态方法
Method staticMethod = clazz.getMethod("main", String[].class);
staticMethod.invoke(null, (Object) new String[] {});
/**
* 非静态方法的调用要有一个类实例
* newInstance() 不传参数时,调用类中缺省的构造方法(本质是调用类的无参构造器)
*/
User user = (User) clazz.newInstance();
// User user = clazz.getDeclaredConstructor().newInstance();
Constructor<User> constructor = clazz.getDeclaredConstructor(int.class, String.class);
User user2 = constructor.newInstance(20, "xiao");
动态语言与静态语言
- Java, C, C++ 都是静态语言
- 通过反射可以获得类似动态语言的特性
其他
JackSON 如何序列化和反序列化的
Junit 如何找到 加了 @Test, @After, @Before 注解的方法, 并自动编排这些方法,完成整个单元测试流程的
网友评论