Java 8 新特性:扩展注解(类型注解和重复注解)

简介: Java 8 新特性:扩展注解(类型注解和重复注解)

注解


(注:先回顾下JDK1.5版本出现的注解 ,然后再解释JDK 8的注解 更新内容。)


一.注解(JDK1.5)


1.注解(@):


注解就相当于一种标记,在程序中加了注解就等于为程序加了某种标记。(JDK1.5新特性)。


2.作用:


告诉javac编译器或者java开发工具……向其传递某种信息,作为一个标记。


3.了解注解:


一个注解就是一个类。

标记 可以加在包、类、字段、方法,方法参数以及局部变量上。 可以 同时 存在 多个注解。

每一个注解 结尾都没有“ ; ” 或者 其他 特别 符号 。

eg:

@SuppressWarnings("deprecation")  //编译器警告过时(source阶段)
@Deprecated             //过时(Runtime阶段)
@Override               //重写(source阶段)
@Retention(RetentionPolicy.RUNTIME) 
//保留注解到程序运行时。(Runtime阶段)
@Target({ElementType.METHOD,ElementType.TYPE})
//标记既能定义在方法上,又能定义在类、接口、枚举上等。

N ote :

1) 添加 注解需要有注解类 。RetentionPolicy 是 一个枚举类 (有 三个成员)。

2) Target 中可以存放数组。 它 的默认值为任何元素。

ElementType.METHOD:表示只能标记在方法上。


ElementType.TYPE:表示只能标记定义在类上、接口上、枚举上等。


3)ElementType也是枚举类。成员包括:ANNOTATION_TYPE(注解)、CONSTRUCTOR(构造方法)、FIEID(成员变量)、LOCAL_VARIABLE(变量)、METHOD(方法)、PACKAGE(包)、PARAMETER(参数)、TYPE。


4.注解应用结构图:

image.png

5.简单相关知识了解:


元注解 :注解的注解(理解:给一个注解类再加注解)

元数据:数据的数据


元信息:信息的信息



6.注解分为三个阶段:


java源文件--> class文件 --> 内存中的字节码。


Retention的注解有三种取值 :(分别对应注解的三个阶段)

RetentionPolicy. SOURCE

RetentionPolicy. CLASS

RetentionPolicy. RUNTIME


Note:注解的默认阶段是Class。



7.注解的属性类型:



原始类型(就是八个基本数据类型)、String类型、Class类型、数组类型、枚举类型、注解类型。



8.为注解增加属性:


value: 是一个特殊的属性,若在设置值时只有一个value属性需要设置或者其他属性都采用默认值时 ,那么 value = 可以省略,直接写所设置的值即可。

eg:@SuppressWarnings("deprecation")


为属性指定 缺省值 (默认值):

e g : String value() default   "blue" ; //定义在注解类中

数组 类型的属性:

e g : int [] arrayArr()  default  {3,4,5,5};//定义在注解类中

SunAnnotation (arrayArr={3,9,8}) //设置数组值

Note: 如果数组属性中只有一个元素时,属性值部分可以省略大括号。

e g : SunAnnotation (arrayArr=9)

枚举 类型的属性:

e g : EnumDemo.TrafficLamp lamp()

default  EnumDemo.TrafficLamp. RED ;


//枚举类型属性, 定义在注解类中,这里使用了自定义的枚举类EnumDemo.java并没有给出相关代码,这里只是举个例子


注解 类型的属性:

e g : MetaAnnotation annotationAttr()


default   @MetaAnnotation ( "lhm" );

//定义在一个注解类中,并指定缺省值,

//此属性关联到注解类:MetaAnnotation.java,

@SunAnnotation (annotationAttr= @MetaAnnotation ( "flx" ))

//设置注解属性值


9.注解应用示例:


