Java注解

Java提供的5个基本Annotation

  • @Override
    用于限定重写父类方法,在编译时检查父类是否包含被重写的方法。主要帮助程序员避免一些低级错误。只能修饰方法(修饰范围将在后面的元注解中解释)。
public class Fruit {
    
    public void info(){
        System.out.println("水果的Info方法...");
    }

}

class Apple extends Fruit{

    @Override
    public void info() {
        System.out.println("重写水果的Info方法...");
    }
    
}

上面的例子Apple类继承了Fruit类并重写了Fruit中的info()方法,其中@Override是可选的,可以不写。写上的好处有两个:
①告诉阅读代码的人这个方法是重写父类的方法;
②避免开发人员本想重写父类方法,确因拼写错误而导致错误,例如info()写成inf0(),这种错误后期也是很难发现的。

  • @Deprecated
    用于表示某个程序元素已过时,不推荐使用,后续迭代版本有可能去掉元素,当其他程序使用已过时的类、方法时编译器就会发出警告。

上面的例子Apple类的info()方法使用@Deprecated标记,在使用时eclipse会将方法名加上中划线告诉开发人员此方法已过时。细心的读者肯定会注意到在文档注释中也有一个@deprecated,它们的作用基本相同,但是前者是jdk5才支持的注解,例如:

class Apple extends Fruit{
    
    /**
     * @deprecated 此方法以过时, 由 Apple.info2() 替代
     */
    @Deprecated
    @Override
    public void info() {
        System.out.println("重写水果的Info方法...");
    }
    
    public void info2() {
        System.out.println("苹果的Info方法...");
    }
    
}

可以理解为 @deprecated 是给javadoc生成文档使用的,而 @Deprecate 则是给编译器看得。

  • @SuppressWarnings
    抑制警告,如下代码myList没有声明泛型,会有警告,使用@SuppressWarnings("rawtypes")这个注解后警告就不再提示了。
@SuppressWarnings("rawtypes")
public class SupressWarningsTest {
    
    public static void main(String[] args) {
        List myList = new ArrayList<>();
        System.out.println(myList);
    }
}

这个注解个人不推荐使用,警告本身说明程序可能会存在风险,尽量通过实际手段消除警告。

  • @SafeVarags(Java7新增)

先看一个例子

//不使用泛型
List list = new ArrayList<Integer>();
        
//添加新元素引发 unchecked 异常 
list.add(20);
        
//下面代码引起“未经检查的转换”的警告,编译、运行时完全正常,
List<String> ls = list; //①
        
//但是访问ls里的元素,如下面的代码就会引起运行时异常 java.lang.ClassCastException
System.out.println(ls.get(0));

Java把引发这种错误的原因称为“堆污染”(heap pollution),当把一个不带泛型的的对象赋值给一个带泛型的变量时,往往就会发生这种“堆污染”,如上面带①的行。

例子:

@SafeVarargs // 不是真的安全
static void m(List<String>... stringLists) {
    Object[] array = stringLists;
    List<Integer> tmpList = Arrays.asList(42);
    array[0] = tmpList; // 语义是无效的, 但编译器无警告
    String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}
  • @FunctionalInterface(Java8新增)

Java8中规定,只有一个抽象方法的接口(可以有其他的方法,但是抽象方法只有一个),称为函数式接口。@FunctionalInterface就是指定某个接口必须是函数式接口。函数式接口就是为Java8 中的Lamda表达式准备的。

@FunctionalInterface
public interface FunInterface {
    
    static void foo(){
        System.out.println("foo");
    }
    
    default void bar(){
        System.out.println("bar");
    }
    
    //这个接口中唯一的抽象方法
    void test();
}

JDK的元注解

一般地,称修饰数据的数据为元数据,自然元注解是用来修饰注解的。

  • @Retention
    该注解只能用来修饰Annotation的定义,用于指定被修饰的Annotation可以保留多长时间。
取值 含义
RetentionPolicy.SOURCE 只保留在源码中,编译器直接丢弃这种类型的注解
RetentionPolicy.CLASS 编译器把这类注解保留在class字节码文件中,当运行Java程序时,JVM不可获取这类注解信息,这个默认值
RetentionPolicy.RUNTIME 编译器把这类注解保留在class字节码文件中,当运行Java程序时,JVM可以获取这类注解信息,程序可以通过反射获取这类注解的信息

如果需要通过反射获取注解的信息,需要value属性的值为RetentionPolicy.RUNTIME的@Retention修饰的注解,可以采用如下代码为value赋值。

@Retention(value=RetentionPolicy.RUNTIME)

