沉梦听雨的编程指南 沉梦听雨的编程指南
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • RPC
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 多媒体框架
  • 虚拟机
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)

沉梦听雨

时间是最好的浸渍剂,而沉淀是最好的提纯器🚀
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • RPC
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 多媒体框架
  • 虚拟机
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)
  • 基础篇

    • Java基础小结
    • Java基本数据类型
    • 面向对象基础小结
    • Java常见类详解
    • Java异常详解
    • Java泛型核心知识总结
    • Java反射详解
      • 什么是反射
      • 反射的优缺点
      • 反射的原理是什么?
      • 反射的应用场景
      • 为什么框架需要反射?
      • 如何获取一个类的 Class 对象?你能够通过什么方式获取到一个对象的 Class 对象?
      • 如何创建一个对象并调用其方法?你可以通过反射来创建一个对象吗?
      • 如何获取一个类的构造函数并创建对象?你能够使用反射来获取一个类的构造函数并创建对象吗?
      • 如何获取一个类的属性并修改其值?你可以通过反射来获取一个类的属性并修改其值吗?
      • 什么是动态代理?你能够使用反射来创建动态代理吗?
      • 如何调用一个私有方法?你可以使用反射来调用一个私有方法吗?
      • 如何获取一个类的注解信息?你可以使用反射来获取一个类的注解信息吗?
      • 如何提高反射的性能?你可以使用哪些技术或优化手段来提高反射的性能?
      • 获取对象属性的值
      • 除了 Java 反射,你还了解其他编程语言中的反射机制吗?请举例说明。
    • Java SPI机制详解
    • 聊聊什么是IO流
    • jdk指令
  • 集合篇

  • 并发篇

  • JVM

  • 新特性

  • Java核心技术卷I

  • Java
  • 基础篇
沉梦听雨
2023-06-29
目录

Java反射详解

# Java 反射

# 什么是反射

反射(reflection)是 Java 中的一种机制,能够在程序运行时动态地获取类的信息并操作类或对象的属性、方法和构造器等。

通过反射,可以在运行时获取类的信息,而不需要在编译时确定。

Java 反射机制主要由以下三个类组成:

  1. java.lang.Class:用于表示类的实体,可以获取类的构造器、方法和字段等信息。
  2. java.lang.reflect.Constructor:用于表示类的构造器,可以获取构造器的参数类型、修饰符等信息。
  3. java.lang.reflect.Method:用于表示类的方法,可以获取方法的参数类型、返回值类型、修饰符等信息。 通过这些类和相应的方法,可以在程序运行时获取类的信息,并进行动态的操作。例如,可以通过反射获取类的构造器,然后创建类的实例;可以获取类的方法,然后调用方法;可以获取类的字段,然后修改字段的值等。

# 反射的优缺点

优点:

  1. 动态性:反射可以在程序运行时动态地获取类的信息,并进行动态的操作。这使得 Java 程序具有了更高的动态性和灵活性。
  2. 通用性:反射适用于任何 Java 类型,可以获取任何类的信息,并进行相应的操作,如创建对象、调用方法、修改属性等。
  3. 扩展性:反射可以很方便地扩展程序的功能,例如可以实现动态代理、依赖注入等功能。

缺点:

  1. 性能问题:反射的操作需要消耗一定的时间和资源,因此在性能要求较高的场合,反射可能会影响程序的性能。(因为反射需要在运行时动态地加载和使用类)

  2. 安全问题:反射可以访问私有属性、方法和构造器等,可能会破坏程序的安全性。

  3. 可读性问题:使用反射的代码可读性较差,难以理解和维护。

# 反射的原理是什么?

反射的原理是:通过获取类的 Class 对象,然后使用该对象的一些方法,如 getDeclaredFields()、getDeclaredMethods()、getConstructors() 等来获取类的成员信息,以便在运行时动态地使用它们。

# 反射的应用场景

  1. 依赖注入(DI):通过反射获取类的信息,然后动态地创建对象并向对象中注入依赖的组件,实现对象之间的解耦。
  2. 框架开发:很多框架都使用了反射机制,例如 Spring 框架就使用了反射来实现依赖注入和 AOP 等功能。
  3. 动态代理:通过反射实现动态代理,可以在运行时创建一个代理对象,并在代理对象中调用目标对象的方法。
  4. 序列化和反序列化:通过反射获取对象的属性和方法,然后将对象序列化为字节流或反序列化为对象。
  5. 单元测试:通过反射获取类的信息,然后动态地创建对象并调用对象的方法,实现单元测试的自动化。
  6. 数据库操作:通过反射获取实体类的属性和方法,然后将实体类映射到数据库表,实现数据库操作的自动化。
  7. 动态加载类:通过反射实现动态加载类,可以在程序运行时根据需要动态地加载类并执行相应的操作。

# 为什么框架需要反射?

在 Java 中,框架需要反射的主要原因是为了提高程序的灵活性和可扩展性。

例如:

  • Spring 框架可以通过反射机制动态地创建和管理对象,以及自动装配对象之间的依赖关系。
  • 通过反射机制,框架可以动态地加载和使用用户自定义的类、方法和属性,以便能够满足不同的需求和场景。

