ToB企服应用市场:ToB评测及商务社交产业平台

标题: Java学习十七—反射机制:解锁代码的无限大概 [打印本页]

作者: 农民    时间: 2024-11-3 09:55
标题: Java学习十七—反射机制:解锁代码的无限大概
Java学习十七—反射机制:解锁代码的无限大概

一、关于反射

1.1 简介

Java 反射(Reflection)是Java 的特征之一,它答应程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。
反射机制可以或许使程序具备更大的灵活性和扩展性


Oracle 官方对反射的解释:
Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

1.2 发展

Java 反射机制的发展进程可以分为几个紧张的阶段,随着 Java 语言和平台的演进,反射的功能和使用场景也不断丰富。
1. Java 1.0 版本(1996 年)
2. Java 1.1 版本(1997 年)
3. Java 2(JDK 1.2,1998 年)
4. Java 5(JDK 1.5,2004 年)
5. Java 6(2006 年)及后续版本
6. Java 8(2014 年)
7. Java 9 及后续版本

1.3 特点

优点
缺点

1.4 应用场景

很多人都认为反射在现实的 Java 开发应用中并不广泛,实在否则。当我们在使用 IDE(如 Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会主动列出它的属性或方法,这里就会用到反射。
反射最紧张的用途就是开发各种通用框架。 很多框架(比如 Spring)都是设置化的(比如通过 XML 文件设置 Bean),为了保证框架的通用性,它们大概需要根据设置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml​ 里去设置 Action​,比如:
  1.            <action name="login"
  2.                
  3.                method="execute">
  4.            <result>/shop/shop-index.jsp</result>
  5.            <result name="error">login.jsp</result>
  6.        </action>
复制代码
设置文件与 Action​ 建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter​ 拦截,然后 StrutsPrepareAndExecuteFilter​ 会去动态地创建 Action 实例。比如我们请求 login.action​,那么 StrutsPrepareAndExecuteFilter​就会去剖析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。
对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的焦点。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过相识一下框架的底层机制有助于丰富自己的编程头脑,也是很有益的。

以下是一些常见的应用场景:

1. 框架开发

2. 单位测试

3. 动态署理

4. 设置文件剖析

5. 序列化和反序列化

6. 插件系统

7. 数据绑定

8. 脚本语言集成

9. 安全性和权限管理

10. 日志记载


1.5 主要API

在Java中,反射主要涉及以下几个紧张的类和接口:


二、反射工作原理

调用反射的总体流程如下:
1、当编写完一个Java项目之后,每个java文件都会被编译成一个.class文件。

2、这些class文件在程序运行时会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中主动产生一个Class对象。

3、通过Class对象获取Field/Method/Construcor

我们一般平时是通过new的形式创建对象现实上就是通过这些Class来创建的,只不过这个class文件是编译的时候就生成的,程序相当于写死了给jvm去跑。

反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类大概之前用不到所以不用加载到jvm,而是在运行时根据需要才加载。
原来使用new的时候,需要明确的指定类名,这个时候属于硬编码实现,而在使用反射的时候,可以只传入类名参数,就可以生成对象,降低了耦合性,使得程序更具灵活性。


二、反射的根本运用

反射可以用于判断任意对象所属的类,获得 Class 对象,构造任意一个对象以及调用一个对象。
这里介绍根本反射功能的使用和实现(反射相干的类一般都在 java.lang.relfect 包里)。

2.1 获取Class 对象

在Java中,获取一个类的Class​对象是使用反射的第一步,Class​对象用于操作类的信息。
方式

有以下集中方式可以获取Class类的实例:
  1. ClassLoader classLoader = MyClass.class.getClassLoader();
  2. Class<?> clazz = classLoader.loadClass("com.example.MyClass");
复制代码

示例


  1. public class ReflectionDemo {
  2.     public static void main(String[] args) throws ClassNotFoundException {
  3.         //获取 Class 对象
  4.         //方法1:使用 Class 类的 forName 静态方法(通过类的全名(包括包名)来获取)
  5.         Class<?> baseAreaInfoDTOClass = Class.forName("com.gree.gpurchase.finance.infrastructure.dto.BaseAreaInfoDTO");
  6.         System.out.println(baseAreaInfoDTOClass);
  7.         //方法2:使用类名.class
  8.         Class<BaseAreaInfoDTO> dtoClass = BaseAreaInfoDTO.class;
  9.         System.out.println(dtoClass);
  10.         //方法3:调用某个对象的 getClass() 方法
  11.         BaseAreaInfoDTO baseAreaInfoDTO = new BaseAreaInfoDTO();
  12.         Class<? extends BaseAreaInfoDTO> infoDTOClass = baseAreaInfoDTO.getClass();
  13.         System.out.println(infoDTOClass);
  14.         //方法4:使用 ClassLoader
  15.         ClassLoader classLoader = BaseAreaInfoDTO.class.getClassLoader();
  16.         Class<?> bClass = classLoader.loadClass("com.gree.gpurchase.finance.infrastructure.dto.BaseAreaInfoDTO");
  17.         System.out.println(bClass);
  18.         //示例:获取基本类型的 Class 对象
  19.         Class<?> intClass = int.class;
  20.         Class<?> voidClass = void.class;
  21.         System.out.println("int class对象:" + intClass);
  22.         System.out.println("void class对象:" + voidClass);
  23.         //示例:获取数组的 Class 对象
  24.         Class<BaseAreaInfoDTO[]> dClass = BaseAreaInfoDTO[].class;
  25.         System.out.println("数组的class对象:" + dClass);
  26.         int[] intArray = {5, 7};
  27.         Class<?> intArrayClass = intArray.getClass();
  28.         System.out.println(intArrayClass);
  29.         String[] strArray = {"a", "b"};
  30.         Class<?> strArrayClass = strArray.getClass();
  31.         System.out.println(strArrayClass);
  32.     }
  33. }
复制代码

2.2 获取类的字段

使用反射获取类的成员变量(字段)的方法主要依赖于Class​类和Field​类。下面是具体的步骤,展示如何使用反射获取类的成员变量。
步骤


示例

以下示例中界说了一个简单的类Person​,然后通过反射获取其成员变量。
  1. import java.lang.reflect.Field;
  2. class Person {
  3.     private String name;
  4.     public int age;
  5.     public Person(String name, int age) {
  6.         this.name = name;
  7.         this.age = age;
  8.     }
  9. }
  10. public class ReflectionExample {
  11.     public static void main(String[] args) {
  12.         try {
  13.             // 获取Person类的Class对象
  14.             Class<?> personClass = Person.class;
  15.             // 获取所有声明的字段
  16.             Field[] fields = personClass.getDeclaredFields();
  17.             // 遍历字段并打印信息
  18.             for (Field field : fields) {
  19.                 // 获取字段名称
  20.                 String fieldName = field.getName();
  21.                 // 获取字段类型
  22.                 Class<?> fieldType = field.getType();
  23.                 System.out.println("Field Name: " + fieldName + ", Field Type: " + fieldType.getName());
  24.                 // 如果字段是私有的,需要设置可访问性
  25.                 if (!field.isAccessible()) {
  26.                     field.setAccessible(true);
  27.                 }
  28.                 // 创建实例以获取字段的值
  29.                 Person person = new Person("John Doe", 30);
  30.                 // 获取字段的值
  31.                 Object value = field.get(person);
  32.                 System.out.println("Value of " + fieldName + ": " + value);
  33.             }
  34.         } catch (Exception e) {
  35.             e.printStackTrace();
  36.         }
  37.     }
  38. }
复制代码

输出结果
运行该代码后,输出如下所示:
  1. Field Name: name, Field Type: java.lang.String
  2. Value of name: John Doe
  3. Field Name: age, Field Type: int
  4. Value of age: 30
复制代码

注意事项
通过上述步骤和示例,您可以轻松获取类的成员变量。使用反射时要谨慎,确保理解其影响和使用场景

2.3 获取类的方法

使用反射机制可以动态地获取类的信息,包括类的方法。下面是如何使用反射获取类的方法的步骤和示例代码。
步骤


示例

以下是一个示例代码,展示了如何获取某个类的方法信息:

  1. import java.lang.reflect.Method;
  2. public class ReflectionExample {
  3.     public static void main(String[] args) {
  4.         // 示例类
  5.         Class<?> clazz = SampleClass.class; // 获取SampleClass的Class对象
  6.         // 获取所有公有的方法
  7.         Method[] methods = clazz.getMethods();
  8.         System.out.println("公有方法:");
  9.         for (Method method : methods) {
  10.             System.out.println("方法名: " + method.getName());
  11.             System.out.println("返回类型: " + method.getReturnType().getName());
  12.             System.out.print("参数类型: ");
  13.             Class<?>[] parameterTypes = method.getParameterTypes();
  14.             for (Class<?> paramType : parameterTypes) {
  15.                 System.out.print(paramType.getName() + " ");
  16.             }
  17.             System.out.println("\n");
  18.         }
  19.         // 获取所有声明的方法,包括私有方法
  20.         Method[] declaredMethods = clazz.getDeclaredMethods();
  21.         System.out.println("所有声明的方法:");
  22.         for (Method declaredMethod : declaredMethods) {
  23.             System.out.println("方法名: " + declaredMethod.getName());
  24.             System.out.println("返回类型: " + declaredMethod.getReturnType().getName());
  25.             System.out.print("参数类型: ");
  26.             Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
  27.             for (Class<?> paramType : parameterTypes) {
  28.                 System.out.print(paramType.getName() + " ");
  29.             }
  30.             System.out.println("\n");
  31.         }
  32.     }
  33. }
  34. // 示例类
  35. class SampleClass {
  36.     public void publicMethod(String param) {
  37.         // 方法实现
  38.     }
  39.     private void privateMethod() {
  40.         // 方法实现
  41.     }
  42.     protected int protectedMethod(int x) {
  43.         return x * 2;
  44.     }
  45.     void defaultMethod() {
  46.         // 方法实现
  47.     }
  48. }
复制代码

2.4 获得类的构造函数

使用反射机制可以获取类的构造函数。下面是获取构造函数的步骤和示例代码。
步骤


示例


  1. import java.lang.reflect.Constructor;
  2. public class ReflectionExample {
  3.     public static void main(String[] args) {
  4.         // 示例类
  5.         Class<?> clazz = SampleClass.class; // 获取SampleClass的Class对象
  6.         // 获取所有公有构造函数
  7.         Constructor<?>[] constructors = clazz.getConstructors();
  8.         System.out.println("公有构造函数:");
  9.         for (Constructor<?> constructor : constructors) {
  10.             System.out.println("构造函数名: " + constructor.getName());
  11.             System.out.print("参数类型: ");
  12.             Class<?>[] parameterTypes = constructor.getParameterTypes();
  13.             for (Class<?> paramType : parameterTypes) {
  14.                 System.out.print(paramType.getName() + " ");
  15.             }
  16.             System.out.println("\n");
  17.         }
  18.         // 获取所有声明的构造函数,包括私有构造函数
  19.         Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
  20.         System.out.println("所有声明的构造函数:");
  21.         for (Constructor<?> declaredConstructor : declaredConstructors) {
  22.             System.out.println("构造函数名: " + declaredConstructor.getName());
  23.             System.out.print("参数类型: ");
  24.             Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
  25.             for (Class<?> paramType : parameterTypes) {
  26.                 System.out.print(paramType.getName() + " ");
  27.             }
  28.             System.out.println("\n");
  29.         }
  30.     }
  31. }
  32. // 示例类
  33. class SampleClass {
  34.     public SampleClass() {
  35.         // 默认构造函数
  36.     }
  37.     public SampleClass(String name) {
  38.         // 带参数的构造函数
  39.     }
  40.     private SampleClass(int age) {
  41.         // 私有构造函数
  42.     }
  43. }
复制代码

2.5 调用类的方法

使用反射机制可以动态地调用一个类的方法。下面是实现这一功能的步骤和示例代码。
步骤


示例

以下是一个示例代码,展示如何使用反射调用类的方法:
  1. import java.lang.reflect.Method;
  2. public class ReflectionExample {
  3.     public static void main(String[] args) {
  4.         try {
  5.             // 获取SampleClass的Class对象
  6.             Class<?> clazz = SampleClass.class;
  7.             // 创建SampleClass的实例
  8.             Object sampleInstance = clazz.getDeclaredConstructor().newInstance();
  9.             // 获取公有方法
  10.             Method publicMethod = clazz.getMethod("publicMethod", String.class);
  11.             publicMethod.invoke(sampleInstance, "Hello, World!");
  12.             // 获取私有方法
  13.             Method privateMethod = clazz.getDeclaredMethod("privateMethod");
  14.             privateMethod.setAccessible(true); // 允许访问私有方法
  15.             privateMethod.invoke(sampleInstance);
  16.         } catch (Exception e) {
  17.             e.printStackTrace();
  18.         }
  19.     }
  20. }
  21. // 示例类
  22. class SampleClass {
  23.     public void publicMethod(String message) {
  24.         System.out.println("公有方法被调用,消息: " + message);
  25.     }
  26.     private void privateMethod() {
  27.         System.out.println("私有方法被调用");
  28.     }
  29. }
复制代码

2.6 使用反射创建数组

可以使用反射来动态创建数组。通过 java.lang.reflect.Array​ 类,可以创建和操作数组。以下是创建数组的步骤和示例代码。
步骤


示例

以下示例展示如何使用反射创建一维和二维数组:
  1. import java.lang.reflect.Array;
  2. public class ReflectionArrayExample {
  3.     public static void main(String[] args) {
  4.         try {
  5.             // 创建一个一维数组,类型为Integer,长度为5
  6.             Class<?> arrayClass = Integer.class; // 数组元素类型
  7.             int length = 5;
  8.             Object intArray = Array.newInstance(arrayClass, length);
  9.             // 设置数组元素
  10.             for (int i = 0; i < length; i++) {
  11.                 Array.set(intArray, i, i * 10); // 设置数组元素
  12.             }
  13.             // 打印数组内容
  14.             for (int i = 0; i < length; i++) {
  15.                 System.out.println(Array.get(intArray, i)); // 获取数组元素
  16.             }
  17.             // 创建一个二维数组,类型为String,维度为3x2
  18.             Class<?> strArrayClass = String.class;
  19.             int rows = 3;
  20.             int cols = 2;
  21.             Object strArray = Array.newInstance(strArrayClass, rows, cols);
  22.             // 设置二维数组元素
  23.             Array.set(strArray, 0, Array.newInstance(strArrayClass, cols)); // 第一行
  24.             Array.set(Array.get(strArray, 0), 0, "Hello");
  25.             Array.set(Array.get(strArray, 0), 1, "World");
  26.             Array.set(strArray, 1, Array.newInstance(strArrayClass, cols)); // 第二行
  27.             Array.set(Array.get(strArray, 1), 0, "Java");
  28.             Array.set(Array.get(strArray, 1), 1, "Reflection");
  29.             // 打印二维数组内容
  30.             for (int i = 0; i < rows; i++) {
  31.                 for (int j = 0; j < cols; j++) {
  32.                     System.out.print(Array.get(Array.get(strArray, i), j) + " ");
  33.                 }
  34.                 System.out.println();
  35.             }
  36.         } catch (Exception e) {
  37.             e.printStackTrace();
  38.         }
  39.     }
  40. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4