学习笔记06—注解(Annotation)

来源:互联网 发布:大数据涂子沛 编辑:程序博客网 时间:2024/04/29 08:58


面向对象的封装可以把现实对象的属性封装到对象内部。

个人感觉这样的方式并不能有效的抽象现实。有些对象的属性是易于变换的,并不总是固定在对象内部。

我想到一个现实的例子来理解注解是怎样的一种好的方式:


大兵军服口袋里都有一个番号牌,上面记录着大兵的身份信息。大兵战死后,可以根据这个番号牌确认其身份。

大兵本身具有血型、基因、身高等属性,这些可以封装到对象内部,而大兵所属的部队番号、建制信息相当于标签,可以经常更换,这些功能可以由注解完成。


在程序中需要对一个类进行注解,以增加一些属性时就要用到Annotation.


注解


注解简介


Jdk1.5新特性:注解枚举……

注解是一种标记,为程序打上某种标记。


通过一个实验介绍什么是注解。


 写一个AnnotationTest类


package cn.itcast.day2; public classAnnotationTest {    publicstaticvoidmain(String[] args) {        System.runFinalizersOnExit(true);    } }


在命令提示符窗口编译上面的源文件

会提示下面的信息:

 

注:AnnotationTest.java使用或覆盖了已过时的API。

注: 有关详细信息, 请使用-Xlint:deprecation 重新编译。

 

然后使用javac –Xlint:deprecation AnnotationTest.java编译,

会出现下面的提示:

 

AnnotationTest.java:6:警告: [deprecation]System中的runFinalizersOnExit(boolean)已过时

               System.runFinalizersOnExit(true);

                      ^

1 个警告

 

要“抑制”这样的警告可以在AnnotationTest源文件中加一句:

 

package cn.itcast.day2; public classAnnotationTest {   @SuppressWarnings(value= { "deprecation" })   publicstaticvoidmain(String[] args) {        System.runFinalizersOnExit(true);    } }

 

这样在命令行窗口重新编译就不会出现提示了。

 suppress是“抑制,镇压,取缔”的意思

 

 

@SuppressWarnings(value= { "deprecation" })

 

也可以写成

 

@SuppressWarnings("deprecation")

 

这其中的原因后面会提到


一个注解就是一个类,在这里加注解相当于生成一个注解的实例。

 

再看一个注解。

在AnnotationTest中加一个方法,但后来因为某种原因想弃用这个方法,

为了保证调用该方法的程序仍然能够运行,可以加一个注解,告知消费者方法已经过时

 

package cn.itcast.day2; public classAnnotationTest {   @SuppressWarnings(value= { "deprecation" })   publicstaticvoidmain(String[] args) {        System.runFinalizersOnExit(true);    }   @Deprecated   publicstaticvoidsayHi() {        System.out.println("Hi");   }}


 

这样在别处调用sayHi()方法的时候会提示已过时。

在Eclipse中是以删除线的方式表示的。

 

package cn.itcast.day2; public classTempTest {    publicstaticvoidmain(String[] args) {        AnnotationTest.sayHi();   } }

 

再来看一个注解Override

重写时添加该注解,可以防止写错方法参数,造成实际上没有重写

例如重写Object.equals(Object obj)方法时,错把参数写成其他类型,相当于没有覆盖。

如果没有覆盖,Eclipse中不会出现绿三角的提示图标。

为了避免这样的错误,在重写时先加一个@Override

这样会检查方法签名是否与父类方法一致,如果不一致会提示错误。

 

总结:

 

注解是一种标记,为程序打上某种标记。

编译器或者其他工具可以通过反射来了解你的元素上加的标记类型,从而进行某种处理

类、包、成员变量、方法参数、局部变量都可以加注解

 

注解的反射调用

---------------------------------------------------------------------------------------------------

 

注解的应用结构图


---------------------------------------------------------------------------------------------------

 

第一步:设计注解类

 

新建一个注解类

 

package cn.itcast.day2; public @interfaceMyAnnotation{ }


---------------------------------------------------------------------------------------------------

 

第二步:应用注解类

 

在AnnotationTest类上加上@MyAnnotation


@MyAnnotation

