编程技术分享平台

网站首页 > 技术教程 正文

JAVA学习基础之Annotation(java annotation processor)

xnh888 2024-10-19 15:59:06 技术教程 17 ℃ 0 评论

第十六章、Annotation

在JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在JDK1.5之后称为注释,即:使用注释的方式加入一些程序的信息。

java.lang.annotation.Annotation接口是所有的Annotation都必须实现的接口。

16-1、Annotation——系统内建Annotation

在JDK1.5之后,系统中已经建立了如下的三个内建的Annotation类型,用户直接使用即可:

·@Override:覆写的Annotation

·@Deprecated:不赞成使用的Annotation

·@SuppressWarnings:压制安全警告的Annotation

NO

Annotation

JAVA中的声明

1

@Override

@Target(value=METHOD)

@Retention(value=SOURCE)

public @interface Override

2

@Deprecated

@Documented

@Retention(value=RUNTIME)

public @interface Deprecated

3

@SuppressWarnings

@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,

LOCAL_VARIABLE})

@Retention(value=SOURCE)

public @interface SuppressWarnings

16-1-1、@Override

Override表示方法覆写的正确性,例如现在有如下一段代码:

class Person{

public String getInfo(){ // 取得信息

return "这是一个Person类。" ;

}

};

class Student extends Person{ // 继承此类

public String getinfo(){ // 覆写方法

return "这是一个Student类。" ;

}

};

public class OverrideAnnotationDemo01{

public static void main(String args[]){

Person per = new Student() ;

System.out.println(per.getInfo()) ; // 输出信息

}

};

此时,可能存在某种失误,将方法名称编写错误:

class Person{

public String getInfo(){ // 取得信息

return "这是一个Person类。" ;

}

};

class Student extends Person{ // 继承此类

@Override

public String getinfo(){ // 覆写方法

return "这是一个Student类。" ;

}

};

public class OverrideAnnotationDemo01{

public static void main(String args[]){

Person per = new Student() ;

System.out.println(per.getInfo()) ; // 输出信息

}

};

使用Override注释可以保证程序正确的执行。

16-1-2、@Deprecated

使用Deprecated注释的Annotation本身是不建议使用的一个操作。

class Demo{

@Deprecated // 声明不建议使用的操作

public String getInfo(){

return "这是一个Person类。" ;

}

};

public class DeprecatedAnnotationDemo01{

public static void main(String args[]){

Demo d = new Demo() ;

System.out.println(d.getInfo()) ;

}

};

以上程序编译出错,而是出现了一个安全的警告信息。

@Deprecated // 声明不建议使用的操作

class Demo{

public String getInfo(){

return "这是一个Person类。" ;

}

};

public class DeprecatedAnnotationDemo02{

public static void main(String args[]){

Demo d = new Demo() ;

System.out.println(d.getInfo()) ;

}

};

16-1-3、@SuppressWarnings

用于压制警告信息。

以之前的泛型操作为例,在泛型中如果没有指定泛型类型,则使用时肯定出现安全警告。

class Demo<T>{

private T var ;

public T getVar(){

return this.var ;

}

public void setVar(T var){

this.var = var ;

}

};

public class SuppressWarningsAnnotationDemo01{

public static void main(String args[]){

Demo d = new Demo() ;

d.setVar("李兴华") ;

System.out.println("内容:" + d.getVar()) ;

}

};

此时就可以使用SuppressWarnings这个Annotation将这种警告信息进行压制。

class Demo<T>{

private T var ;

public T getVar(){

return this.var ;

}

public void setVar(T var){

this.var = var ;

}

};

public class SuppressWarningsAnnotationDemo01{

@SuppressWarnings(“unchecked”)

public static void main(String args[]){

Demo d = new Demo() ;

d.setVar("李兴华") ;

System.out.println("内容:" + d.getVar()) ;

}

};

以上只是压制了一个警告信息,当然,也可以同时压制多个警告信息,只需要以数组的形式出现即可。

@Deprecated

