java反射与注解
反射
我们都知道, 计算机运行代码, 需要经过编译 - 运行这两个步骤, 而反射就是当程序运行状态时, 通过类名, 就知道这个类有什么属性, 有什么方法在里面. 简而言之, 在 Java 中, 只要给定类的名字, 就可以通过反射机制来获得类的所有信息。
反射的使用
首先要获取到每一个字节码文件(.class)对应的 Class 类型的对象.
//3 种方法获取对象名 a. 通过现有的对象来获取: Class cls = master.getClass();//这里的 master 是一个现有的对象 b. 通过现有的类来获取: Class cls = Master.class;//Master 是一个现有的能够拿到的类 c. 通过字符串, 来得到 Class 对象, 这种方式是我们使用最广泛的一种方式, 如 Hibernate,mybatis 的映射文件 Class cls = Class.forName("cn.sz.gl.pojo.Master");//双引号中是 Master 类的 包. 类名
根据以下方法来获取类中的构造方法
//获得所有的构造方法,private 修饰的构造不能获得 Constructor[] cons = cls.getConstructors();//获得本类所有的构造方法,包括 private 修饰的构造
Constructor[] cons = cls.getDeclaredConstructors();
//得到指定的构造 方法, 如果有多个参数, 中间用 "," 隔开
Constructor con = cls.getDeclaredConstructor(String.class,Integer.class);
//也可以使用另一种方式来获得:cls.getConstructor(parameterTypes)
获取属性
//获得所有的 public 修饰的属性 (继承自父类的属性也能获得),private 修饰的属性不能获得 Field [] fs = cls.getFields();//获得本类所有的属性 (继承自父类的属性不能获得),private 修饰的属性也能获得
Field [] fs = cls.getDeclaredFields();//获得指定的属性
Field f = cls.getDeclaredField("mastername");
//但是,mastername 属性为私有的, 所以要先解除封装
f.setAccessible(true);//修改指定的属性
f.set(obj, "lisi");
获取方法
//获得所有的方法, 包括继承自父类的方法, 但只能是 public 修饰的方法 Method[] ms = cls.getMethods();//获得本类所有的方法, 不包括继承自父类的方法, 但 private 修饰的方法也能获得
Method[] ms = cls.getDeclaredMethods();//获得指定方法 (无参),show 为方法名
Method ms = cls.getDeclaredMethod("show");//获得指定方法 (有参)
Method ms = cls.getMethod("print", String.class);如果为 private 修饰,在调用该方法之前,需要先解除封装:
ms.setAccessible(true);//通过 invoke 调用方法,obj 表示在哪个对象里调用方法, 后续的参数是方法的传入参数
String str = (String) ms.invoke(obj, "测试有参方法");
注解
注解并不能改变程序的运行结果,也不会影响程序运行的性能。有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息。
1. 注解的作用
1、生成文档。这是最常见的,也是 java 最早提供的注解。常用的有 @param @return 等 2、跟踪代码依赖性,实现替代配置文件功能。比如 Spring 的注入,未来 java 开发,将大量注解配置,具有很大用处; 3、在编译时进行格式检查。如 @override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出
2. 下面三个是 java 中的内置注解:
@Deprecated: 当程序使用过时的元素时, 会发出警告
@Override: 用来修饰对父类进行重写的方法
用来抑制编译器生成警告信息
3.java.lang.annotation 提供了四种元注解
表示是否将注解信息添加在 javadoc 文档中
@Retention 用于描述注解的生命周期, 也就是该注解被保留的时间长短
@Target–注解用于什么地方
ElementType.CONSTRUCTOR: 用于描述构造器
ElementType.FIELD: 成员变量、对象、属性(包括 enum 实例)
ElementType.LOCAL_VARIABLE: 用于描述局部变量
ElementType.METHOD: 用于描述方法
ElementType.PACKAGE: 用于描述包
ElementType.PARAMETER: 用于描述参数
ElementType.TYPE: 用于描述类、接口 (包括注解类型) 或 enum 声明
@Inherited – 定义该注释和子类的关系
4. 自定义注解
声明 Test 注解
// 使用 @interface 关键字 public @interface Test { }
定义成员变量
public @interface Test { // 定义带两个成员变量的注解 // 注解中的成员变量以方法的形式来定义 String name(); int age(); //参数成员只能用 public 或缺省这两个访问权修饰 //参数成员只能用八种基本数据类型和 String、Enum、Class、annotations 等数据类型, 以及这一些类型的数组. }
要获取类方法和字段的注解信息,必须通过 Java 的反射技术来获取 Annotation 对象.
例子
自定义一个注解 @FruitName
@Target(ElementType.FIELD)//可以用来修饰对象,属性 @Retention(RetentionPolicy.RUNTIME)//什么时候都不丢弃 @Documented//用于生成 javadoc 文档 public @interface FruitName {String value() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;
}
创建一个工具类用来处理注解:
public class AnnotationUtil { public static Object findInfo(Class clazz){ Object obj = clazz.newInstance();// 获得对象 Field fs [] = clazz.getDeclaredFields();// 获得全部的属性</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i < fs.length; i++<span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">判断该属性上是否有FruitName类型的注解</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(fs[i].isAnnotationPresent(FruitName.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)){ FruitName fn </span>= fs[i].getAnnotation(FruitName.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">为属性赋值</span> fs[i].setAccessible(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">); fs[i].set(obj, fn.value()); } } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> obj; }
}