反射被视为动态语言的关键。Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。
反射的定义
在Java中反射(Reflection)是一种Java 程序运行期间的动态技术,它可以在运行时(runtime)检査、修改其自身结构或行为。通过反射,程序可以访问、检测和修改它自己的类、对象、方法、属性等成员。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
反射的作用
- 动态加载类:程序可以在运行时动态地加载类库中的类;
- 动态创建对象:反射可以基于类的信息,程序可以在运行时,动态创建对象实例;
- 调用方法:反射可以根据方法名称,程序可以在运行时,动态地调用对象的方法(即使方法在编写程序时还没有定义)
- 访问成员变量:反射可以根据成员变量名称,程序可以在运行时,访问和修改成员变量(反射可以访问私有成员变量)
- 运行时类型信息:反射允许程序在运行时,查询对象的类型信息,这对于编写通用的代码和库非常有用;
实现反射机制的类
Java中主要由以下的类来实现Java反射机制(这些类都位于java.lang.reflect包中):
- Class类:代表一个类。 Field类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
反射的基础使用
获取Class对象
反射的第一步是获取 Class 对象(动态加载类)。Class 对象表示某个类的元数据,可以通过以下几种方式获取:
1 2 3 4 5 6 7 8
| Class stringClass1 = String.class;
Class stringClass2 = Class.forName("java.lang.String");
Class stringClass3 = "".getClass();
|
解析字符串为Java对象
使用fastjson
的JSON
类解析一个字符串为Java对象,并输出该对象的字段值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.alibaba.fastjson.JSON; import lombok.Data;
public class ReflectionTest { public static void main(String[] args) { String demoStr = "{\"age\":18,\"name\":\"demo1\"}"; Demo demo = JSON.parseObject(demoStr, Demo.class); System.out.printf("name is : %s%n", demo.getName()); System.out.printf("age is : %s%n", demo.getAge()); } }
@Data class Demo { private String name; private Integer age; }
|
输出
1 2
| name is : demo1 age is : 18
|
获取类的相关信息
通过 Class 对象在运行时获取一个类的相关信息,包括类名、包名、成员变量(字段)、成员方法等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.lang.reflect.*;
public class ReflectionTest2 { public static void main(String[] args) throws ClassNotFoundException { Class clz = Class.forName("java.util.HashMap");
System.out.println("完全限定名:" + clz.getName()); System.out.println("简单的类名:" + clz.getSimpleName());
System.out.println("package" + clz.getPackage().getName()); System.out.println();
Field[] fieldArray = clz.getDeclaredFields(); System.out.println("成员变量(字段)"); for (Field field : fieldArray) { System.out.println(field); } System.out.println();
Method[] methodArray = clz.getDeclaredMethods(); System.out.println("成员方法"); for (Method method : methodArray) { System.out.println(method); } } }
|
通过反射创建对象
通过反身创建对象,通过反射获取方法,通过反射执行方法等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import lombok.SneakyThrows; import java.lang.reflect.Method;
public class ReflectionTest3 { @SneakyThrows public static void main(String[] args) { Class<Demo> clazz = Demo.class; Method setName = clazz.getMethod("setName", String.class); Demo demo = clazz.newInstance(); setName.invoke(demo, "new Name"); System.out.printf("demo name is : %s%n", demo.getName()); Method getName = clazz.getMethod("getName"); System.out.printf("demo reflect getname is : %s%n", getName.invoke(demo));
Demo demo2 = new Demo(); demo2.setName("demo2"); System.out.printf("demo2 reflect getname is : %s%n", getName.invoke(demo2)); } }
|
输出
1 2 3
| demo name is : new Name demo reflect getname is : new Name demo2 reflect getname is : demo2
|
反射的性能问题
反射虽然功能强大,但由于是在运行时动态操作类,因此性能相对较低。此外,反射也会破坏封装性,使用时要谨慎。
反射的安全性
使用反射时需要注意安全问题,因为它可以绕过Java的访问控制机制。例如,可以访问私有字段或方法,因此在开发中使用反射要特别小心。甚至在运行时,添加、改变方法。
反射的常见场景
- 框架开发:如 Spring 中的依赖注入、Hibernate 中的 ORM 等。
- 调试工具:如 Java 的调试器、分析工具等。
- 动态代理:在 Java 中,动态代理依赖于反射。
- 常用的fastjson
JSONObject.parseObject(xxStr)