Java学习之反射和注解

Java注解和反射学习

一.什么是注解
注解是Jdk1.5增加的对元数据(MetaData)的支持,注解可以作用在类、接口、枚举声明、注解、成员变量、方法、构造器、包、参数和局部变量上,可以在运行时通过读取这些注解执行相应的处理。
二.注解的用途
在框架中使用注解或XML文件作为数据源来进行配置,诸如SSH这样的框架中都提供了用于配置的各种注解,从而减少XML配置文件的使用。
三.基本注解
1.@Override

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

由该Annotation的定义来看,该注解只能作用在方法上,并且只在Java源文件阶段有效,只在编译期做一些语法检查的工作,并不写入Class字节码文件。该注解的作用是强制当前方法必须是重写的父类的方法,如果当前方法在父类中找不到相关的定义,则无法编译通过。

public class Animal {
            public void eat(){
                System.out.println("吃东西");
            }
}
public class Dog extends Animal {
            @Override
            public void   run(){
            }
}

如上所示代码片,当在run方法上不添加@Override注解是可以编译通过的,加上该注解则会有The method run() of type Dog must override or implement a supertype method这样的编译错误,因为被@Override修饰的方法必须是重写父类的,但是在父类中并不能发现run方法的定义。
2.@Deprecated

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
该注解可以作用于构造器,成员变量,局部变量,方法,包,参数,类,接口上,并且可以在运行时被读取,@Documented表明该注解会被JavaDoc工具记录在生成的文档中。该注解的作用是标记被作用的类型是被遗弃的,然后表遗弃会做出相应的警告。
public class Animal {
    @Deprecated
            public void eat(){
                System.out.println("吃东西");
            }
}
Animal animal=new Animal();
    animal.eat();

上述代码中被标记的eat方法会划上横线,表示该方法已过世,当调用该方法时则会发出“The method eat() from the type Animal is deprecated”,这样的警告。
3. @ SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
有注解定义可知,该注解的作用元素与@Deprecated少了对包的支持,并且该注解同样只存在Java源文件中。该注解的作用是抑制编译器的警告,value数组中的值是警告的类型。

@SuppressWarnings({ "rawtypes", "unused" })
public class TestAnnotation {
   public static void main(String[] args) {
    List list=new ArrayList();
}

}
当使用集合时如果没有指定泛型参数,则会出现不安全的警告,list变量没有被使用也会出现"unused"的警告,通过使用SuppressWarnings注解可以使编译器不在出现警告。

四.元注解
1.@Retention

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

该注解只能用来修饰注解,其中RetentionPolicy 是一个枚举类,共有3个取值。
(1).RUNTIME 该注解信息会保留到运行时,可以通过反射在运行时获取注解信息。
(2).CLASS 该注解会被编译到Class字节码中,但无法保存到运行时
(3).SOURCE 该注解仅仅在Java源文件中存在,用于辅助编译器进行语法校正。
@Retention注解的作用是用来标记其他注解的生存范围,然后编译器和虚拟机会根据相应的策略做出不同的处理。
2.@Target

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

该注解用于标识对应的注解作用在何中元素上,其中ElementType枚举包含TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE八种类型,TYPE代表类或者接口,然后依次是成员变量,方法,参数,构造函数,局部变量,注解,包,在定义注解是可以根据需要进行选择来限定所定义的注解的作用元素。
3.@Documented
用于指定被该元注解所修饰的Annotation将被JavaDoc工具提取成文档,如果定义Annotation时使用了该注解,则所有使用该Annotation的元素提取的API文档中都会包含该Annotation说明。
五.自定义注解和注解的读取

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD })
public @interface ArrayType {
       String[] array();
       int id() default 3;
}

自定义注解非常简单,上面就是一个自定义注解的例子,自定义注解使用@interface关键字,在定义注解元素时可以用default指定默认值。其中元素类型只能是字符串,类,注解,枚举以及基本数据类型和由这几种类型组成的数组。
关于注解信息的读取需要用到反射的知识,所以我们先来总结反射中的知识。
六.反射
1.什么是反射?
当运行Java程时,编译器首先会将Java源文件编译成class字节码文件,然后将字节码文件加载到内存,解析字节码文件为每一个字节码文件生成对应的Class对象,通过这个Class对象可以获取类的变量,方法和构造器的信息,还可以进一步创建对应的对象,调用对应的方法以及为对象的属性赋值,这种在运行时操作类和对象的方式称作反射。
2.类的加载、链接、初始化
理解反射首先必须理解类的初始化,类的初始化又分为加载、链接和初始化3个阶段。
(1).类加载
类加载指的是将类的字节码文件加载到内存生成对应的Class对象的过程。
(2).类链接可以分为验证、准备和解析3个阶段。
(3).类初始化主要是初始化静态变量和执行静态代码块,并初始化该类的父类。
如何加载一个类?

