Java之反射详解
来源:互联网 发布:java程序员发展方向 编辑:程序博客网 时间:2024/06/05 09:48
Java反射技术提供了一种用以检查或者修改程序在运行的状态或者行为的一种机制。
反射用途:
- 可扩展性:你可以通过名字匹配的方式来创建用户自定义实例
提供类管理功能和可视化开发上下文:类管理器可以用来枚举类中的成员变量,而可视化的开发环境可以帮助开发人员书写准确的代码
提供调试与测试工具:调试人员可以借助反射检查类中的私有变量,测试人员可以利用反射获取类提供的接口集合,提高代码覆盖率
反射的缺点
反射虽好,可不要“贪杯哦”,use it when necessary 。如果可以在不使用反射的情况下就可以完成实现相应的功能,那么最好不要使用。
注意,使用反射时,应注意以下问题:
性能开销:因为反射涉类型的动态解析,因此,某些针对类型的虚拟机优化操作就无法执行,这样就会导致反射的性能比非反射要差,因此应该避免在性能敏感的应用程序中频繁调用反射操作。
安全限制:反射需要一种特殊的运行时权限,因此,在安全受限的执行上下文中,你需要考虑代码可能带来的潜在风险。
内部封装的暴露:因为反射允许执行在非反射环境下“非法”的代码操作,例如:直接访问私有方法和变量,反射可能带来一些意想不到的风险以及由此带来的功能异常,反射会破坏抽象和可移植性,并因此可能会随着平台的升级而改变行为。
简单示例
首先定义一个实体类,后续我们将通过反射的方式来获取这个实体的相关信息:
import lombok.*;@Getter@Setter@ToString@AllArgsConstructor@NoArgsConstructor@DoIt(author = "test")public class User { private String name; private int age; private String address; private String format() { return String.format("%s @ %s", name, address); } String getDescription() { return format(); }}
注意,这里我为这个实体类添加了一个@DoIt的注解,RetentionPolicy=RUNTIME,这样,我们通过反射可以获取到实体类上的相关注解信息:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface DoIt { String author() default "clyde";}
接下来,注解的运用:
1、反射获取原实体类上的注解信息:
Class c = Class.forName("reflect.User"); Annotation[] annotations = c.getDeclaredAnnotations(); for (Annotation annotation : annotations) { System.out.println(String.format("annotation is %s", annotation.annotationType())); }
annotation is interface reflect.DoIt
2、反射获取原实例类属性信息
System.out.println("Fields"); Field[] fields = c.getFields(); for (Field field : fields) { System.out.println(String.format("field name is %s, field type is %s", field.getName(), field.getType())); } System.out.println("DeclaredFields"); Field[] declaredFields = c.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(String.format("field name is %s, field type is %s", field.getName(), field.getType())); }
注意这两个方法的区别:getFields() 和 getDeclaredFields()(注意:与getMethods()和getDeclaredMethods()区别类似)
- getFields() :返回的是原实体类所有的公共属性,包括所有父类中公共属性。
- getDeclaredFields():返回的是原实体类所有的属性,包括public, protected, default (package) access, and private这些属性,但不包括继承属性(即父类中的属性)
3、反射获取原实体类方法信息
/** @return the array of {@code Method} objects representing the * public methods of this class */ System.out.println("Methods"); Method[] methods = c.getMethods(); for (Method method : methods) { System.out.println(String.format("method name is %s, method modifier is %s, method parameters is %s", method.getName(), method.getModifiers(), Arrays.toString(method.getParameters()))); } /** @return the array of {@code Method} objects representing all the * declared methods of this class */ System.out.println("Declared Methods"); Method[] declaredMethods = c.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(String.format("method name is %s, method modifier is %s, method parameters is %s", method.getName(), method.getModifiers(), Arrays.toString(method.getParameters()))); }
- getMethods() :返回的是原实体类所有的公共方法,包括继承类方法(即所有父类中公共方法)。
- getDeclaredMethods():返回的是原实体类所有声明方法,但不包括继承方法(即父类中对子类可见的方法)
执行结果:
Methods
method name is toString, method modifier is 1, method parameters is []
method name is getAddress, method modifier is 1, method parameters is []
method name is getName, method modifier is 1, method parameters is []
method name is setName, method modifier is 1, method parameters is [java.lang.String arg0]
method name is getAge, method modifier is 1, method parameters is []
method name is setAddress, method modifier is 1, method parameters is [java.lang.String arg0]
method name is setAge, method modifier is 1, method parameters is [int arg0]
method name is wait, method modifier is 17, method parameters is []
method name is wait, method modifier is 17, method parameters is [long arg0, int arg1]
method name is wait, method modifier is 273, method parameters is [long arg0]
method name is equals, method modifier is 1, method parameters is [java.lang.Object arg0]
method name is hashCode, method modifier is 257, method parameters is []
method name is getClass, method modifier is 273, method parameters is []
method name is notify, method modifier is 273, method parameters is []
method name is notifyAll, method modifier is 273, method parameters is []
Declared Methods
method name is toString, method modifier is 1, method parameters is []
method name is getAddress, method modifier is 1, method parameters is []
method name is getName, method modifier is 1, method parameters is []
method name is format, method modifier is 2, method parameters is []
method name is setName, method modifier is 1, method parameters is [java.lang.String arg0]
method name is getDescription, method modifier is 0, method parameters is []
method name is getAge, method modifier is 1, method parameters is []
method name is setAddress, method modifier is 1, method parameters is [java.lang.String arg0]
method name is setAge, method modifier is 1, method parameters is [int arg0]
从上面的执行结果我们可以看到,getMethods()返回了父类java.lang.Object中的hashCode、notify等方法,我们再看下
method name is notify, method modifier is 273, method parameters is []
可以看到,Method.getModifiers()返回的是整形273,我们看下父类Object中notify方法的定义如下:
public final native void notify();
方法有三个修饰符public,final,native,在java.lang.reflect.Modifier中,这三类修饰符的定义如下:
/** * The {@code int} value representing the {@code public} * modifier. */ public static final int PUBLIC = 0x00000001; /** * The {@code int} value representing the {@code final} * modifier. */ public static final int FINAL = 0x00000010; /** * The {@code int} value representing the {@code native} * modifier. */ public static final int NATIVE = 0x00000100;
三种修饰符组合正好是 0x00000111 = 273
4、方法调用
User user = (User) c.newInstance(); user.setAddress("SH"); user.setAge(30); user.setName("Clyde");
- 无参调用
Method getNameMethod = c.getDeclaredMethod("getName"); System.out.println(getNameMethod.invoke(user));
- 有参调用
Method setAddressMethod = c.getDeclaredMethod("setAddress", String.class); setAddressMethod.invoke(user, "BJ");
- 原实体类私有方法调用
Method formatMethod = c.getDeclaredMethod("format"); if (!formatMethod.isAccessible()) { formatMethod.setAccessible(true); } System.out.println(formatMethod.invoke(user));
https://docs.oracle.com/javase/tutorial/reflect/