当前位置: 首页 > news >正文

深入理解Java反射

 反射(Reflection)是Java语言的一个强大特性,它允许程序在运行时动态地获取类的信息并操作类或对象的属性、方法和构造器。就是在获取运行时的java字节码文件,通过各种方法去创建对象,反射是Java被视为动态语言的关键特性之一。

反射其实就是获取到内存的class对象,而class对象是类加载器加载了字节码文件转换过来的。

一、反射基础概念

1. 什么是反射

反射是指在程序运行期间,能够动态地获取类的信息(如类名、方法、字段、构造器等)并动态调用对象方法或修改属性的能力。

2. 反射的核心类

Java反射主要涉及以下核心类:

  • Class:表示类的实体

  • Field:表示类的成员变量

  • Method:表示类的方法

  • Constructor:表示类的构造方法

  • Array:提供了动态创建和访问数组的方法

二、反射的基本使用

1. 获取Class对象的三种方式

// 1. 通过类名.class
Class<?> clazz1 = String.class;

// 2. 通过对象.getClass()
String str = "Hello";
Class<?> clazz2 = str.getClass();

// 3. 通过Class.forName()
Class<?> clazz3 = Class.forName("java.lang.String");

2. 创建对象

// 使用无参构造器
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance(); // 已废弃,建议使用getDeclaredConstructor().newInstance()

// 使用有参构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
User user = (User) constructor.newInstance("张三", 25);

3. 获取和操作字段

Class<?> clazz = User.class;
User user = new User("李四", 30);

// 获取public字段
Field nameField = clazz.getField("name"); // 只能获取public字段
nameField.set(user, "王五");

// 获取所有字段(包括private)
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 设置可访问private字段
ageField.set(user, 35);

4. 调用方法 

Class<?> clazz = User.class;
User user = new User();

// 获取public方法
Method publicMethod = clazz.getMethod("publicMethod", String.class);
publicMethod.invoke(user, "参数");

// 获取所有方法(包括private)
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(user);

三、反射的高级特性

1. 动态代理

反射常用于实现动态代理:

interface Subject {
    void request();
}

class RealSubject implements Subject {
    public void request() {
        System.out.println("真实请求");
    }
}

class DynamicProxy implements InvocationHandler {
    private Object target;
    
    public DynamicProxy(Object target) {
        this.target = target;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前操作");
        Object result = method.invoke(target, args);
        System.out.println("代理后操作");
        return result;
    }
}

// 使用
Subject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
    realSubject.getClass().getClassLoader(),
    realSubject.getClass().getInterfaces(),
    new DynamicProxy(realSubject)
);
proxy.request();

2. 注解处理

反射可以用于处理运行时注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
    String value();
}

class AnnotationProcessor {
    public void process(Object obj) throws Exception {
        for (Method method : obj.getClass().getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
                System.out.println("发现注解方法: " + method.getName() + 
                                 ", 注解值: " + annotation.value());
                method.invoke(obj);
            }
        }
    }
}

3. 泛型类型擦除后的类型获取

虽然Java在运行时擦除了泛型类型信息,但可以通过反射获取字段或方法的泛型签名:

 

class GenericClass<T> {
    private List<String> stringList;
    
    public void setValue(T value) {}
}

// 获取字段的泛型类型
Field field = GenericClass.class.getDeclaredField("stringList");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
    ParameterizedType pType = (ParameterizedType) type;
    Type[] actualTypes = pType.getActualTypeArguments(); // [String]
}

// 获取方法的泛型参数类型
Method method = GenericClass.class.getMethod("setValue", Object.class);
Type[] paramTypes = method.getGenericParameterTypes();

四、反射的性能考虑

反射虽然强大,但也有一些缺点:

  1. 性能开销:反射操作比直接调用慢,因为涉及动态类型解析和方法调用验证

  2. 安全限制:反射需要运行时权限,可能被安全管理器限制

  3. 破坏封装:可以访问私有成员,破坏了面向对象的封装性

性能优化建议

  • 缓存Class对象、Method对象等反射结果

  • 对于频繁调用的方法,可以设置setAccessible(true)减少访问检查

  • 考虑使用MethodHandle(Java 7+)作为替代方案

五、反射的应用场景

  1. 框架开发:Spring、Hibernate等框架大量使用反射

  2. 动态代理:AOP编程的基础

  3. IDE开发:如代码提示、自动补全

  4. 测试工具:如JUnit通过反射调用测试方法

  5. 序列化/反序列化:如JSON/XML解析库

  6. 插件系统:动态加载和调用插件

六、反射的替代方案

在Java 7+中,可以使用MethodHandle作为反射的替代方案,它提供了更好的性能:

class MethodHandleDemo {
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType type = MethodType.methodType(void.class, String.class);
        MethodHandle handle = lookup.findVirtual(User.class, "publicMethod", type);
        
        User user = new User();
        handle.invokeExact(user, "MethodHandle调用");
    }
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dtcms.com/a/124254.html

相关文章:

  • 导入 Excel 批量替换文件名称及扩展名
  • react中通过 EventEmitter 在组件间传递状态
  • QTreeWidget 手动设置选中项后不高亮的问题
  • rbd块设备的id修改
  • 纳米软件储能电源模块自动化测试深度解析
  • Git版本管理系列:(三)远程仓库
  • vxe-table4.6 + vue3.2 + ant-design-vue 3.x 实现对列的显示、隐藏、排序
  • MYSQL-创建和使用表
  • Higress: 阿里巴巴高性能云原生API网关详解
  • 分享:批量识别图片文字并重命名,根据图片文字内容对图片批量重命名,Python和Tesseract OCR的完成方案
  • 阿里云负载均衡可以抗ddos吗
  • Qt零散知识点
  • Node.js 中处理 Excel 文件的最佳实践
  • SpringBoot中3种优雅停机的实现方式
  • 基于 Qt 的图片处理工具开发(一):拖拽加载与基础图像处理功能实现
  • HTML 嵌入标签对比:小众(<embed>、<object>) 与 <iframe> 的优缺点及使用场景和方式
  • Hierarchical Reinforcement Learning for Course Recommendation in MOOCs论文阅读
  • leetcode 74. 搜索二维矩阵
  • Java从入门到“放弃”(精通)之旅——启航①
  • Ubuntu远程连接Mysql数据库(图文详解)
  • HashMap为什么扩容为原来2倍呢?
  • 栈和队列详细讲解
  • HDCP(五)
  • 【进程通信】 Linux下使用共享内存实现跨进程通信:基于C++的完整示例
  • rancher 采用ingerss ssl 部署nginx+php项目
  • c# 使用NPOI将datatable的数据导出到excel
  • OSPF不规则区域和LSA
  • 【Java学习】AI时代下如何学习Java语言开发
  • 【算法学习计划】回溯 -- 记忆化搜索
  • StringTemplate修仙指南:字符串处理的“言出法随“大法