class Demo<T>{

private T var ;

public T getVar(){

return this.var ;

}

public void setVar(T var){

this.var = var ;

}

};

public class SuppressWarningsAnnotationDemo01{

@SuppressWarnings(“unchecked”,”deprecation”)

public static void main(String args[]){

Demo d = new Demo() ;

d.setVar("李兴华") ;

System.out.println("内容:" + d.getVar()) ;

}

};


@SuppressWarnings中的关键字

NO

关键字

描述

1

deprecation

使用了不赞成使用的类或方法时的警告

2

unchecked

执行了未检查的转换时警告,例如:泛型操作中没有指定泛型类型

3

fallthrough

当switch程序块直接通往下种情况而没有Break时的警告

4

path

在类路径、源文件路径等中有不存在的路径时警告

5

serial

当在可序列化的类上缺少serialVersionUID定义时的警告

6

finally

任何finally子句不能正常完成时的警告

7

all

关于以上所有情况的警告

在设置注释信息的时候,是以keyvalue的形式出现的,所以以上的@SuppressWarnings也可以直接使用“value={“unchecked”,”deprecation”}”的方式设置。

@Deprecated

class Demo<T>{

private T var ;

public T getVar(){

return this.var ;

}

public void setVar(T var){

this.var = var ;

}

};

public class SuppressWarningsAnnotationDemo01{

@SuppressWarnings(value={“unchecked”,”deprecation”})

public static void main(String args[]){

Demo d = new Demo() ;

d.setVar("李兴华") ;

System.out.println("内容:" + d.getVar()) ;

}

};


16-2、Annotation——自定义Annotation

Annotation定义格式:

·[public] @interface Annotation名称{

数据类型 变量名称();

}

定义一个简单的Annotation:

public @interface MyDefaultAnnotationNoneParm{

}

之后就可以直接在程序中使用“@Annotation名称”的格式。

@MyDefaultAnnotationNoneParm

class Demo{

};

此时就表示在Demo类上使用Annotation。

还可以向Annotation中设置,使用变量接收参数:

public @interface MyDefaultAnnotationSingleParam{

public String value(); //接收设置的内容

}

在使用的时候就必须清楚的指明变量的内容:

@MyDefaultAnnotationSingleParam(“cyg”)

class Demo{

};

或者也可以使用明确的标记,表示内容赋给哪个参数:

@MyDefaultAnnotationSingleParam(value=“cyg”)

class Demo{

};

以上的参数是要赋给value属性的。

既然可以设置一个参数,则也就可以同时设置多个参数:

public @interface MyDefaultAnnotationSingleParam{

public String key();

public String value(); //接收设置的内容

}


此Annotation在使用的时候需要设置两个参数,一个是key,另外一个是value。

@MyDefaultAnnotationMoreParam(key=”MLDN”,value=“cyg”)

class Demo{

};


也可以设置一个数组进去:

public @interface MyDefaultAnnotationArrayParam{

public String[] value(); //接收设置的内容

}

接收的内容本身是一个数组类型,要传递数组,类似与之前的SuppressWarnings

@MyDefaultAnnotationArrayParam(value={”MLDN”, “cyg”})

class Demo{

};

以上所定义的全部的Annotation中有一个特点:所有的参数内容需要在使用注释的时候设置上去,那么也可以为一个参数设置默认的内容,在声明的时候使用default即可。

public @interface MyDefaultAnnotationValue{

public String key() default “MLDN”;//指定好了默认值

public String value() default “cyg”; //指定好了默认值

}

当在去使用此Annotation的时候就可以不用设置内容。

@MyDefaultAnnotationValue

class Demo{

}

没有设置内容,编译也不会出现任何的错误。

在操作中,对于一个Annotation而言有时候会固定其取值范围,只能取固定的几个值,那么这个时候实际上就需要依靠枚举。

public enum MyName{

MLDN,cyg,chen;

}

以后的Annotation的取值,只能从这三个值中取走。

public@interface MyDefaultAnnotationEnum{

public MyName name() default MyName.cyg; //指定默认值

}