(注:下面只是示例,涉及枚举的类,并没有给出相关代码。)


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)     //元注解:注解的注解。此注解表示使注解保留到运行时。
//Target中可以存放数组。ElementType.METHOD:表示只能标记在方法上。ElementType.TYPE:表示只能标记定义在类上、接口上、枚举上等。
@Target({ElementType.METHOD,ElementType.TYPE})  
public @interface SunAnnotation {
  String color() default "blue"; //表示有一个color属性,以方法的形式。设置默认属性值为蓝色
  String value();   
  //是一个特殊的属性,若在设置值时只有一个value属性需要设置 ,那么value可以省略,直接写所设置的值即可。
  //数组属性
  int[] arrayArr() default {3,4,5,5};
  //枚举类型属性,这里使用了自定的枚举类EnumDemo.java
  EnumDemo.TrafficLamp lamp() default EnumDemo.TrafficLamp.RED;
  //注解类型属性,此时关联到注解类:MetaAnnotation.java,并指定缺省值
  MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
  //Class类属性:设置默认值 ReflectDemo.java 类
  Class annotationClass() default ReflectDemo.class;
}
@SunAnnotation(annotationClass=ReflectDemo2.class,annotationAttr=@MetaAnnotation("flx"),color="red",value="abc",arrayArr={1,3,9})   //注解:为注解添加属性值
//若在设置值时只有一个value属性需要设置 ,那么value可以省略,直接写所设置的值即可。
public class AnnotationDemo {
  /**
   * @param args
   */
  @SunAnnotation("xyz")
  @SuppressWarnings("deprecation")  //注解:告诉编译器或者开发工具……
  //若在设置值时只有一个value属性需要设置 ,那么value可以省略,直接写所设置的值即可。
  //上面"deprecation"即是省略的value后的值。
  public static void main(String[] args)throws Exception {
    System.runFinalizersOnExit(true);//中间带横线的部分,说明该方法已经过时了。
    //检查某注解是否存在,使用反射;并返回该注解
    if(AnnotationDemo.class.isAnnotationPresent(SunAnnotation.class))
    {
      //获取注解
      SunAnnotation annotation=(SunAnnotation)AnnotationDemo.class.getAnnotation(SunAnnotation.class);
      //获取属性值
      System.out.println("属性color值:"+annotation.color());//调用color属性方法
      System.out.println(annotation.value());
      System.out.println(annotation.arrayArr().length);//这里输出的是数组长度,不能直接输出数组的每一个值,调用得到的是数组哈希地址值
      System.out.println(annotation.lamp().nextLamp().name());//调用注解默认灯的nextLamp方法
      annotation.annotationAttr().value();
      //获取类类型注解中的 类名称
      System.out.println(annotation.annotationClass());
    }
  }
  @Deprecated //表示下面的方法过时了
  public static void sayHello()
  {
    System.out.println("hi");
  }
}

二.扩展注解(JDK 8)


对于注解(也被称做元数据),Java 8 主要有两点改进:类型注解和重复注解。


1.类型注解


1)Java 8 的类型注解扩展了注解使用的范围。


在java 8之前,注解只能是在声明的地方所使用,java8开始,注解可以应用在任何地方。


eg:


创建类实例

new@Interned MyObject();

类型映射

myString = (@NonNull String) str;

implements 语句中

class UnmodifiableList<T> implements@Readonly List<@Readonly T> { ... }

throw exception声明

void monitorTemperature() throws@Critical TemperatureException { ... }


Note:


在Java 8里面,当类型转化甚至分配新对象的时候,都可以在声明变量或者参数的时候使用注解。

Java注解可以支持任意类型。

类型注解只是语法而不是语义,并不会影响java的编译时间,加载时间,以及运行时间,也就是说,编译成class文件的时候并不包含类型注解。


2)新增ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)


新增的两个注释的程序元素类型 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER 用来描述注解的新场合 。

ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。

ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(eg:声明语句、泛型和强制转换语句中的类型)。

eg:


@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}

3)类型注解的作用


类型注解被用来支持在Java的程序中做强类型检查。配合第三方插件工具Checker Framework(注:此插件so easy,这里不介绍了),可以在编译的时候检测出runtime error(eg:UnsupportedOperationException; NumberFormatException;NullPointerException异常等都是runtime error),以提高代码质量。这就是类型注解的作用。


Note:


使用Checker Framework可以找到类型注解出现的地方并检查。


eg:

import checkers.nullness.quals.*;
public class TestDemo{
    void sample() {
        @NonNull Object my = new Object();
    }
}

