Java注解和反射
注解和反射
一、注解(Annotation)
注解定义
- 不是程序本身,可以对程序做出解释,这一点和注释(comment)没什么区别
- 可以被其他程序读取
- 可以附在 package,class,method,field 等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程是实现对这些元数据的访问
内置注解
三个在 java.lang 包下
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告
四 个作用在其他注解的注解(或者说元注解)在 java.lang.annotation 包下
-
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问
-
@Documented - 标记这些注解是否包含在用户文档中
-
@Target - 标记这个注解应该是哪种 Java 成员
-
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
-
//定义一个注解 // Target 表示注解可以用在那些地方(在 {} 中可以指定作用在类,方法等地方) @Target(value = {ElementType,Type,ElementType.METHOD}) // Retention 表示注解在什么地方有效(runtime > class > source) @Retention(RetentionPolicy.RUNTIME) // Documented 表示注解是否将注解生成在Javadoc中 @Documented // Inherited 表示子类可以继承父类的注解 @Inherited @interface MyAnnotaion{ }
自定义注解@interface,自动继承了 java.lang.annotaion.Annotation 接口
-
@interface 用来声明一个注解,格式:public @ interface 注解名
-
其中的每一个方法实际上是声明了一个配置参数
-
方法的名称就是参数的名称
-
返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
-
可以通过 default 来声明参数的默认值
-
如果只有一个参数成员,一般参数名为 value
-
注解元素必须要有值,定义注解元素时,经常使用空字符串,0 作为默认值
-
//自定义注解 public class Test{ //注解可以显示赋值,如果没有默认值,就必须给注解赋值 @MyAnnotaion2(name = "李明",schools = {"清华大学","北京大学"}) public void test(){} } @Target(value = {ElementType,Type,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotaion2{ //注解的参数 : 参数类型 + 参数名(); String name() default ""; int age() default 0; int id() default -1;//如果默认值为-1,代表不存在 String[] schools() default {"清华大学","北京大学"}; }
二、反射机制
Class类
一个 Class 对象包含了特定某个结构(class / interface / enum / annotation / primitive type / void / [])的有关信息
- Class 本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在 JVM 中只会有一个 Class 实例
- 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成的
- 通过 Class 可以完整地得到一个类中的所有被加载的结构
- Class 类是由 Reflection 的根源,针对任何想动态加载、运行的类,唯有先获取相应的 Class 对象
反射(Reflection)
-
通过java语言中的反射机制允许程序在执行期间借助与 Reflection API 取得任何类的内部信息,并直接操作任意对象的内部属性及方法,即可以操作字节码文件(可以读和修改字节码文件)
-
相关的主要API:
- java.lang.Class :代表一个类
- java.lang.reflect.Method :代表类的方法
- java.lang.reflect.Field :代表类的成员变量
- java.lang.reflect.Constructor :代表类的构造器
-
获取java.lang.Class实例
方式 备注 Class.forName(“完整类名带包名”) 静态方法 对象.getClass() 任何类型.class -
加载完类之后,在堆内存的方法区就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构的信息。可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以称之为:反射
-
区别:
- 正常方式:引入需要的“包类”名称 ---> 通过new实例化 ---> 取得实例化对象
- 反射方式:实例化对象 ---> getClass()方法 ---> 取得完整的“包类“名称
-
优缺点:
- 优点:可以实现动态创建对象和编译,体现出很大的灵活性
- 缺点:使用反射基本上是一种解释操作,就是在告诉 JVM 希望做什么并且需要满足什么要求。这类操作总是慢于执行相同的操作
//获得类的信息
public class Test{
public static void main(String[] args) throws ClassNotFoundException{
//获得 User 实例的 Class 对象
Class c1 = Class.forName("com.daxia.User");
//获得类的名字
System.out.printf(c1.getName()); //获得包名 + 类名
System.out.printf(c1.getSimpleName()); //获得类名
//获得类的属性
Field[] fields = c1.getFields(); //只能找到Public属性
fields = c1.getDeclaredFields(); //找到全部的属性
for(Field field : fields){
System.out.printf(field);
}
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.printf(name);
//获得类的方法
Method[] methods = c1.getMethods(); //获取本类及其父类的全部Public方法
for(Methods methods : methods){
System.out.printf("正常的"+methods);
}
methods = c1.getDeclaredFields(); //获得本类的所有方法
for(Methods methods : methods){
System.out.printf("getDeclaredFields"+methods);
}
//获得指定方法
//重载
Method getName = c1.getMethod("getName",null);
Method setName = c1.getMethod("setName",String.class);
System.out.printf(getName);
System.out.printf(setName);
//获得指定的构造器
Constructor[] constructors = c1.getConstructors(); //获取本类及其父类的全部Public构造器
for(Constructor constructor : constructors){
System.out.printf(constructor);
}
constructors = c1.getDeclaredConstructors(); //获取本类及其父类的全部Public构造器
for(Constructor constructor : constructors){
System.out.printf("#"+constructor);
}
//获得指定构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class);
System.out.printf("#"+declaredConstructor);
}
}
调用指定方法
Object invoke ( Object obj,Object ... args )
-
Object 对应原方法的返回值,若原方法无返回值,此时返回 null
-
若原方法为静态方法,此时形参 Object obj 可为 null
-
若原方法形参列表为空,则 Object[] args 为 null
-
若原方法声明为 private,则 需要在调用此 invoke() 方法前,显式调用方法对象的 setAccessible(true) 方法,将可以访问 private 的方法
-
Method、Field 和 Constructor 对象都有 setAccessible() 方法。setAccessible 作用就是启动和金庸访问安全检查的开关,如果代码必须用反射,并且需要频繁的被调用,设置为true,这样就使得原本无法访问的私有成员也可以被访问
-
Class c1 = class.forName("com.chen.User"); User user = (USer)c1.newInstance(); Method setName = c1.getDeclaredMethod("setName",String.class); //invoke激活 setName.invoke(user,"李明"); System.out.println(user,getName());
ORM(Object Relationship Mapping)-->对象关系映射
//反射操作注解
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.chen.reflection.student");
//1.通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//2.获得注解的value值
Chen chen = (chen) c1.getAnnotation(chen.class);
System.out.println(chen.value());
//3.获得类指定字段的注解
Field f = c1.getDeclaredField("id");
Fieldhaha annotation = f.getAnnotation(Fieldchen.class);
System.out.println(annotation.columnName());
System.out.println(annotation.length());
System.out.println(annotation.type());
}
}
@Chen("db_student")
class student{
@Fieldchen(columnName = "db_id",type = "int",length = 10)
private int id;
@Fieldchen(columnName = "db_age",type = "int",length = 10)
private int age;
@Fieldchen(columnName = "db_name",type = "varchar",length = 3)
private String name;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface chen{
String value();
}
//定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldchen{
String columnName();
String type();
int length();
}
public student() {
}
public student(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}

浙公网安备 33010602011771号