# 如何获取一个类的 Class 对象?你能够通过什么方式获取到一个对象的 Class 对象?

获取一个类的 Class 对象可以使用三种方式:

  1. 通过类名的方式(如 MyClass.class)

    public class MyClass {
        // 类的成员变量和方法
    }
    
    // 获取 MyClass 的 Class 对象
    Class<MyClass> myClass = MyClass.class;
    
    1
    2
    3
    4
    5
    6
  2. 通过对象的方式(如 myObj.getClass())

    public class MyClass {
        // 类的成员变量和方法
    }
    
    MyClass myObj = new MyClass();
    
    // 获取 myObj 的 Class 对象
    Class<? extends MyClass> myClass = myObj.getClass();
    
    1
    2
    3
    4
    5
    6
    7
    8
  3. 通过 Class.forName() 方法。

    public class MyClass {
        // 类的成员变量和方法
    }
    
    // 通过 Class.forName() 方法获取 MyClass 的 Class 对象
    try {
        Class<?> myClass = Class.forName("com.example.MyClass");
    } catch (ClassNotFoundException e) {
        // 处理异常
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    注意:第三种方式需要指定类的完整路径名(包名 + 类名),并且需要处理 ClassNotFoundException 异常。

前两种方式只适用于已知类的情况,而后一种方式可以在运行时动态地加载和使用类。

# 如何创建一个对象并调用其方法?你可以通过反射来创建一个对象吗?

  1. 创建一个对象并调用其方法可以使用 Class.newInstance() 或 Constructor.newInstance() 方法。
  2. 同时,还可以使用 Method.invoke() 方法来调用对象的方法。
class MyClass {
    public void myMethod() {
        System.out.println("Hello, world!");
    }
}

// 通过 Class.newInstance() 方法创建 MyClass 的对象,并调用其方法
try {
    MyClass obj1 = MyClass.class.newInstance();
    obj1.myMethod();
} catch (InstantiationException | IllegalAccessException e) {
    // 处理异常
}

// 通过 Constructor.newInstance() 方法创建 MyClass 的对象,并调用其方法
try {
    Constructor<MyClass> constructor = MyClass.class.getConstructor();
    MyClass obj2 = constructor.newInstance();
    obj2.myMethod();
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
    // 处理异常
}

// 通过 Method.invoke() 方法调用 MyClass 的方法
try {
    MyClass obj3 = new MyClass();
    Method method = MyClass.class.getMethod("myMethod");
    method.invoke(obj3);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    // 处理异常
}
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
31

# 如何获取一个类的构造函数并创建对象?你能够使用反射来获取一个类的构造函数并创建对象吗?

  1. 获取一个类的构造函数可以使用 Class.getConstructors() 或 Class.getConstructor() 方法。
  2. 然后,可以使用 Constructor.newInstance() 方法来创建对象。
class MyClass {
    public MyClass() {}
    public MyClass(String arg1, int arg2) {}
}

// 获取 MyClass 的所有构造函数
Constructor<?>[] constructors = MyClass.class.getConstructors();

// 获取 MyClass 的指定构造函数
try {
    Constructor<MyClass> constructor = MyClass.class.getConstructor(String.class, int.class);
} catch (NoSuchMethodException e) {
    // 处理异常
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 如何获取一个类的属性并修改其值?你可以通过反射来获取一个类的属性并修改其值吗?

  1. 获取一个类的属性可以使用 Class.getDeclaredFields() 或 Class.getDeclaredField() 方法。
  2. 然后,可以使用 Field.set() 或 Field.get() 方法来修改或获取属性的值。
class MyClass {
    private int myField;

    public void setMyField(int value) {
        myField = value;
    }

    public int getMyField() {
        return myField;
    }
}

// 获取 MyClass 的 myField 属性,并修改其值
try {
    MyClass obj = new MyClass();
    Field field = MyClass.class.getDeclaredField("myField");
    field.setAccessible(true);
    field.setInt(obj, 42);
    System.out.println(obj.getMyField()); // 输出 42
} catch (NoSuchFieldException | IllegalAccessException e) {
    // 处理异常
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 什么是动态代理?你能够使用反射来创建动态代理吗?

动态代理是一种通过代理对象来访问目标对象的机制。

  • 可以使用 java.lang.reflect.Proxy 类来创建动态代理。
  • 可以使用 java.lang.reflect.InvocationHandler 接口来实现代理对象的调用逻辑。
interface MyInterface {
    void myMethod(String arg);
}

class MyClass implements MyInterface {
    public void myMethod(String arg) {
        System.out.println("Hello, " + arg + "!");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private final Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

// 创建 MyClass 的代理对象
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
        MyClass.class.getClassLoader(),
        new Class[] { MyInterface.class },
        new MyInvocationHandler(new MyClass())
);

// 调用代理对象的方法
proxy.myMethod("world"); // 输出 "Before method myMethod","Hello, world!","After method myMethod"
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
31
32
33
34

# 如何调用一个私有方法?你可以使用反射来调用一个私有方法吗?

调用一个私有(private)方法

  1. 首先可以使用 Class.getDeclaredMethod() 方法获取私有方法的 Method 对象,
  2. 然后使用 Method.setAccessible(true) 方法来允许访问私有方法,
  3. 最后使用 Method.invoke() 方法调用私有方法。
class MyClass {
    private void myPrivateMethod() {
        System.out.println("Hello, world!");
    }
}

// 调用 MyClass 的私有方法
try {
    // 获取对象
    MyClass obj = new MyClass();
    // 获取私有方法的 Method 对象
    Method method = MyClass.class.getDeclaredMethod("myPrivateMethod");
    // 允许访问私有方法
    method.setAccessible(true);
    // 调用私有方法
    method.invoke(obj);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    // 处理异常
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 如何获取一个类的注解信息?你可以使用反射来获取一个类的注解信息吗?

  • 获取一个类的注解信息可以使用 Class.getAnnotations() 或 Class.getAnnotation() 方法。
  • 其中,Class.getAnnotation() 方法可以获取指定类型的注解信息。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value();
}

@MyAnnotation("MyClass")
class MyClass {
    // 类的成员变量和方法
}

// 获取 MyClass 的所有注解信息
Annotation[] annotations = MyClass.class.getAnnotations();

// 获取 MyClass 的指定注解信息
MyAnnotation annotation = MyClass.class.getAnnotation(MyAnnotation.class);
String value = annotation.value();
System.out.println(value); // 输出 "MyClass"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@Target 和 @Retention 是 Java 中两个重要的元注解(即用于注解其他注解的注解),它们分别用于指定注解的作用目标和生命周期。

  • @Target 注解用于指定注解的作用目标,即注解可以用于哪些元素上。例如,@Target(ElementType.TYPE) 表示该注解可以用于类、接口、枚举等类型的元素上。
  • @Retention 注解用于指定注解的生命周期,即注解可以保留多长时间。例如,@Retention(RetentionPolicy.RUNTIME) 表示该注解可以在运行时保留,并可以通过反射机制在运行时获取注解信息。

具体来说:

  • @Target 注解有一个 value 属性,类型为 ElementType[],用于指定注解的作用目标。

    常用的目标类型包括:

    • ElementType.TYPE:类、接口、枚举等类型定义
    • ElementType.FIELD:字段、枚举常量等成员变量
    • ElementType.METHOD:方法
    • ElementType.PARAMETER:方法参数
    • ElementType.CONSTRUCTOR:构造函数
    • ElementType.LOCAL_VARIABLE:局部变量
    • ElementType.ANNOTATION_TYPE:注解类型
    • ElementType.PACKAGE:包定义
    • ElementType.TYPE_PARAMETER:类型参数声明
    • ElementType.TYPE_USE:类型使用声明
  • @Retention 注解有一个 value 属性,类型为 RetentionPolicy,用于指定注解的生命周期。

    常用的生命周期包括:

    • RetentionPolicy.SOURCE:注解只保留在源代码中,编译器会忽略它,不会包含在编译后的字节码中。
    • RetentionPolicy.CLASS:注解保留在编译后的字节码中,但不会在运行时保留。这是默认值。
    • RetentionPolicy.RUNTIME:注解保留在编译后的字节码中,并在运行时保留,可以通过反射机制获取注解信息。

# 如何提高反射的性能?你可以使用哪些技术或优化手段来提高反射的性能?

提高反射的性能可以使用缓存机制、懒加载机制、使用高性能的反射库等技术或优化手段。

# 获取对象属性的值

代码示例参考:

    Map<String, Object> dtoMap = new HashMap<>(16);
    for (Field field : value.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        Object fieldValue = field.get(value);
        if (field.getType().isAssignableFrom(MultipartFile.class) && fieldValue != null) {
            MultipartFile multipartFile = (MultipartFile) fieldValue;
            String multipartFileFiledName = multipartFile.getName();
            String fileName = multipartFile.getOriginalFilename();
            dtoMap.put(multipartFileFiledName, fileName);
        } else {
            dtoMap.put(field.getName(), fieldValue);
        }
    }
    paraMap.put(paraName, dtoMap);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

参考:java反射获取一个对象中属性(field)的值_java field 获取值-CSDN博客 (opens new window)

# 除了 Java 反射,你还了解其他编程语言中的反射机制吗?请举例说明。

其他编程语言中也存在反射机制,如 C#、Python、Ruby 等。

例如,

  • C# 中的反射机制可以使用 System.Reflection 命名空间中的类来实现,
  • Python 中的反射机制可以使用 getattr()、setattr()、hasattr() 等内置函数来实现,
  • Ruby 中的反射机制可以使用 Object#send 和 Object#define_method 等方法来实现。
上次更新: 2025/3/24 17:36:04
Java泛型核心知识总结
Java SPI机制详解

← Java泛型核心知识总结 Java SPI机制详解→

Theme by Vdoing | Copyright © 2023-2025 沉梦听雨 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式