面向对象的封装可以把现实对象的属性封装到对象内部。
个人感觉这样的方式并不能有效的抽象现实。有些对象的属性是易于变换的,并不总是固定在对象内部。
我想到一个现实的例子来理解注解是怎样的一种好的方式:
大兵军服口袋里都有一个番号牌,上面记录着大兵的身份信息。大兵战死后,可以根据这个番号牌确认其身份。
大兵本身具有血型、基因、身高等属性,这些可以封装到对象内部,而大兵所属的部队番号、建制信息相当于标签,可以经常更换,这些功能可以由注解完成。
在程序中需要对一个类进行注解,以增加一些属性时就要用到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的枚举类型。
SOURCE、CLASS和RUNTIME分别对应源代码文件,class文件和内存中的字节码。
默认值是CLASS.
现在可以解释为什么第三步输出false了:
AnnotationTest 加了@MyAnnotation注解,这个注解只会跟随AnnotationTest保留到class文件阶段
而当AnnotationTest的class文件被载入内存,@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.