此时以上的Annotation已经设置好了一个枚举中的内容作为默认值,那么外部使用此Annotation的时候也需要从枚举固定取值:

@MyDefaultAnnotationEnum(name=MyName.cyg) //只能从固定的枚举中取内容

class Demo{

}

Retention和RetentionPolicy

在Annotation可以使用Retention定义一个Annotation的保存范围,此Annotation的定义如下:

@Documented

@Retention(value=RUNTIME)

@Target(value=ANNOTATION_TYPE)

public @interface Retention{

RetentionPolicy value();

}

在以上的Retention定义中存在了一个RetentionPolicy的变量,此变量用于指定Annotation的保存范围,RetentionPolicy包含三种范围:

NO

范围

描述

1

SOURCE

此Annotation类型的信息只会保留在程序源文件之中(*.java),编译之后不会保存在编译好的类文件(*.class)之中

2

CLASS

此Annotation类型将保留在程序源文件(*.java)和编译之后的类文件(*.class)之中,在使用此类的时候,这些Annotation信息将不会被加载到虚拟机(JVM)之中,如果一个Annotation声明时没有指定范围,则默认是此范围

3

RUNTIME

此Annotation类型的信息保留在源文件(*.java)、类文件(*.class)在执行时也会加载到JVM中

在三个范围中,最需要的关心的就是RUNTIME范围,因为在执行的时候起作用。

使用Retention指定一个Annotation的范围,范围为RUNTIME范围:

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME) //表示此Annotation在运行时有效

public @interface MyDefaultRetentionAnnotation{

public String name() default “cyg”;

}

此Annotation可以在运行时起作用,而且在反射与Annotation的操作中,也必须使用此种范围。

内建Annotation的RetentionPolicy

三个内建的Annotation的定义:

·Override定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

·Deprecated定义采用的是@Retention(value=RUNTIME),可以在执行时出现。

·SuppressWarnings定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

16-3、Annotation——反射与Annotation

一个Annotation如果要想让其变得有意义,则必须结合反射机制取得Annotation中设置的全部内容。

通过反射取得Annotation

在Class类中存在以下几种与Annotation操作有关的方法:

NO

方法

类型

描述

1

public <A extends Annotation> A getAnnotation(Class<A> annotationClass)

普通

如果在一个元素中存在注释,则取得全部注释

2

public Annotation[] getAnnotations()

普通

返回此元素上的所有注释

3

public Annotation[] getDeclaredAnnotations()

普通

返回直接存放在此元素上的所有注释

4

public boolean isAnnotation()

普通

判断元素是否表示一个注释

5

public boolean isAnnotationPresent(Class<? Extends Annotation> annotationClass)

普通

判断一个元素上是否存在注释

例:

package org.lxh.demo16.reflectannotation ;

public class SimpleBeanOne{

@SuppressWarnings("unchecked")

@Deprecated

@Override

public String toString(){

return "Hello LiXingHua!!!" ;

}

};

同时设置了三个Annotation,那么此时注意,只有Depreacted的Annotation的定义范围是RUNTIME范围,所以此时通过反射的话只能取得一个。

import java.lang.annotation.Annotation ;

import java.lang.reflect.Method ;

public class ReflectDemo01{

public static void main(String args[]) throws Exception{ // 所有异常抛出

Class <?> c = null ;

c = Class.forName("org.lxh.demo16.reflectannotation.SimpleBeanOne") ;

Method toM = c.getMethod("toString") ; // 找到toString()方法

Annotation an[] = toM.getAnnotations() ; // 取得全部的Annotation

for(Annotation a:an){ // 使用 foreach输出

System.out.println(a) ;

}

}

};

此时,已经取得了一个Annotation。

以上的操作代码实际上是通过三个系统内建的Annotation完成的,那么也可以自定义一个Annotation。

package org.lxh.demo16.reflectannotation ;

import java.lang.annotation.Retention ;

import java.lang.annotation.RetentionPolicy ;

@Retention(value=RetentionPolicy.RUNTIME) // 此Annotation在类执行时依然有效

