再次认识java反射
一、概述
在认识java反射之前我们先来认识一下什么是动态语言与静态语言。
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以 被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。 Java的动态性让编程的时候更加灵活!
像C、C++这类型语言一旦编译完成之后,运行时加载到内存后,程序内部结构无法被改变,因此被称为静态语言。但是java由于有反射的功能,可以通过反射让程序在运行过程中,动态的加载类到内存,从而改变了程序结构,因此java在一定程度上具备了动态语言的特性。
反射
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

反射提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射工作原理
步骤一:源文件转class文件
在Java编程中,一旦我们完成了一个Java项目的编写,每个Java源文件都会经过编译器的处理,生成相应的.class文件。这是反射机制得以发挥作用的第一步。
步骤二:加载到jvm
当我们的Java程序开始运行时,ClassLoader会介入并负责将编译好的.class文件加载到JVM中。这一步是反射机制得以顺利工作的关键。
当一个类被成功加载到JVM后,系统会在内存中自动为其创建一个Class对象。接下来,我们可以通过这个Class对象来获取该类的各种信息,包括其属性、方法和构造函数。这一过程可以通过以下方式实现:
在日常编程中,我们通常使用new关键字来创建对象。实际上,这个过程是通过Class对象来完成的。需要注意的是,这个Class文件是在编译阶段就已经生成的,也就是说,程序在运行时,JVM是按照预先定义好的规则来执行和创建对象的。
在使用new关键字创建对象时,我们必须明确指定类名,这种实现方式被称为硬编码,缺乏灵活性。然而,通过Java的反射机制,我们只需传入类名参数,就能动态生成对象。这种灵活性极大地降低了程序各部分之间的耦合性,使得程序更加易于扩展和维护。因此,许多Java开发框架都充分利用了反射机制的优势。
使用反射
Class类是Java反射机制的核心,它代表了Java程序中的类和接口。每个类在JVM中都有一个唯一的Class对象,通过Class对象可以获取类的元数据(如构造函数、字段、方法等),并动态地操作这些元数据。
Class类的获取方式
1、通过对象的getClass()方法
package org.example; public class Main { public static void main(String[] args) { String str = "Hello, Java"; Class<?> clazz = str.getClass(); System.out.println(clazz.getName()); // 输出:java.lang.String } }

2、通过类名.class
package org.example; public class Main { public static void main(String[] args) { Class<?> clazz = String.class; System.out.println(clazz.getName()); // 输出:java.lang.String } }
3、通过Class.forName()方法
Class.forName()方法可以通过类的全限定名(包括包名)动态加载类,并返回对应的Class对象。
package org.example; public class Main { public static void main(String[] args) { try { Class<?> clazz = Class.forName("java.lang.String"); System.out.println(clazz.getName()); // 输出:java.lang.String } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
Class类的作用
Class类提供了丰富的API,用于获取类的元数据,并动态地创建对象、调用方法和访问字段。
1、获取类的信息
package org.example;
import java.lang.reflect.Modifier; public class Main { public static void main(String[] args) { Class<?> clazz = String.class; // 获取类名 System.out.println("Class Name: " + clazz.getName()); // 输出:java.lang.String // 获取简单类名 System.out.println("Simple Name: " + clazz.getSimpleName()); // 输出:String // 获取修饰符 int modifiers = clazz.getModifiers(); System.out.println("Modifiers: " + Modifier.toString(modifiers)); // 输出:public final // 获取父类 Class<?> superClass = clazz.getSuperclass(); System.out.println("Superclass: " + superClass.getName()); // 输出:java.lang.Object // 获取实现的接口 Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> iface : interfaces) { System.out.println("Interface: " + iface.getName()); } } }
运行结果

2、创建类的实例
通过Class对象,可以使用反射创建类的实例。常用的方法是newInstance(),但需要注意的是,newInstance()方法已经被标记为过时,推荐使用getDeclaredConstructor().newInstance()。
package org.example; public class Main { public static void main(String[] args) { try { Class<?> clazz = Class.forName("java.util.ArrayList"); // 创建实例 Object instance = clazz.getDeclaredConstructor().newInstance(); System.out.println(instance.getClass().getName()); // 输出:java.util.ArrayList } catch (Exception e) { e.printStackTrace(); } } }
3、获取类的构造方法、字段和方法
package org.example; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Main { public static void main(String[] args) { Class<?> clazz = String.class; // 获取所有构造方法 Constructor<?>[] constructors = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("Constructor: " + constructor); } // 获取所有字段(包括私有字段) Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println("Field: " + field.getName()); } // 获取所有方法(包括私有方法) Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println("Method: " + method.getName()); } } }
运行结果
"C:\Program Files\Java\jdk1.8.0_241\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2023.2\lib\idea_rt.jar=5619:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2023.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_241\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\rt.jar;D:\project\IDEAjava\testPro\reflectTest\target\classes;C:\Users\liyuanhong\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.70\bcprov-jdk15on-1.70.jar;C:\Users\liyuanhong\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.13.1\jackson-core-2.13.1.jar;C:\Users\liyuanhong\.m2\repository\org\apache\commons\commons-lang3\3.9\commons-lang3-3.9.jar;C:\Users\liyuanhong\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.13.3\jackson-databind-2.13.3.jar;C:\Users\liyuanhong\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.13.3\jackson-annotations-2.13.3.jar;C:\Users\liyuanhong\.m2\repository\com\alibaba\fastjson\1.2.76\fastjson-1.2.76.jar" org.example.Main Constructor: public java.lang.String(byte[],int,int) Constructor: public java.lang.String(byte[],java.nio.charset.Charset) Constructor: public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException Constructor: public java.lang.String(byte[],int,int,java.nio.charset.Charset) Constructor: public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException Constructor: java.lang.String(char[],boolean) Constructor: public java.lang.String(java.lang.StringBuilder) Constructor: public java.lang.String(java.lang.StringBuffer) Constructor: public java.lang.String(byte[]) Constructor: public java.lang.String(int[],int,int) Constructor: public java.lang.String() Constructor: public java.lang.String(char[]) Constructor: public java.lang.String(java.lang.String) Constructor: public java.lang.String(char[],int,int) Constructor: public java.lang.String(byte[],int) Constructor: public java.lang.String(byte[],int,int,int) Field: value Field: hash Field: serialVersionUID Field: serialPersistentFields Field: CASE_INSENSITIVE_ORDER Method: equals Method: toString Method: hashCode Method: compareTo Method: compareTo Method: indexOf Method: indexOf Method: indexOf Method: indexOf Method: indexOf Method: indexOf Method: valueOf Method: valueOf Method: valueOf Method: valueOf Method: valueOf Method: valueOf Method: valueOf Method: valueOf Method: valueOf Method: charAt Method: checkBounds Method: codePointAt Method: codePointBefore Method: codePointCount Method: compareToIgnoreCase Method: concat Method: contains Method: contentEquals Method: contentEquals Method: copyValueOf Method: copyValueOf Method: endsWith Method: equalsIgnoreCase Method: format Method: format Method: getBytes Method: getBytes Method: getBytes Method: getBytes Method: getChars Method: getChars Method: indexOfSupplementary Method: intern Method: isEmpty Method: join Method: join Method: lastIndexOf Method: lastIndexOf Method: lastIndexOf Method: lastIndexOf Method: lastIndexOf Method: lastIndexOf Method: lastIndexOfSupplementary Method: length Method: matches Method: nonSyncContentEquals Method: offsetByCodePoints Method: regionMatches Method: regionMatches Method: replace Method: replace Method: replaceAll Method: replaceFirst Method: split Method: split Method: startsWith Method: startsWith Method: subSequence Method: substring Method: substring Method: toCharArray Method: toLowerCase Method: toLowerCase Method: toUpperCase Method: toUpperCase Method: trim
反射与注解
注解(Annotation)是Java中的一种元数据机制,反射可以读取注解信息,并根据注解动态地执行操作。
1、通过反射读取注解信息
首先,定义一个注解
package org.example.anno; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); }
然后,使用注解
package org.example.anno; @MyAnnotation("Class Annotation") public class AnnotationExample { @MyAnnotation("Method Annotation") public void myMethod() { System.out.println("Method called"); } }
通过反射读取注解信息
import java.lang.reflect.Method; public class AnnotationReader { public static void main(String[] args) throws Exception { // 获取类的注解 Class<?> clazz = AnnotationExample.class; MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class); System.out.println("Class Annotation: " + classAnnotation.value()); // 获取方法的注解 Method method = clazz.getMethod("myMethod"); MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class); System.out.println("Method Annotation: " + methodAnnotation.value()); } }
运行结果

2、自定义注解与反射结合
自定义注解可以与反射结合,实现一些高级功能。例如,假设我们定义了一个注解@Log,用于在方法调用时记录日志:
package org.example.anno; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Log {
在方法上使用该注解:
package org.example.anno; public class LogExample { @Log public void myMethod() { System.out.println("Method called"); } }
通过反射读取注解并记录日志:
package org.example.anno; import java.lang.reflect.Method; public class LogProcessor { public static void main(String[] args) throws Exception { Class<?> clazz = LogExample.class; Method method = clazz.getMethod("myMethod"); // 检查方法是否包含@Log注解 if (method.isAnnotationPresent(Log.class)) { System.out.println("Logging before method call"); method.invoke(clazz.getDeclaredConstructor().newInstance()); System.out.println("Logging after method call"); } } }
运行结果

反射机制的高级应用
1、动态代理与反射
动态代理是反射机制的经典应用之一,它允许在运行时动态生成代理类,从而在不修改原始类的情况下,为对象添加额外的功能。
动态代理的原理
动态代理的核心是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。Proxy类用于生成代理对象,而InvocationHandler接口定义了代理对象的行为。
动态代理的工作流程如下:
- 定义接口:代理类和目标类需要实现相同的接口。
- 实现InvocationHandler接口:在invoke方法中定义代理类的行为。
- 使用Proxy.newProxyInstance生成代理对象:通过反射动态生成代理对象
使用反射实现动态代理
首先定义一个接口
package org.example.dyProxy; public interface Calculator { int add(int a, int b); }
然后实现目标类
package org.example.dyProxy; public class SimpleCalculator implements Calculator { @Override public int add(int a, int b) { return a + b; } }
实现InvocationHandler接口
package org.example.dyProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class CalculatorProxy implements InvocationHandler { private Object target; // 目标对象 public CalculatorProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 记录方法调用前的日志 System.out.println("Calling method: " + method.getName() + " with args: " + args[0] + ", " + args[1]); // 调用目标方法 Object result = method.invoke(target, args); // 记录方法调用后的日志 System.out.println("Method result: " + result); return result; } }
生成代理对象
package org.example.dyProxy; import java.lang.reflect.Proxy; public class DynamicProxyExample { public static void main(String[] args) { // 创建目标对象 Calculator target = new SimpleCalculator(); // 创建代理对象 Calculator proxy = (Calculator) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 类加载器 target.getClass().getInterfaces(), // 目标对象的接口 new CalculatorProxy(target) // 代理处理器 ); // 调用代理对象的方法 proxy.add(10, 20); } }

在这个例子中,通过反射生成了一个代理对象,并在调用目标方法前后添加了日志功能。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号