Java核心 --- 注解

时间:2023-03-08 19:02:04

Java核心——注解

注解是jdk5以后的新特性,Spring和Hibernate等框架提供了注解的配置方式使用,

本文参考了浪曦风中叶的注解讲解,主要讲解jdk内置注解的用法,注解的声明和定义,以及自定义注解的用法

写过java代码的很多程序员都是用过注解,但是也只是知道一些皮毛

比如:注释是给人看的,注解是给编译器看的;前面加一个@就可以了

一般常用的也就是@Override,@SuppressWarnings之类的注解

下面,我们会给出详细说明:

一、JDK5后的新特性,Annotation

在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");
    }

}

如果没有报出警告,可以在勾选下面的复选框

Java核心 --- 注解

压制编译程序警告 @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");
    }
}

Java核心 --- 注解

点击生成javadoc,生成的文件如下:

Java核心 --- 注解

子类是否继承父类 @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