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() default ""; }
创建一个工具类用来处理注解:
public class AnnotationUtil { public static Object findInfo(Class clazz){ Object obj = clazz.newInstance();//获得对象 Field fs [] = clazz.getDeclaredFields();//获得全部的属性 for (int i = 0; i < fs.length; i++) { //判断该属性上是否有FruitName类型的注解 if(fs[i].isAnnotationPresent(FruitName.class)){ FruitName fn = fs[i].getAnnotation(FruitName.class); //为属性赋值 fs[i].setAccessible(true); fs[i].set(obj, fn.value()); } } return obj; } }