Java中的反射(Reflection)是一种强大的工具,它允许在运行时检查和修改程序的行为。反射机制使得Java程序能够在运行时分析类、接口、字段和方法的信息。这种能力在实际应用中非常有用,例如开发框架、工具、插件等。
通过反射,Java代码可以获取类的所有信息(如名称、成员变量、成员方法、构造函数等),并且可以动态地创建对象、调用方法和改变字段值。这使得开发人员可以在不知道具体类名称的情况下,编写出能够操作这些类的代码。
Java反射应用场景的逐一举例:
-
框架设计:
在框架设计中,反射被广泛用于实现依赖注入和控制反转。例如,在Spring框架中,可以使用反射来创建和管理bean。Spring通过反射扫描类路径,自动识别并注册bean,并根据配置文件或注解来注入依赖。这样,开发人员就可以专注于编写业务逻辑,而不用手动管理对象的创建和依赖关系。 -
插件可扩展性:
插件可扩展性是通过反射实现动态加载和扩展功能的一种方式。例如,开发一个扩展性强的应用程序时,可以使用反射来加载插件JAR包中的类,并通过反射调用插件提供的方法。这样,其他开发人员可以编写符合一定规范的插件,并将其集成到应用程序中,以扩展应用程序的功能,而无需修改源代码。 -
单元测试:
在单元测试中,反射可用于访问和操作私有字段和方法,以便进行测试。例如,使用反射可以修改一个私有字段的值,以便测试一个特定条件下的代码分支。或者,可以使用反射调用一个私有方法,以确保该方法的行为符合预期。这样的测试能够更全面地覆盖代码,并提高代码质量。 -
序列化与反序列化:
在对象序列化和反序列化的过程中,反射起着重要作用。例如,使用Jackson库将Java对象转换为JSON字符串时,Jackson会利用反射获取对象的类信息,并将其转换为相应的JSON表示。同样地,在将JSON字符串反序列化为Java对象时,Jackson会使用反射根据JSON数据的结构和类型信息创建相应的对象实例,并设置其属性值。
这些举例展示了Java反射在不同应用场景中的使用方式。通过反射,Java能够在运行时动态地获取和操作类、方法、字段等信息,从而实现更加灵活、可扩展和可测试的代码。
反射的优点:
- 灵活性:反射提高了Java的灵活性,可以在运行时动态地获取类的信息并调用其方法,而无需硬编码。
- 可扩展性:通过反射可以加载外部的类和方法,增强了代码的可扩展性。
反射的缺点:
- 性能开销:反射涉及动态解析类的信息,这在性能上比直接的Java代码要慢得多。
- 安全性问题:由于反射可以访问私有方法和字段,它可能会破坏封装性,导致代码的安全性问题。
- 代码可读性:大量使用反射会使得代码难以理解和维护,因为它违反了面向对象编程的一些基本原则。
- 依赖问题:过度使用反射可能导致紧密的依赖关系,使得代码难以测试和复用。
因此,在使用反射时,需要权衡其灵活性和带来的性能、安全性问题。尽量避免在性能敏感的场合下大量使用反射,同时要注意保证代码的安全性和可读性。
//反射的几大作用
//1. 识别注解 最重要(框架的底层)
//2.1. 获取类的结构
//2.2. 创建对象
//2.2 调用有参构造方法
//2.3. 调用对象的方法
//2.4. 设置对象的属性值 (重要)
//3. 调用类加载(调用类的静态构造块) 很少使用
JDBC面向接口编程,不面向实现类
public static void main(String[] args) {
//1. 重要反射用法, 获取类上的标记的注解
//获取某个类
//类对象
Class<JobService> jobServiceClass = JobService.class;
//获取JobService类上所有的标注的注解
Annotation[] annotations = jobServiceClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("annotation = " + annotation);
//注解对象
Class<? extends Annotation> annotationType = annotation.annotationType();
System.out.println(annotationType == Service.class);
}
}
annotation = @org.springframework.stereotype.Service(value=)
true
2.重要反射用法, 获取属性上的标记的注解
首先需要确保类中存在属性
@Service
public class JobService {
@Autowired
private UserService userService;
//2. 重要反射用法, 获取属性上的标记的注解
// 获取类 -> 获取类的属性 -> 获取属性上的注解
Class<JobService> jobServiceClass = JobService.class;
Field[] declaredFields = jobServiceClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("declaredField.getName() = " + declaredField.getName());
}
declaredField.getName() = userService
重要反射用法, 获取属性上的标记的注解
获取类 -> 获取类的属性 -> 获取属性上的注解
//2. 重要反射用法, 获取属性上的标记的注解
// 获取类 -> 获取类的属性 -> 获取属性上的注解
Class<JobService> jobServiceClass = JobService.class;
Field[] declaredFields = jobServiceClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("declaredField.getName() = " + declaredField.getName());
//获得当前属性上标记的所有注解
Annotation[] annotations = declaredField.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType() == Autowired.class);
}
}
declaredField.getName() = userService
true
此处也可以直接用官方的isAnnotationPresent()
方法
//2. 重要反射用法, 获取属性上的标记的注解
// 获取类 -> 获取类的属性 -> 获取属性上的注解
Class<JobService> jobServiceClass = JobService.class;
Field[] declaredFields = jobServiceClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("declaredField.getName() = " + declaredField.getName());
System.out.println("该方法是否被Autowired注解标记:" + declaredField.isAnnotationPresent(Autowired.class));
}
- 重要反射用法, 获取方法上的标记的注解
获取类 -> 获取类的方法 -> 获取方法上的注解
//注解的生命周期, 必须是runtime, 表示运行的时候可以被其他人识别
@Retention(RetentionPolicy.RUNTIME)
public @interface BiaoJi {
}
//3. 重要反射用法, 获取方法上的标记的注解
// 获取类 -> 获取类的方法 -> 获取方法上的注解
Class<JobService> jobServiceClass = JobService.class;
Method[] declaredMethods = jobServiceClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
//获取当前方法上标记的注解
System.out.println("当前方法名称" + declaredMethod.getName());
System.out.println("该方法是否被BiaoJi注解标记:" + declaredMethod.isAnnotationPresent(BiaoJi.class));
}
当前方法名称addJob
该方法是否被BiaoJi注解标记:false
当前方法名称countJob
该方法是否被BiaoJi注解标记:true
//1. 无参构造
扫描JobService.class这个类, 如果有@Service, 则创建对象并置入到hashmap(ioc)
//spring ioc 如何做的
//扫描JobService.class这个类, 如果有@Service, 则创建对象并置入到hashmap(ioc)
//1. 无参构造
HashMap<String, Object> ioc = new HashMap<>();
Class aClass = JobService.class;
if (aClass.isAnnotationPresent(Service.class)){
//根据当前类对象, 创建一个类的对象
Object ob = aClass.newInstance();
//根据name得到小驼峰命名然后放到hashmap中
String name = aClass.getName();
ioc.put("jobService", ob);
}
System.out.println("ioc = " + ioc);
ioc = {jobService=com.itszt.springbootaop.service.JobService@2f2c9b19}
获取有参构造方法, 然后再调用构造方法
此时构造器只有一个有参构造器
@Service
public class JobService {
public JobService(String data){
System.out.println("data = " + data);
System.out.println("JobService.JobService");
}
//2. 获取有参构造方法, 然后再调用构造方法
Constructor<?>[] declaredConstructors = JobService.class.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
Object o = constructor.newInstance("张三");
System.out.println("o = " + o);
}
data = 张三
JobService.JobService
o = com.itszt.springbootaop.service.JobService@a09ee92
同时存在无参构造器和有参构造器
@Service
public class JobService {
public JobService(){}
public JobService(String data){
System.out.println("data = " + data);
System.out.println("JobService.JobService");
}
//同时存在无参构造器和有参构造器
Constructor<JobService> declaredConstructor = JobService.class.getDeclaredConstructor(String.class);
JobService lisi = declaredConstructor.newInstance("lisi");
System.out.println("lisi = " + lisi);
data = lisi
JobService.JobService
lisi = com.itszt.springbootaop.service.JobService@a09ee92