标签:style blog http color java 使用 os io
Java核心——注解
注解是jdk5以后的新特性,Spring和Hibernate等框架提供了注解的配置方式使用,
本文参考了浪曦风中叶的注解讲解,主要讲解jdk内置注解的用法,注解的声明和定义,以及自定义注解的用法
在开始,说一个小小的笑话:
<!-- start -->
昨晚梦见男朋友和别的女人在逛街,梦里我的第一反应是查源代码…
结果调试半天查不出来为什么显示的是那个女人不是我,
最后含泪把那个女人给注释掉了,再一运行就是我男朋友自己逛街了…
醒来囧字脸呆了很久…囧rz
评论:
1L
把那个女人的指针指向你即可
2L
谁让你把男朋友设成public的
3L
加个断点看看那女人是谁
4L
心真软,就该把他的接口屏蔽掉。//是我想多了么
5L
protected 逛街(youOnly)
6L
设计问题,应该采用单例模式
7L
没做回归测试
8L
标准做法是做个断言
9L
注释掉了逛街的参数不用改么
10L
不要忘记GC谢谢
11L
查一下Log,只逛街了吗/.
**看到一个最狠的回复:
恩,上绝招,用goto,做个死循环,让他们逛死;
<!-- END -->
写过java代码的很多程序员都是用过注解,但是也只是知道一些皮毛
比如:注释是给人看的,注解是给编译器看的;前面加一个@就可以了
一般常用的也就是@Override,@SuppressWarnings之类的注解
下面,我们会给出详细说明:
在java应用中,我们常遇到一些使用模板代码的情况
例如,为了编写一个web service,
我们必须提供一对接口和实现作为模板代码
如果使用annotation对远程访问的方法进行修饰的话
这个模板就能够使用工具自动生成
另外,一些API需要使用与程序代码同时维护附属文件
例如,EJB需要一个部署描述符。此时在程序中
使用annotation来维护这些附属文件的信息,将十分便利而且减少了错误
jdk内置的注解,Override,Deprecated,SuppressWarnings
@Override,强制去检查子类的方法覆盖了父类的方法
/Annotation/src/yuki/corejava/annotation/jdk5/OverrideTest.java
下面的类重写了父类Object中的toString()方法
package yuki.corejava.annotation.jdk5; public class OverrideTest { @Override public String toString() { return "This is override"; } public static void main(String[] args) { OverrideTest test = new OverrideTest(); System.out.println(test.toString()); } }
从Java5.0版发布以来,5.0平台提供了一个正式的annotation功能
允许开发者定义,使用自己的annotation类型
此功能由一个定义annotation类型的语法和一个描述annotation声明的语法
读取annotation的API,
一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成
annotation并不直接影响代码的语义,
但是它能够工作的方式被看作类似程序的工具或者类库
它会反过来对正在运行的程序语义有所影响
annotation可以从源文件、class文件或者以正在运行时的反射的多种方式被读取
当然,annotation在某种程度上使javadoc tag更加完整
一般情况下,如果这个标记对java文档产生影响或者用于生成java文档的话
它应该作为一个javadoc tag,否则将作为一个annotation
限定Override父类方法 @Override
java.lang.Override是一个marker annotation
它的作用是让编译器检查下面的方法一定是重写方法,否则编译会报错
对编译器说明某个方法已经不建议使用 @Deprecated
java.lang.Deprecated是一个marker annotation
/Annotation/src/yuki/corejava/annotation/jdk5/DeprecatedTest.java
package yuki.corejava.annotation.jdk5; public class DeprecatedTest { @Deprecated public void doSomeThing() { System.out.println("do some thing"); } public static void main(String[] args) { DeprecatedTest test = new DeprecatedTest(); test.doSomeThing(); } }
当子类继承父类后,重写父类的过时方法,开发环境会报出警告
/Annotation/src/yuki/corejava/annotation/jdk5/SubDeprecatedTest.java
package yuki.corejava.annotation.jdk5; public class SubDeprecatedTest extends DeprecatedTest { @SuppressWarnings("deprecation") @Override public void doSomeThing() { System.out.println("do some thing in sub class"); } }
如果没有报出警告,可以在勾选下面的复选框
压制编译程序警告 @SuppressWarnings
对编译程序说明某个方法中若有警告讯息,则加以抑制
/Annotation/src/yuki/corejava/annotation/jdk5/SuppressWarningsTest.java
package yuki.corejava.annotation.jdk5; import java.util.Date; import java.util.Map; import java.util.TreeMap; public class SuppressWarningsTest { @SuppressWarnings({ "unchecked", "rawtypes", "deprecation" }) public static void main(String[] args) { Map map = new TreeMap(); map.put("hello", new Date()); System.out.println(map.get("hello")); DeprecatedTest test = new DeprecatedTest(); test.doSomeThing(); } }
二、自定义Annotation
@interface可以定义Annotation类型
定义好的Annotation可以放在方法前或类前或属性上
定义一个带属性的Annotation,定义一个属性value
如果属性名不是value,在使用时必须显式定义
字符串可以看做字符串数组中的一个特例,所以可以复制给数组
这里就像接口定义一样,只有声明没有实现
给字符串一个默认值
/Annotation/src/yuki/corejava/annotation/define/p1/AnnotationTest.java
package yuki.corejava.annotation.define.p1; public @interface AnnotationTest { // String value1(); // String value(); // String[] value(); String value() default "default string"; }
/Annotation/src/yuki/corejava/annotation/define/p1/AnnotationUsage.java
package yuki.corejava.annotation.define.p1; public class AnnotationUsage { // @AnnotationTest(value1 = "hello world") // @AnnotationTest("hello world") @AnnotationTest public void method(){ System.out.println("usage of annotation"); } public static void main(String[] args) { AnnotationUsage usage = new AnnotationUsage(); usage.method(); } }
可以使用枚举来定义一些固定的备选值,枚举可以定义在方法中
这里,为了简便起见,定义了一个公开的枚举
/Annotation/src/yuki/corejava/annotation/define/p2/EnumTest.java
package yuki.corejava.annotation.define.p2; public enum EnumTest { Hello, World, Welcome }
/Annotation/src/yuki/corejava/annotation/define/p2/AnnotationTest.java
package yuki.corejava.annotation.define.p2; public @interface AnnotationTest { EnumTest value1() default EnumTest.Hello; }
/Annotation/src/yuki/corejava/annotation/define/p2/AnnotationUsage.java
package yuki.corejava.annotation.define.p2; public class AnnotationUsage { // @AnnotationTest(value1 = EnumTest.Hello) @AnnotationTest public void method(){ System.out.println("usage of annotation"); } public static void main(String[] args) { AnnotationUsage usage = new AnnotationUsage(); usage.method(); System.out.println(EnumTest.Hello); } }
主函数的运行结果输出在控制台上的内容如下:
usage of annotation Hello
使用 @interface自定义Annotation型态时,
实际上是自动隐式继承了java.lang.annotation.Annotation接口
由编译程序自动完成其它产生的细节
如果显式的继承了Annotation接口得接口,它只是一个普通的接口
注解类型的声明不能有显式的继承父接口
告知编译程序如何处理 @Retention
java.lang.annotation.Retention型态可以在定义Annotation型态时
指示编译程序该如何对待您的自定义的Annotation型态
预设编译程序将Annotation信息留在.class档案中
但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息
在使用Retention型态时,
需要提供java.lang,annotation.RetentionPolicy的枚举型态
public enum RetentionPolicy{
SOURCE, //编译程序处理完Annotation就完成任务
CLASS, //编译程序将Annotation储存在class当中,缺省
RUNTIME //编译程序将Annotation储存于class当中,可由VM读入
}
/Annotation/src/yuki/corejava/annotation/define/MyAnnotation.java
package yuki.corejava.annotation.define; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String hello() default "default hello"; String world(); }
RetentionPolicy为SOURCE的例子是 @SuppressWarnings
仅在编译时告知编译程序来抑制警告,所以不必将这个信息储存在.class档案
RetentionPolicy为RUNTIME的时机,
可以像是使用Java设计一个程序代码分析工具
必须让VM读出Annotation信息,以便在流程分析程序时使用
搭配反射机制,就可以达到这个目的
运行时把Annotation的信息读取出来
通过反射的方式获得注解的属性
这些在Hibernate和Spring中都有使用,通过注解就可以轻松配置
获得方法前的所有注解遍历
/Annotation/src/yuki/corejava/annotation/define/MyReflection.java
package yuki.corejava.annotation.define; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class MyReflection { public static void main(String[] args) throws Exception { MyTest myTest = new MyTest(); Class<MyTest> c = MyTest.class; Method method = c.getMethod("output", new Class[]{}); if(method.isAnnotationPresent(MyAnnotation.class)){ method.invoke(myTest, new Object[]{}); MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class); String hello = myAnnotation.hello(); String world = myAnnotation.world(); System.out.println(hello); System.out.println(world); } Annotation[] annotations = method.getAnnotations(); for(Annotation annotation : annotations){ System.out.println(annotation.annotationType().getName()); } } }
/Annotation/src/yuki/corejava/annotation/define/MyTest.java
package yuki.corejava.annotation.define; public class MyTest { @MyAnnotation(hello = "beijing" ,world = "shanghai") @Deprecated // @SuppressWarnings("unchecked") public void output(){ System.out.println("output something"); } }
运行结果如下,拿到了注解中属性的右值——"beijing","shanghai"
遍历除了这个方法上所有的RUNTIME的注解
output something beijing shanghai yuki.corejava.annotation.define.MyAnnotation java.lang.Deprecated
告知编译程序如何处理 @Retention
java.lang.reflect.AnnotatedElement接口
定义Annotation时必须设定RetentionPolicy为RUNTIME
就可以在VM中读取Annotation信息
因为 @SuppressWarnings 有 @Retention(RetentionPolicy.SOURCE)
所以在编译后,没有存在
当Retention的值为RetentionPolicy.CLASS时
注解的信息只会在.class文件中存在,而并不会被读出
/Annotation/src/yuki/corejava/annotation/define/p4/EnumTest.java
package yuki.corejava.annotation.define.p4; public enum EnumTest { HELLO, WORLD, welcome }
/Annotation/src/yuki/corejava/annotation/define/p4/AnnotationTest.java
package yuki.corejava.annotation.define.p4; public @interface AnnotationTest { EnumTest value1() default EnumTest.HELLO; String value2(); Class<?> c(); // Date date(); }
当声明Date时,编译器提示出如下结果:
Invalid type Date for the annotation attribute AnnotationTest.date; only primitive type, String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof
Invalid type Date for the annotation attribute AnnotationTest.date;
only primitive type, String, Class, annotation, enumeration
are permitted or 1-dimensional arrays thereof
就是说,只能定义基本类型,字符串,类,注解,枚举,以及他们的一维数组
三、Target, Documented, Inherited
限定annotation适用对象的 @Target
使用java.lang.annotation.Target可以定义其使用的时机
如果不加目标,注解就可以定义在任何地方
在定义时要指定java.lang.annotation.ElementType的枚举值之一
public enum ElementType{
TYPE, //class, interface, enum
METHOD, //method
PARAMETER, //parameter of method
CONSTRUCTOR, //constructor
LOCAL_VARIABLE, //local varible
ANNOTATION_TYPE,//annotation
PACKAGE //package
}
如果注解的位置没有放对的话,就会给出如下警告
The annotation @MyTarget is disallowed for this location
/Annotation/src/yuki/corejava/annotation/define/target/MyTarget.java
package yuki.corejava.annotation.define.target; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface MyTarget { String value(); }
/Annotation/src/yuki/corejava/annotation/define/target/MyTargetTest.java
package yuki.corejava.annotation.define.target; //@MyTarget("value") public class MyTargetTest { @MyTarget("value") public void doSomeThing(){ System.out.println("do some thing"); } }
要求为API文件 @Documented
想要在使用者制作JavaDoc文件的同时
也一并将Annotation的讯息加入至API中
使用java.lang.annotation.Documented
/Annotation/src/yuki/corejava/annotation/define/doc/DocumentAnnotation.java
package yuki.corejava.annotation.define.doc; import java.lang.annotation.Documented; @Documented public @interface DocumentAnnotation { String hello(); }
/Annotation/src/yuki/corejava/annotation/define/doc/DocumentTest.java
package yuki.corejava.annotation.define.doc; public class DocumentTest { /** * This is comments that I have added */ @DocumentAnnotation(hello = "welcome") public void method(){ System.out.println("hello world"); } }
点击生成javadoc,生成的文件如下:
子类是否继承父类 @Inherited 。。。?
预设父类别中的Annotation并不会被继承至子类别中
可以在定义Annotation型态时加上
java.lang.annotation.Inherited型态的Annotation
类的注解和方法的注解都可以继承到子类
/Annotation/src/yuki/corejava/annotation/define/doc/InheritedTest.java
package yuki.corejava.annotation.define.doc; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface InheritedTest { String value(); }
/Annotation/src/yuki/corejava/annotation/define/doc/Parent.java
package yuki.corejava.annotation.define.doc; @InheritedTest("welcome") public class Parent { public void doSomeThing(){ System.out.println("hello"); } }
/Annotation/src/yuki/corejava/annotation/define/doc/Child.java
package yuki.corejava.annotation.define.doc; public class Child extends Parent { }
/Annotation/src/yuki/corejava/annotation/define/doc/Test.java
package yuki.corejava.annotation.define.doc; public class Test { public static void main(String[] args) { Class<Child> c = Child.class; // Class<Parent> c = Parent.class; if(c.isAnnotationPresent(InheritedTest.class)){ InheritedTest inheritedTest = c.getAnnotation(InheritedTest.class); String value = inheritedTest.value(); System.out.println(value); } } }
运行结果如下:
welcome
如果子类重写父类的方法,父类方法中的注解也会被覆盖掉
/Annotation/src/yuki/corejava/annotation/define/inherit/InheritedTest.java
package yuki.corejava.annotation.define.inherit; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface InheritedTest { String value(); }
/Annotation/src/yuki/corejava/annotation/define/inherit/Parent.java
package yuki.corejava.annotation.define.inherit; public class Parent { @InheritedTest("welcome") public void doSomeThing(){ System.out.println("parent do some thing"); } }
/Annotation/src/yuki/corejava/annotation/define/inherit/Child.java
package yuki.corejava.annotation.define.inherit; public class Child extends Parent { @Override public void doSomeThing(){ System.out.println("child do some thing"); } }
/Annotation/src/yuki/corejava/annotation/define/inherit/Test.java
package yuki.corejava.annotation.define.inherit; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { Class<Child> c = Child.class; Method method = c.getMethod("doSomeThing", new Class[]{}); if(method.isAnnotationPresent(InheritedTest.class)){ InheritedTest inheritedTest = method.getAnnotation(InheritedTest.class); String value = inheritedTest.value(); System.out.println(value); } } }
运行时在控制台上没有输出。即父类的注解也被覆盖了
只会在继承父类时继承,而不会在实现接口时继承
/Annotation/src/yuki/corejava/annotation/define/inherit/ParentInterface.java
package yuki.corejava.annotation.define.inherit; @InheritedTest("welcome") public interface ParentInterface { }
/Annotation/src/yuki/corejava/annotation/define/inherit/ChildClass.java
package yuki.corejava.annotation.define.inherit; public class ChildClass implements ParentInterface { }
/Annotation/src/yuki/corejava/annotation/define/inherit/TestInterface.java
package yuki.corejava.annotation.define.inherit; public class TestInterface { public static void main(String[] args) throws Exception { Class<ChildClass> c = ChildClass.class; if(c.isAnnotationPresent(InheritedTest.class)){ InheritedTest inheritedTest = c.getAnnotation(InheritedTest.class); String value = inheritedTest.value(); System.out.println(value); } } }
运行结果也是没有任何结果,说明接口的注解不能被实现类继承。
更多精彩文章请查看:http://www.cnblogs.com/kodoyang/
孔东阳
2014/8/13
标签:style blog http color java 使用 os io
原文地址:http://www.cnblogs.com/kodoyang/p/CoreJava_Annotation.html