Java8新特性之重复注解与类型注解详解
作者:冰 河 发布时间:2023-01-16 16:18:38
目录
Java8新特性重复注解与类型注解
一、JDK5中的注解
1.注解(@)
2.作用
3.如何理解注解?
4.关于注解
5.注解分为三个阶段
6.注解的属性类型
7.为注解增加属性
二、Java8中的注解
1.类型注解
2.重复注解
三、Java8对注解的增强
Java8新特性重复注解与类型注解
在Java8之前,在某个类或者方法,字段或者参数上标注注解时,同一个注解只能标注一次。但是在Java8中,新增了重复注解和类型注解,也就是说,从Java8开始,支持在某个类或者方法,字段或者参数上标注多个相同的注解。那么,有读者就会问了:如何实现呢?别急,往下看!文中不只是Java8中的注解。
一、JDK5中的注解
1.注解(@)
注解就相当于一种标记,在程序中加了注解就等于为程序加了某种标记。(JDK1.5新特性)。
2.作用
告诉javac编译器或者java开发工具……向其传递某种信息,作为一个标记。
3.如何理解注解?
一个注解就是一个类。
标记可以加在包、类、字段、方法,方法参数以及局部变量上。可以同时存在多个注解。
每一个注解结尾都没有“;”或者其他特别符号。
定义注解需要的基础注解信息如下所示。
@SuppressWarnings("deprecation") //编译器警告过时(source阶段)
@Deprecated //过时(Runtime阶段)
@Override //重写(source阶段)
@Retention(RetentionPolicy.RUNTIME)
//保留注解到程序运行时。(Runtime阶段)
@Target({ElementType.METHOD,ElementType.TYPE})
//标记既能定义在方法上,又能定义在类、接口、枚举上等。
注意:
1)添加注解需要有注解类。RetentionPolicy是一个枚举类(有三个成员)。
2)Target中可以存放数组。它的默认值为任何元素。
ElementType.METHOD
:表示只能标记在方法上。
ElementType.TYPE
:表示只能标记定义在类上、接口上、枚举上等
3)ElementType也是枚举类。成员包括:ANNOTATION_TYPE(注解)、CONSTRUCTOR(构造方法)、FIEID(成员变量)、LOCAL_VARIABLE(变量)、METHOD(方法)、PACKAGE(包)、PARAMETER(参数)、TYPE。
4.关于注解
元注解:注解的注解(理解:给一个注解类再加注解)
元数据:数据的数据
元信息:信息的信息
5.注解分为三个阶段
java源文件–> class文件 --> 内存中的字节码。
Retention的注解有三种取值:(分别对应注解的三个阶段)
RetentionPolicy.SOURCE
RetentionPolicy.CLASS
RetentionPolicy.RUNTIME
注意:注解的默认阶段是Class。
6.注解的属性类型
原始类型(就是八个基本数据类型)、String类型、Class类型、数组类型、枚举类型、注解类型。
7.为注解增加属性
value:是一个特殊的属性,若在设置值时只有一个value属性需要设置或者其他属性都采用默认值时 ,那么value=可以省略,直接写所设置的值即可。
例如:
@SuppressWarnings("deprecation")
为属性指定缺省值(默认值):
例如:
String value() default "blue"; //定义在注解类中
数组类型的属性:
例如:
int[] arrayArr() default {3,4,5,5};//定义在注解类中
SunAnnotation(arrayArr={3,9,8}) //设置数组值
注意:如果数组属性中只有一个元素时,属性值部分可以省略大括号。
例如:
SunAnnotation(arrayArr=9)
枚举类型的属性:
例如:
EnumDemo.TrafficLamp lamp()
枚举类型属性, 定义在注解类中,这里使用了自定义的枚举类EnumDemo.java并没有给出相关代码,这里只是举个例子
default EnumDemo.TrafficLamp.RED;
注解类型的属性:
例如:
MetaAnnotation annotationAttr()
//定义在一个注解类中,并指定缺省值,
//此属性关联到注解类:MetaAnnotation.java,
default @MetaAnnotation("lhm");
//设置注解属性值
@SunAnnotation(annotationAttr=@MetaAnnotation("flx"))
二、Java8中的注解
对于注解(也被称做元数据),Java 8 主要有两点改进:类型注解和重复注解。
1.类型注解
1)Java 8 的类型注解扩展了注解使用的范围。
在java 8之前,注解只能是在声明的地方所使用,java8开始,注解可以应用在任何地方。
例如:
创建类实例
new @Interned MyObject();
类型映射
myString = (@NonNull String) str;
implements 语句中
class UnmodifiableList<T> implements@Readonly List<@Readonly T> { ... }
throw exception声明
void monitorTemperature() throws@Critical TemperatureException { ... }
注意:
在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 表示该注解能写在使用类型的任何语句中(例如:声明语句、泛型和强制转换语句中的类型)。
例如,下面的示例。
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}
3)类型注解的作用
类型注解被用来支持在Java的程序中做强类型检查。配合第三方插件工具Checker Framework(注:此插件so easy,这里不介绍了),可以在编译的时候检测出runtime error(例如:UnsupportedOperationException; NumberFormatException;NullPointerException异常等都是runtime error),以提高代码质量。这就是类型注解的作用。
注意:使用Checker Framework可以找到类型注解出现的地方并检查。
例如下面的代码。
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 注解。
实际上,重复注解不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。
例如,我们可以使用如下示例来具体对比Java8之前的版本和Java8中的注解。
**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(){
}
}
什么?没看懂?那就再来一波!!!
三、Java8对注解的增强
Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。总体来说,比较简单,下面,我们就以实例的形式来说明Java8中的重复注解和类型注解。
首先,我们来定义一个注解类BingheAnnotation,如下所示。
package io.mykit.binghe.java8.annotition;
import java.lang.annotation.*;
/**
* @author binghe
* @version 1.0.0
* @description 定义注解
*/
@Repeatable(BingheAnnotations.class)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface BingheAnnotation {
String value();
}
注意:在BingheAnnotation注解类上比普通的注解多了一个@Repeatable(BingheAnnotations.class)注解,有小伙伴会问:这个是啥啊?这个就是Java8中定义可重复注解的关键,至于BingheAnnotations.class,大家别急,继续往下看就明白了。
接下来,咱们定义一个BingheAnnotations注解类,如下所示。
package io.mykit.binghe.java8.annotation;
import java.lang.annotation.*;
/**
* @author binghe
* @version 1.0.0
* @description 定义注解
*/
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface BingheAnnotations {
BingheAnnotation[] value();
}
看到这里,大家明白了吧!!没错,BingheAnnotations也是一个注解类,它相比于BingheAnnotation注解类来说,少了一个@Repeatable(BingheAnnotations.class)注解,也就是说,BingheAnnotations注解类的定义与普通的注解几乎没啥区别。值得注意的是,我们在BingheAnnotations注解类中,定义了一个BingheAnnotation注解类的数组,也就是说,在BingheAnnotations注解类中,包含有多个BingheAnnotation注解。所以,在BingheAnnotation注解类上指定@Repeatable(BingheAnnotations.class)来说明可以在类、字段、方法、参数、构造方法、参数上重复使用BingheAnnotation注解。
接下来,我们创建一个Binghe类,在Binghe类中定义一个init()方法,在init方法上,重复使用@BingheAnnotation注解指定相应的数据,如下所示。
package io.mykit.binghe.java8.annotation;
/**
* @author binghe
* @version 1.0.0
* @description 测试注解
*/
@BingheAnnotation("binghe")
@BingheAnnotation("class")
public class Binghe {
@BingheAnnotation("init")
@BingheAnnotation("method")
public void init(){
}
}
到此,我们就可以测试重复注解了,创建类BingheAnnotationTest,对重复注解进行测试,如下所示。
package io.mykit.binghe.java8.annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author binghe
* @version 1.0.0
* @description 测试注解
*/
public class BingheAnnotationTest {
public static void main(String[] args) throws NoSuchMethodException {
Class<Binghe> clazz = Binghe.class;
BingheAnnotation[] annotations = clazz.getAnnotationsByType(BingheAnnotation.class);
System.out.println("类上的重复注解如下:");
Arrays.stream(annotations).forEach((a) -> System.out.print(a.value() + " "));
System.out.println();
System.out.println("=============================");
Method method = clazz.getMethod("init");
annotations = method.getAnnotationsByType(BingheAnnotation.class);
System.out.println("方法上的重复注解如下:");
Arrays.stream(annotations).forEach((a) -> System.out.print(a.value() + " "));
}
}
运行main()方法,输出如下的结果信息。
类上的重复注解如下:
binghe class
=============================
方法上的重复注解如下:
init method
最后,附上Java8新特性核心知识图,祝大家在学习Java8新特性时少走弯路。
来源:https://blog.csdn.net/l1028386804/article/details/106522281