public classAnnotationTest {

//……

 

---------------------------------------------------------------------------------------------------

 

第三步:下一级消费者使用注解类

 

首先在TempTest中检测一个类有无注解


package cn.itcast.day2;  public classTempTest {    publicstaticvoidmain(String[] args) {        AnnotationTest.sayHi();        // 可以查看MyAnnotation的属性是否是Annotation        boolean isAnno = MyAnnotation.class.isAnnotation();         // 对应用了MyAnnotation注解的类——AnnotationTest类进行反射操作        // 查看AnnotationTest是否加了注解        boolean flag = AnnotationTest.class.isAnnotationPresent(MyAnnotation.class);         System.out.println(flag);    } }

 

出乎意料,结果是false。


明明在AnnotationTest类上注明类@MyAnnotation,为什么检测不到?

 

这里牵扯到Annotation的生命周期的问题

 

------------------元注解Retention---------------------------------------------------------------------------------

 

我们需要对MyAnnotation本身进行一些设置。如何设置?答案竟是——再加注解。

对注解进行注解的注解类叫元注解

 

修改后的MyAnnotation如下

 

 

package cn.itcast.day2; import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME)public @interfaceMyAnnotation{ }


Retention是“保留,保持”的意思,就是说该注解要保留到什么时候。

其参数是RetentionPolicy的枚举类型。

SOURCECLASSRUNTIME分别对应源代码文件,class文件和内存中的字节码。

默认值是CLASS.


现在可以解释为什么第三步输出false了:


AnnotationTest 加了@MyAnnotation注解,这个注解只会跟随AnnotationTest保留到class文件阶段

而当AnnotationTestclass文件被载入内存,@MyAnnotation注解就会被丢掉,不会进入运行时状态。

 

@Override的元注解属性是SOURCE

@SuppressWarnings的是SOURCE

@Deprecated的是RUNTIME

可以通过反射方式获得,如果不去看文档的话:

 

String retentionValue = Deprecated.class.getAnnotation(Retention.class).value().name();System.out.println(retentionValue);


 

----------------元注解target-----------------------------------------------------------------------------------

 

下面介绍另一个元注解@target

为MyAnnotation添加另一个元注解

 

@Target(value= { ElementType.TYPE,ElementType.METHOD})


 

参数表示MyAnnotation可以加到什么样的元素上

TYPE是个接口

 

修改过MyAnnotation后,第三步就可以检测到AnnotationTest类上存在MyAnnotation注解了

 

注解的属性

注解 我认为可以这么理解注解的实际应用:


大头兵军服口袋里都有一个番号牌,上面记录着大兵的身份信息。

大兵战死后,可以根据这个注解统计信息。。。。

大兵本身具有血型、基因、身高等属性,这些可以封装到对象内部,

而大兵所属的部队番号、建制信息相当于标签,可以经常更换,这些功能可以由注解完成。

注解类似于便利贴。

 

 下面就以大兵的例子设计注解类

---------------------------------------------------------------------------------------------------

第一步:设计注解类

仍然用MyAnnotation.

 

在MyAnnotation里添加属性:

说明:


code属性(或者叫方法)表示大兵所属的编制代号,默认是000

value表示雌雄阴阳男女。。。

arr纯粹为了测试用。。。

 

 package cn.itcast.day2; import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME)public @interfaceMyAnnotation{   String code() default "000";    SEX value() default SEX.MALE;    int[] arr() default{7, 8, 9};}


上面用到的枚举SEX定义如下,其中有个变性方法:

万一大兵死了认不出男女雌雄阴阳,最后通过DNA鉴定发现需要改性别。。。。 

 

public enumSEX {   MALE{        @Override        SEX sexChange() {            // TODO Auto-generated method stub            return FEMALE;        }   }, FEMALE{        @Override        SEX sexChange() {            // TODO Auto-generated method stub            return MALE;        }   };      abstractSEX sexChange();}

关于枚举的设计方法,可以参考我的另一篇笔记枚举,设计一个抽象的方法


 

---------------------------------------------------------------------------------------------------

 

第二步:应用注解类

 

新建一个大兵类,加上注解并添加属性值

如果code取默认属性,则可以直接写属性值

Value属性是个特殊的属性,

当一个类的注解上只有一个value属性需要设定时,可以不写value=,直接写属性值

这就是为什么上面@SuppressWarnings(value= { "deprecation" })

也可以写成@SuppressWarnings("deprecation")的原因。


数组只有一个值得时候可以直接写,当然也可以写成这样:{2}

 

package cn.itcast.day2; //code已经有默认值,所以可以直接写@MyAnnotation( SEX.FEMALE )//arr的属性可以写{1,2},只有一个值时可以直接写2,也可以写作{2}@MyAnnotation(code = "123",value = SEX.FEMALE,arr = 2)public classSoldier { }


 

 

---------------------------------------------------------------------------------------------------

 

第三步:使用

 

public staticvoidmain(String[] args) {    // 对应用了MyAnnotation注解的Soldier类进行反射操作   // 首先判断Soldier是否加了注解   booleanflag = Soldier.class.isAnnotationPresent(MyAnnotation.class);   System.out.println(flag);    if(flag) {       MyAnnotation myAnno = Soldier.class.getAnnotation(MyAnnotation.class);       System.out.println(myAnno.code());       System.out.println(myAnno.value());       System.out.println(myAnno.arr().length);       System.out.println(myAnno.value().sexChange().name());   } }


其中 MyAnnotation myAnno = Soldier.class.getAnnotation(MyAnnotation.class);

本来应该进行转换 MyAnnotation myAnno = (MyAnnotation) Soldier.class.getAnnotation(MyAnnotation.class);

不转换也没有问题 原因可能是自动加了泛型

 

输出结果:

true

123

FEMALE

1

MALE

 

 

最后一点:

注解还可以添加注解类型的属性

可以这么理解:

大兵的番号牌上面不光有所属部队,还写着一句话:请参考另一个口袋里的标签。

 

第一步:设计注解

 

首先新建一个注解叫做AnotherLabel,表示另一个标签

它有一个属性where,表示这个标签放在哪儿。

 

package cn.itcast.day2; public @interfaceAnotherLabel{   String where();}


 

设计注解,这里仍然用MyAnnotation

为MyAnnotation添加一个属性

 

package cn.itcast.day2; import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME)public @interfaceMyAnnotation{    String code() default "000";     SEX value() default SEX.MALE;     int[] arr() default{7, 8, 9};    // 增加一个属性,返回值是另一个标签,默认返回左下口袋里的标签,如果有左下口袋的话。。。。    AnotherLabel label() default @AnotherLabel(where= "lowerLeft");}


 

 

第二步:应用注解

这个兵种的大兵的另一张标签不在左下口袋,而在右下口袋,所以不要默认的


 

package cn.itcast.day2; @MyAnnotation(label = @AnotherLabel(where= "lowerRight"))public classSoldier { }

 

第三步:使用

 

System.out.println(myAnno.label().where());

 

输出lowerRight


Over.