使用javac编译上面的类:(当然若下载了Checker Framework插件就不需要这么麻烦了)

javac -processor checkers.nullness.NullnessChecker TestDemo.java


上面编译是通过的,但若修改代码:


@NonNull Object my = null;


但若不想使用类型注解检测出来错误,则不需要processor,正常javac TestDemo.java是可以通过编译的,但是运行时会报 NullPointerException 异常。


为了能在编译期间就自动检查出这类异常,可以通过类型注解结合 Checker Framework 提前排查出来错误异常。


注意java 5,6,7版本是不支持注解@NonNull,但checker framework 有个向下兼容的解决方案,就是将类型注解@NonNull 用/**/注释起来。


import checkers.nullness.quals.*;
public class TestDemo{
    void sample() {
        /*@NonNull*/ Object my = null;
    }
}

这样javac编译器就会忽略掉注释块,但用checker framework里面的javac编译器同样能够检测出@NonNull错误。

通过 类型注解 + checker framework 可以在编译时就找到runtime error。


2.重复注解


允许在同一声明类型(类,属性,或方法)上多次使用同一个注解。


Java8以前的版本使用注解有一个限制是相同的注解在同一位置只能使用一次,不能使用多次。


Java 8 引入了重复注解机制,这样相同的注解可以在同一地方使用多次。重复注解机制本身必须用 @Repeatable 注解。


实际上,重复注解不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。


eg:


1)自定义一个包装类Hints注解用来放置一组具体的Hint注解

@interface MyHints {
    Hint[] value();
}
@Repeatable(MyHints.class)
@interface Hint {
    String value();
}

使用包装类当容器来存多个注解(旧版本方法)  

@MyHints({@Hint("hint1"), @Hint("hint2")})
class Person {}

使用多重注解(新方法)

@Hint("hint1")
@Hint("hint2")
class Person {}

2)

public class RepeatingAnnotations {
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Filters {
        Filter[] value();
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(Filters.class)
    public @interface Filter {
        String value();
    }
    @Filter("filter1")
    @Filter("filter2")
    public interface Filterable {
    }
    public static void main(String[] args) {
        for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) {
            System.out.println(filter.value());
        }
    }
}

输出结果:

filter1

filter2


分析:


注释Filter被@Repeatable( Filters.class )注释。Filters 只是一个容器,它持有Filter, 编译器尽力向程序员隐藏它的存在。通过这样的方式,Filterable接口可以被Filter注释两次。

另外,反射的API提供一个新方法getAnnotationsByType() 来返回重复注释的类型(注意Filterable.class.getAnnotation( Filters.class )将会返回编译器注入的Filters实例)。


3)java 8之前也有重复使用注解的解决方案,但可读性不好。

public @interface MyAnnotation {  
     String role();  
}  
public @interface Annotations {  
    MyAnnotation[] value();  
}  
public class RepeatAnnotationUseOldVersion {  
    @Annotations({@MyAnnotation(role="Admin"),@MyAnnotation(role="Manager")})  
    public void doSomeThing(){  
    }  
}

 

Java8的实现方式(由另一个注解来存储重复注解,在使用时候,用存储注解Authorities来扩展重复注解),可读性更强。

@Repeatable(Annotations.class) 
public @interface MyAnnotation {  
     String role();  
}  
public @interface Annotations {  
    MyAnnotation[] value();  
}  
public class RepeatAnnotationUseOldVersion {  
  @MyAnnotation(role="Admin")  
    @MyAnnotation(role="Manager")
    public void doSomeThing(){  
    }  
} 


目录
相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
66 2
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
41 3
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
33 2
|
2月前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
34 3
|
2月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
91 43
Java学习十六—掌握注解:让编程更简单
|
22天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
30 4
|
24天前
|
存储 Java 开发者
Java 中 Set 类型的使用方法
【10月更文挑战第30天】Java中的`Set`类型提供了丰富的操作方法来处理不重复的元素集合,开发者可以根据具体的需求选择合适的`Set`实现类,并灵活运用各种方法来实现对集合的操作和处理。
|
1月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
50 14
|
24天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
45 2
|
1月前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
32 12