猜你喜欢
- 使用范围: 只能作用在方法和构造函数之上@SneakyThrows注解的作用得从java的异常设计体系说起。java中常见的异常有两种:Ex
- 今天看到一篇好的文章,分享给大家,膜拜大佬。Android10填坑适配指南,包含实际经验代码,绝不照搬翻译文档1.Region.Op相关异常
- 前言Word中可以针对不同文档排版设计要求来设置背景设置颜色。常见的可设置单一颜色、渐变色或加载图片来设置成背景。下面通过Java来设置以上
- 请停止代码注释“干净的代码应该像写好的散文一样” - Robert C. Martin不良代码的通病就是有很多注释。这是凌乱的源代码最明显的
- 实验题目:MapReduce:编程实验内容:本实验利用 Hadoop 提供的 Java API 进行编程进行 MapReduce 编程。实验
- 安装Free Mybatis plugin即可安装MyBatisCodeHelper插件(推荐)补充:IDEA插件(mybatis框架下ma
- 我们已经写了一些Java程序。之前的每个Java程序都被保存为一个文件,比如Test.java。随后,该程序被编译为Test.class。我
- 一、关联映射举例关系说明数据库创建表,student,teacher关系说明:一个老师可以有多个学生一个学生只有一个老师一个老师对学生:一对
- 打印Java程序的线程栈信息jstack可以得知当前线程的运行情况安装jstack等命令集,jstack是开发版本jdk的一部分,不是开发版
- 今天看到已经更新了devblogs,新增的C# 11的!!(用于检查null的语法)经过非常长的讨论,最后取消了。然后我又想起来null检查
- 前言这几天听朋友说JPA很好用,根本不用写sql。我在想一个程序员不写sql还能叫程序员?而且越高级的工具封装越多的工具,可拓展性和效率就非
- 需要自定义ListView。这里就交FloatDelListView吧。复写onTouchEvent方法。如下:@Override &nbs
- 1.Spring bean组件 ”默认为单例模式scope=“singleton, 运行JavaApplication容器启动时自动创建对象
- 本文实例讲述了C#基于DBContext(EF)实现通用增删改查的REST方法,分享给大家供大家参考。具体如下:我们用ADO.NET Ent
- 一、Java中锁的概念自旋锁:是指当一个线程获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能被成功获取,
- 本文实例为大家分享了Java实现简单贪吃蛇游戏的具体代码,供大家参考,具体内容如下贪吃蛇小游戏制作方法首先需要的准备有:1、掌握Java基础
- 一、使用Pull解析器读取XML文件除了可以使用SAX或DOM解析XML文件之外,大家也可以使用Android内置的Pull解析器解析XML
- 1、Hutool工具简介HuTool工具(糊涂工具),第三方插件工具,简化操作,是国产的一个产品,界面简洁易懂,比较人性化。(上班可能经常用
- 一、说明1.spring aop中的 * 主要有两种方式,jdk * 和cglib * 2.从实现接口、继承父类的角度讨论区别3.从限
- 一.内部类的介绍 内部类: 一个类定义在 另一个类 的 内部。 &