//通过调用类加载器的loadClass方法 ClassLoader.getSystemClassLoader().loadClass("com.sunjinxi.spring.test.Person");
//通过Class的forName方法
   Class.forName("com.sunjinxi.spring.test.Person", true,ClassLoader.getSystemClassLoader());

loadClass方法只会执行类加载,Class.forName方法可以通过布尔值控制是否初始化该类。
3.获取Class对象
(1).通过Class.forName(“com.sunjinxi.spring.test.Person”)或者ClassLoader.loadClass(“com.sunjinxi.spring.test.Person”);
这是在字节码阶段生成Class对象的方式。
(2).通过类名获取,Person.class,该方式仅仅执行类的加载。
(3).通过对象反向获取,person.getClass();
4.反射API
通过查找API文档,制作了一张反射API类UML图,可以很清晰的了解反射API结构。
反射
由图中可以看出Java程序的四个元素分别是Class、Method、Construct和Field,Member接口是对四个元素通用操作的抽取,AnnotationElement接口定义了操作注解信息的模板,AccessibleObject实现了AnnotationElement接口主要进行访问权限的控制,通过反射访问Method、Construct和Field时都需要获取私有变量的访问权限,所有这三个类继承自AccessibleObject.。访问Class只需要获得对应的字节码即可,无需获取访问权限,所以Class类直接实现了AnnotaionElement接口。Type是一个代表Java类型的接口,其中有5个实现类,其中Class代表的是一个没有任何泛型形参的类型,GenericArrayType代表的是数组元素有泛型的类型,类似List<String>[]这种类型,ParameterizedType代表的是有泛型参数的类型,比如List<String>这样的类型,WildcardType代表的是通配符类型,比如List<? extends Number>这样的类型,TypeVariable代表的是类型参数指的是指定泛型时的T或者Z。GenericDeclaration是一个接口定义了一个获取类型参数的方法,主要是针对获取定义Method,Class和Construct时要用到的泛型形参,所以他们实现了这个接口,而Field没有实现这个接口。Modifer主要是用来判断Java程序元素的修饰符,即Public,private,protected等等。
5.反射方法浏览
Class:
forName(String className) 加载指定字节码到内存并初始化该类
forName(String name, boolean initialize,
ClassLoader loader)
加载指定字节码到内存,initialize表示是否初始化该类,是上一个方法的重载方法
newInstance() 用当前字节码对象创建一个实例,调用空参构造方法。
isInstance(Object obj) 判断指定对象是否是当前字节码对象的实例。
isAssignableFrom(Class<?> cls) 判断调用者的字节码对象所代表的类和指定字节码对象所代表的的类是否是同一个类,或者前者是后者的父类。
isInterface() 判断当前字节码对象代表的类是否是一个接口。
isArray() 判断当前字节码对象代表的是否是一个数组。
isPrimitive()判断当前字节码对象代表的是否是一个原始类型,包括(Boolean、Character 、Byte、 Short、 Integer、 Long 、Float、 Double 、Void)
isAnnotation()判断当前字节码对象代表的是否是一个注解
isSynthetic()判断当前字节码对象代表的是否是一个复合类
getName()获取当前字节码对象代表的类的名称
getClassLoader()获取加载当前字节码的类加载器
getTypeParameters()获取声明当前字节码对象所代表的的类的泛型参数,是GenericDeclaration接口中定义的方法。
getSuperclass() 返回当前字节码对象代表的类的父类的字节码对象
getGenericSuperclass() 父类字节码对象中包含泛型,返回一个Type类型,可以转换为ParameterizedType来获取参数类型
getPackage() 获取当前字节码对象代表的类所在的包对象
getInterfaces() 获取当前字节码对象代表的类所实现的接口
getGenericInterfaces() 字节码代表的类实现的接口中包含泛型
getComponentType() 用于Arry类型,获取元素中的类型
getModifiers() 获得当前字节码代表的类的修饰符
getEnclosingMethod() 如果当前字节码代表的类定义在某个方法中则返回这个方法,否则返回null;
getEnclosingConstructor() 如果当前字节码代表的类定义在某个构造器中则返回这个构造器,否则返回null;
getDeclaringClass() 返回在该字节码代表的类作为成员类所在的外部类的字节码对象
getEnclosingClass() 如果当前字节码代表的类定义在某个类中则返回这个类,否则返回null;
getSimpleName() 返回当前字节码对象代表的类的简单名称
getCanonicalName() 返回当前字节码对象代表的类的权威名称
isAnonymousClass() 判断当前指定字节码对象是否是匿名类
isLocalClass() 判断当前指定字节码对象是否是局部类(方法和构造器中的类)
isMemberClass() 判断当前指定字节码对象是否是成员内部类
getClasses() 返回当前字节码代表的类的公有成员类或者接口
getFields() 返回当前字节码代表的类的public变量
getMethods()返回当前字节码代表的类的public方法
getConstructors()返回当前字节码代表的类的public构造器
getField(String name)返回当前字节码代表的类的指定变量
getMethod(String name, Class<?>… parameterTypes)返回当前字节码代表的类的指定方法
getConstructor(Class<?>… parameterTypes)返回当前字节码代表的类的指定构造器
getDeclaredClasses() 返回在该字节码代表的类内部定义的类,接口,包括共有的私有的受保护的一个字节码数组
getDeclaredFields() 返回当前字节码代表的类的所有变量
getDeclaredMethods()返回当前字节码代表的类的所有方法
getDeclaredConstructors()返回当前字节码代表的类的所有构造器
getDeclaredField(String name)返回当前字节码代表的类的指定变量
getDeclaredMethod(String name, Class<?>… parameterTypes)
返回当前字节码代表的类的指定方法
getDeclaredConstructor(Class<?>… parameterTypes)
返回当前字节码代表的类的指定构造器
isEnum() 判断当前字节码代表的类是不是枚举类
getEnumConstants() 返回枚举类中定义的常量
cast(Object obj) 将一个对象转换成对应的类或者接口
asSubclass(Class<T> clazz) 将一个字节码对象转换为指定字节码代表的类的子类所编译的字节码对象。
getAnnotation(Class<A> annotationClass) 获取指定字节码代表的类上的注解
isAnnotationPresent(
Class<? extends Annotation> annotationClass)判断指定注解是否作用在这个字节码代表的对象上
getAnnotations() 返回该元素上的所有注解包括继承自父类的
getDeclaredAnnotations() 返回该元素上的所有注解不包括继承自父类的
Field:
由于四个元素实现的接口大体相同,其余的三个元素只列出各自不同的方法。
getType() 获取变量的类型
getGenericType()获取变量的类型,泛型类型
getXXX(Object object) 获取指定对象的该元素的各种类型的值
setXXX(Object object)为指定对象的当前变量赋各种类型的值
Method:
getReturnType() 获取方法返回值的类型
getGenericReturnType() 获取方法返回值的类型,泛型类型
getParameterTypes()获取方法参数的类型
getGenericParameterTypes()获取方法参数的类型,泛型类型
getExceptionTypes()获取异常的类型
getGenericExceptionTypes()获取异常的类型,泛型类型
invoke(Object obj, Object… args) 调用该方法
isBridge()判断是否是桥接方法
isVarArgs()判断参数中是否包含可变参数
getDefaultValue()
getParameterAnnotations()获得方法参数中的注解
Construct:
newInstance(Object … initargs) 使用指定构造器初始化对象
以上基本是所有的反射的API,除此之外,还有五种类型和Array类中的方法。
ParameterizedType: 用来获取泛型参数中的具体类型
getActualTypeArguments获得实际的类型实参
getRawType获得原始类型
getOwnerType如果原始类型是某各类的成员类,则返回这个内部类所在的外部类,O<T>.I<S>返回O<T>
TypeVariable:
getBounds 返回泛型参数的上边界的类型
getName 返回类型形参的名称
getGenericDeclaration返回声明该类型形参的类的名称
GenericArrayType:数组中的元素包含泛型
getGenericComponentType 获取数组中元素的类型
WildcardType:统配符类型
getUpperBounds:获取通配符上限
getLowerBounds:获取通配符下限
Array:用来通过反射操作数组
newInstance 根据指定的长度和类型初始化一个数组
getXXX 获取数组中指定位置的数值
setXXX设定数组中指定位置的值。
以上应该就是所有的和反射有关的方法。
七. 总结:
注解是作用在Java各个元素上的预置信息,并且可以通过反射读取,然后做出字节的处理。反射机制一般情况下很少用到,为了熟悉这一块的知识,在下一篇文章中我们将综合利用Java反射、注解和泛型的知识来简单实现Spring框架中的核心注入功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值