public @interface MyDefaultAnnotationReflect{

public String key() default "LXH" ;

public String value() default "李兴华" ;

}

以上的Annotation范围是在运行时依然有效,下面定义一个类使用此Annotation。

package org.lxh.demo16.reflectannotation ;

public class SimpleBeanTwo{

@SuppressWarnings("unchecked")

@Deprecated

@Override

@MyDefaultAnnotationReflect(key="MLDN",value="www.mldnjava.cn")

public String toString(){

return "Hello LiXingHua!!!" ;

}

};

下面使用反射取得指定的Annotation,因为现在唯一设置内容的就是MyDefaultAnnotationRelfect.

import org.lxh.demo16.reflectannotation.MyDefaultAnnotationReflect ;

import java.lang.annotation.Annotation ;

import java.lang.reflect.Method ;

public class ReflectDemo02{

public static void main(String args[]) throws Exception{ // 所有异常抛出

Class <?> c = null ;

c = Class.forName("org.lxh.demo16.reflectannotation.SimpleBeanTwo") ;

Method toM = c.getMethod("toString") ; // 找到toString()方法

if(toM.isAnnotationPresent(MyDefaultAnnotationReflect.class)){

// 判断是否是指定的Annotation

MyDefaultAnnotationReflect mda = null ;

mda = toM.getAnnotation(MyDefaultAnnotationReflect.class) ; // 得到指定的Annotation

String key = mda.key() ; // 取出设置的key

String value = mda.value() ; // 取出设置的value

System.out.println("key = " + key) ;

System.out.println("value = " + value) ;

}

}

};

总结:

1、Annotation在实际的开发中,不管如何使用,其最终肯定是结合反射机制,也就是说可以通过Annotation设置一些内容到一个方法上去,以完成特定的功能。

2、在EJB3.0中可以发现有更多的Annotation应用。

16-4、Annotation——深入Annotation

之前定义的Annotation,如果没有明确的说明可以在任意的位置上使用。

@MyDefaultAnntationReflect(key="MLDN",value="www.mldnjava.cn")

public class SimpleBean{

@MyDefaultAnntationReflect(key="MLDN",value="www.mldnjava.cn")

public String toString(){

return "Hello LiXingHua!!!" ;

}

};

如果现在需要指定其使用的范围,则必须使用@Target注释:

@Documented

@Retention(value=RUNTIME)

@Target(value=ANNOTATION TYPE)

public @interface Target

ElementType的保存范围

NO

范围

描述

1

public static final ElementType ANNOTATION_TYPE

只能用在注释声明上

2

public static final ElementType CONSTRUCTOR

只能用在构造方法声明上

3

public static final ElementType FIELD

只能用在字段声明(包括枚举常量)上

4

public static final ElementType LOCAL_VARLABLE

只能用在局部变量声明上

5

pubic static final ElementType METHOD

只能用在方法的声明上

6

public static final ElementType PACKAGE

只能用在包的声明上

7

public static final ElementType PARAMETER

只能用在参数的声明上

8

public static final ElementType TYPE

只能用在类、接口、枚举类型上

现在定义一个Annotation,只能在类上使用,类型是TYPE:

import java.lang.annotation.Target ;

import java.lang.annotation.ElementType ;

import java.lang.annotation.Retention ;

import java.lang.annotation.RetentionPolicy ;

@Target(ElementType.TYPE) // 此注释只能用在类上

@Retention(value=RetentionPolicy.RUNTIME)

public @interface MyTargetAnntation{

public String key() default "LXH" ;

public String value() default "李兴华" ;

}

此时在SimpleBean的类及方法的声明上使用此Annotation:

@MyTargetAnntation(key="MLDN",value="www.mldnjava.cn")

public class SimpleBean{

@MyTargetAnntation(key="MLDN",value="www.mldnjava.cn")

public String toString(){

return "Hello LiXingHua!!!" ;

}

};

如果现在希望一个Annotation可以在类及方法上同时使用,则必须设置多个范围:

import java.lang.annotation.Target ;

import java.lang.annotation.ElementType ;

import java.lang.annotation.Retention ;

import java.lang.annotation.RetentionPolicy ;

@Target({ElementType.TYPE,ElementType.METHOD}) // 此注释只能用在类上

@Retention(value=RetentionPolicy.RUNTIME)

public @interface MyTargetAnntation{

public String key() default "LXH" ;

public String value() default "李兴华" ;

}

@Documented,可以用在任何Annotation上使用。所有的Annotation默认情况下都是使用@Documented进行注释的,而且在生成javadoc的时候可以通过@Documented设置一些说明信息。

import java.lang.annotation.Documented ;

@Documented

public @interface MyDocumentedAnntation{

public String key() default "LXH" ;

public String value() default "李兴华" ;

}

成功之后,在使用此Annotation的时候就可以增加一些信息上去。

@MyDocumentedAnntation(key="MLDN",value="www.mldnjava.cn")

public class SimpleBeanDocumented{

/**

* 此方法在对象输出时调用,返回对象信息

*/

@MyDocumentedAnntation(key="MLDN",value="www.mldnjava.cn")

public String toString(){

return "Hello LiXingHua!!!" ;

}

};

之后通过javadoc命令,生成java doc文档。

F:\网页\李兴华\031604_【第16章:Annotation】_深入Annotation\代码>javadoc -d doc

SimpleBeanDocumented.java

正在加载源文件SimpleBeanDocumented.java...

正在构造 Javadoc 信息...

标准 Doclet 版本 1.7.0_01

正在构建所有程序包和类的树...

正在生成doc\SimpleBeanDocumented.html...

正在生成doc\package-frame.html...

正在生成doc\package-summary.html...

正在生成doc\package-tree.html...

正在生成doc\constant-values.html...

正在构建所有程序包和类的索引...

正在生成doc\overview-tree.html...

正在生成doc\index-all.html...

正在生成doc\deprecated-list.html...

正在构建所有类的索引...

正在生成doc\allclasses-frame.html...

正在生成doc\allclasses-noframe.html...

正在生成doc\index.html...

正在生成doc\help-doc.html...

@Inherited注释,此注释表示一个Annotation是否可以被继承下来。

package org.lxh.demo16.inheriteddemo ;

import java.lang.annotation.Retention ;

import java.lang.annotation.RetentionPolicy ;

import java.lang.annotation.Documented ;

import java.lang.annotation.Inherited ;

@Documented

@Inherited

@Retention(value=RetentionPolicy.RUNTIME)

public @interface MyInheritedAnnotation{

public String name() ;

}

定义一个父类,在父类上使用此Annotation:

package org.lxh.demo16.inheriteddemo ;

@MyInheritedAnnotation(name=”cyg”)

public class Person{

}

定义子类:

public class Student extends Person{

}

按照所解释的,使用Inherited声明的Annotation是可以被子类继承下来的。

import java.lang.annotation.Annotation ;

import org.lxh.demo16.inheriteddemo.MyInheritedAnnotation ;

public class ReflectInheritedDemo{

public static void main(String args[]) throws Exception{

Class<?> c = null ;

c = Class.forName("org.lxh.demo16.inheriteddemo.Student") ;

Annotation ann[] = c.getAnnotations() ; // 取得全部的Annotation

for(Annotation a:ann){ // 输出

System.out.println(a) ;

}

// 继续取得此Annotation设置的内容

if(c.isAnnotationPresent(MyInheritedAnnotation.class)){

MyInheritedAnnotation mda = null ;

mda = c.getAnnotation(MyInheritedAnnotation.class) ;

String name = mda.name() ; // 取出name的内容

System.out.println("name = " + name) ;

}

}

}

总结:

·熟悉Documented注释的作用:加入说明信息。

·熟悉Target的作用,并使用Target注释指定注释的使用位置。

·如果一个Annotation要想被子类继承下来,则使用Inherited注释说明。

·反射机制对于操作Annotation是最重要的。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表