或者

@Retention(RetentionPolicy.RUNTIME)
  • @Target
    该注解也只能修饰Annotation的定义,用于指定被修饰的Annotation能修饰哪些程序单元,@Target也包含一个名为value的成员变量,该变量的取值只能是如下几个。
取值 可修饰单元类型
ElementType.TYPE 类, 接口 (包括注解类型), 或者枚举定义
ElementType.FIELD 成员变量 (包括枚举常量)
ElementType.METHOD 方法
ElementType.PARAMETER 参数
ElementType.CONSTRUCTOR 构造函数
ElementType.LOCAL_VARIABLE 局部变量
ElementType.ANNOTATION_TYPE 注解类型
ElementType.PACKAGE
ElementType.TYPE_PARAMETER 参数类型, since 1.8
ElementType.TYPE_USE Use of a type, since 1.8
  • @Documented

如果使用了@Documented修饰的注解,该注解会被javac工具提取成文档,API文档中将包含该注解的说明。

  • @Inherited
    使用@Inherited修饰的注解具有继承性。举一个例子
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Inheritable {
    
}
//使用 @Inheritable修饰的基类
@Inheritable
class Base{
    
}

//InheritableTest,只是继承了Base类,并未直接使用@Inheritable这个注解修饰
public class InheritableTest extends Base{
    
    public static void main(String[] args) {
        
        boolean result = InheritableTest.class.isAnnotationPresent(Inheritable.class);
        //打印InheritableTest这个类是否有@Inheritable修饰
        System.out.println(result);
    }
    
}

上面的例子结果为 true,InheritableTest通过继承Base类,把Base上的注解@Inheritable也继承了。

如何定义一个注解

在自定注解前先看看java提供的@Override是如何定义的

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

通过元注解@Target(ElementType.METHOD)表明该注解可修饰方法,@Retention(RetentionPolicy.SOURCE)表明该注解只保留在源码中,编译器会忽略掉,不会出现在.class字节码文件中。

下面定义一个MyAnnotation,包含一个value属性,并且演示如何通过反射获取注解的信息。

自定义注解通过@interface关键字来定义,非常类似于接口的定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    //声明一个属性value,并设置默认值hello,在使用注解时没有指定值就会使用这个默认值
    String value() default "hello";
}

可以理解为方法的返回值类型就是属性的数据类型,方法名就是属性名。

提取注解信息

使用注解修饰某些程序元素之后,注解不会自己生效,必须由开发者提供相应的工具来读取并处理注解信息。从Java5开始,java.lang.reflect包反射API提供了读取运行时注解的能力。注意:只有定义注解时使用@Retention(RetentionPolicy.RUNTIME)修饰的的注解才能在运行时可见。

Java 5 在 java.lang.reflect包下新增了AnnotatedElement接口,这个接口的实现类主要包括

  • Class: 类定义
  • Constructor: 构造器定义
  • Field: 类的成员变量定义
  • Method: 方法的定义
  • Package: 包的定义

程序可调用AnnotatedElement接口下的方法来访问注解信息,这个接口包含如下方法(jdk 1.8)。

//判断该程序元素上是否存在指定的注解,如果存在返回true,否则返回false.
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }
//返回该程序元素上指定类型的注解,否则返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
//返回该程序元素上的注解数组,如果没有则返回一个长度为0的数组
Annotation[] getAnnotations();
//这个方法与<T extends Annotation> T getAnnotation(Class<T> annotationClass)的功能基本相似,但是java8增加了重复注解,因此需要使用该方法获取修饰该元素的指定类型的多个注解。

default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
//返回直接修饰该程序元素的注解数组,如果没有则返回长度为0的数组
Annotation[] getDeclaredAnnotations();
//返回直接修饰该程序元素的指定类型注解的数组,如果没有则返回长度为0的数组
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
//该方法与default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)基本相似,但是java8增加了重复注解,因此需要使用该方法获取修饰该元素的指定类型的多个注解。
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)

下面使用前面定义的MyAnnotation,并通过反射获取其value值

class Orange {
    
    @MyAnnotation(value="I am an orange...")
    public void info(){
        System.out.println("橙子");
    }
}
public class MyAnnotationTest {
    
    public static void main(String[] args) {
        try {
            Annotation[] ans = Class.forName("com.jdqm.annotation.test.Orange").getMethod("info").getAnnotations();
            for(Annotation an: ans) {
                System.out.println(((MyAnnotation)an).value());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印结果

I am an orange...

这个例子中我们在使用注解的时候设置了value的值为“I am an orange...”,如果不设置的话就是使用默认的hello。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容