1、Java反射(Reflection)语法知识点及案例代码
一、Java反射基础
1. 反射定义
Java反射是指在程序运行期间,能够动态地获取类的内部信息(如属性、方法、构造方法等),并能直接操作任意对象的内部属性及方法。反射是Java被视为动态语言(或准动态语言)的一个关键特质。
2. 反射的主要作用
- 获取任意类的名称、包信息、所有属性、方法、注解、类型。
- 获取任意对象的属性,并且能改变对象的属性。
- 判断任意一个类所具有的成员变量和方法。
- 实例化任意一个类的对象。
- 实现动态分配、降低代码的耦合度、动态代理等。
3. 反射的优缺点
- 优点:
- 代码更加灵活。
- 为各种框架提供开箱即用的功能提供了便利。
- 缺点:
- 增加安全问题,可以无视泛型参数的安全检查。
- 反射的性能比较差,但在框架中影响不大。
二、反射的主要API
1. Class类
- 表示一个类的类型,可以通过它获取类的构造函数、方法、字段等信息。
- 获取Class实例的几种方式:
- 通过运行时类的属性:
类名.class
- 通过运行时类的对象:
对象名.getClass()
- 通过Class的静态方法:
Class.forName("类的路径")
- 通过运行时类的属性:
2. Constructor类
- 表示一个类的构造函数,可以用来创建该类的实例对象。
- 常用方法:
getConstructor(Class[] parameterTypes)
:获取特定构造方法。newInstance(Object... initargs)
:通过构造方法创建对象。
3. Method类
- 表示一个类的方法,可以用来调用类的方法。
- 常用方法:
getMethod(String name, Class[] parameterTypes)
:获取特定方法。invoke(Object obj, Object... args)
:执行方法。
4. Field类
- 表示类的成员变量,可以用来获取或设置对象的属性值。
- 常用方法:
getField(String name)
:获取指定名称的public修饰的成员变量。getDeclaredField(String name)
:获取指定名称的成员变量,不考虑修饰符。set(Object obj, Object value)
:设置值。get(Object obj)
:获取值。setAccessible(true)
:忽略访问权限修饰符的安全检查(暴力反射)。
三、反射的具体实现案例
1. 创建一个基本类Person
package com.ys.reflection;public class Person {// 私有属性private String name = "Tom";// 公有属性public int age = 18;// 构造方法public Person() {}// 私有方法private void say() {System.out.println("private say()...");}// 公有方法public void work() {System.out.println("public work()...");}
}
2. 通过反射操作Person类
package com.ys.reflection;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectionDemo {public static void main(String[] args) throws Exception {// 获取Class对象Class<?> personClass = Class.forName("com.ys.reflection.Person");// 获取构造方法并创建对象Constructor<?> constructor = personClass.getConstructor();Object person = constructor.newInstance();// 获取并设置私有属性nameField nameField = personClass.getDeclaredField("name");nameField.setAccessible(true);nameField.set(person, "Jerry");// 获取并调用私有方法sayMethod sayMethod = personClass.getDeclaredMethod("say");sayMethod.setAccessible(true);sayMethod.invoke(person);// 获取并调用公有方法workMethod workMethod = personClass.getMethod("work");workMethod.invoke(person);// 获取公有属性age并打印Field ageField = personClass.getField("age");int age = (int) ageField.get(person);System.out.println("Age: " + age);}
}
3. 输出结果
private say()...
public work()...
Age: 18
四、总结
Java反射机制允许程序在运行时动态地获取和操作类的内部信息,提高了代码的灵活性和可扩展性。然而,反射也存在性能开销和安全隐患,应谨慎使用。通过反射,可以实现动态代理、依赖注入等高级功能,为Java框架和插件的开发提供了强大的支持。
当然,以下是一些使用Java反射机制的具体案例,这些案例展示了反射在不同场景下的应用。
案例一:动态代理
动态代理是Java反射的一个重要应用,它允许在运行时创建一个实现了一组接口的代理类对象。这个代理对象可以拦截对接口方法的调用,并在调用真实方法之前或之后添加额外的逻辑。
示例代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定义一个接口
interface Service {void execute();
}// 实现接口的真实类
class RealService implements Service {@Overridepublic void execute() {System.out.println("Executing RealService...");}
}// 动态代理处理器
class ServiceInvocationHandler implements InvocationHandler {private Object target;public ServiceInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method execution...");Object result = method.invoke(target, args);System.out.println("After method execution...");return result;}
}public class DynamicProxyDemo {public static void main(String[] args) {// 创建真实对象Service realService = new RealService();// 创建代理对象Service proxyService = (Service) Proxy.newProxyInstance(realService.getClass().getClassLoader(),realService.getClass().getInterfaces(),new ServiceInvocationHandler(realService));// 通过代理对象调用方法proxyService.execute();}
}
输出:
Before method execution...
Executing RealService...
After method execution...
案例二:获取类的详细信息
反射可以用来在运行时获取类的详细信息,包括类的名称、包名、父类、接口、构造函数、方法和字段等。
示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;class MyClass {private String name;public int age;public MyClass() {}public MyClass(String name, int age) {this.name = name;this.age = age;}private void privateMethod() {}public void publicMethod() {}
}public class ReflectionInfoDemo {public static void main(String[] args) {try {// 获取Class对象Class<?> myClass = Class.forName("MyClass");// 打印类名System.out.println("Class Name: " + myClass.getName());// 打印包名Package pkg = myClass.getPackage();System.out.println("Package Name: " + (pkg != null ? pkg.getName() : "No package"));// 打印父类名Class<?> superclass = myClass.getSuperclass();System.out.println("Superclass Name: " + (superclass != null ? superclass.getName() : "No superclass"));// 打印实现的接口Class<?>[] interfaces = myClass.getInterfaces();if (interfaces.length > 0) {System.out.print("Interfaces: ");for (Class<?> iface : interfaces) {System.out.print(iface.getName() + " ");}} else {System.out.println("No interfaces");}// 打印构造函数Constructor<?>[] constructors = myClass.getConstructors();System.out.println("Constructors:");for (Constructor<?> constructor : constructors) {System.out.println(Modifier.toString(constructor.getModifiers()) + " " + constructor);}// 打印方法Method[] methods = myClass.getDeclaredMethods();System.out.println("Methods:");for (Method method : methods) {System.out.println(Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName() + "()");}// 打印字段Field[] fields = myClass.getDeclaredFields();System.out.println("Fields:");for (Field field : fields) {System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getType().getName() + " " + field.getName());}} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
注意:在上面的代码中,MyClass
应该位于与ReflectionInfoDemo
相同的包中,或者你应该使用完整的类名(包括包名)来替换Class.forName("MyClass")
中的"MyClass"
。
案例三:使用反射调用私有方法
有时候,你可能需要调用一个类的私有方法。虽然这通常不是最佳实践(因为它破坏了封装性),但在某些情况下(如测试框架中)可能是必要的。
示例代码:
import java.lang.reflect.Method;class PrivateMethodClass {private void privateMethod() {System.out.println("Private method called!");}
}public class PrivateMethodDemo {public static void main(String[] args) {try {// 创建对象PrivateMethodClass obj = new PrivateMethodClass();// 获取Class对象Class<?> clazz = obj.getClass();// 获取私有方法对象Method privateMethod = clazz.getDeclaredMethod("privateMethod");// 设置方法为可访问privateMethod.setAccessible(true);// 调用私有方法privateMethod.invoke(obj);} catch (Exception e) {e.printStackTrace();}}
}
输出:
Private method called!
这些案例展示了Java反射机制在不同场景下的应用,包括动态代理、获取类的详细信息和调用私有方法等。反射是一个强大的工具,但应该谨慎使用,以避免潜在的性能问题和安全风险。