记录《疯狂Java讲义精粹》划线内容及理解(第九章- Annotation注解)

第九章 Annotation(注解)

在JDK5开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注解)

解释一下——元数据(MetaData),又称中介数据,中继数据,是描述信息的信息,主要描述数据的属性

Annotation其实是代码里的特殊标记,通过这些标记,可以在编译,类加载,运行时被读取,并执行相应的处理

Annotation提供了为程序元素设置元数据的方法,可以用来修饰包,类,构造器,方法,成员变量,参数,局部变量,这些信息被存储在Annotation的键值对中

Annotation是一个接口,程序可以通过反射来获取它,然后在获取到的Annotation对象中的元数据

Annotation是存储元素信息的,所以不会对程序运行造成影响,如果需要Annotation在运行时起到一定作用,需要通过某种配套工具对Annotation进行处理,

对Annotation中的信息进行访问,处理,的工具统称为APT(Annotation Processing Tool)


9.1 基本Annotation

java.lang包下提供的4个基本Annotation注解

@Override

@Deprecated

@SuppressWarnings

@SafeVarargs


9.1.1 限定重写父类方法:@Override

强制一个子类必须覆盖(重写)父类的方法

@Override注解,表示子类必须重写父类的方法,如果一个方法的名字特别难记,在重写时不小心写错了一个字母,编译运行不会发现错误,只有在程序崩溃时才会发现,排查又特别困难,所以用@Override就可以解决这个问题,如果需要被重写的方法,在编译时编译器没有发现,则会报错


9.1.2 标示已过时:@Deprecated

用于修饰程序的某个元素,如方法,类等,如果在别的程序中使用,被@Deprecated修饰的元素,编译器会警告,该元素以过期(废弃)

和这个注解类似的,还有一个注解

@deprecated 用于文档注释,表示一个方法或类等,已过期(废弃)


9.1.3 抑制编译器警告:@SuppressWarnings

@SuppressWarnings可以取消编译器的警告,并且被它修饰的元素,以及这个元素的所有子元素,都会取消(关闭)编译器警告

在使用时,要使用键值对来设置关闭的警告,

如:

​ @SuppressWarnings(values=“unchecked”)

取消(关闭)了所有的编译器警告


9.1.4 Java 7的“堆污染”警告与@SafeVarargs

将一个不带泛型信息的集合,赋值给一个带泛型的集合,陈为转换,反过来就是擦除

当程序发生 转换/擦除 时,往往会发生“堆污染”

当方法的形参是,泛型,又是个数可变的时,更容易发生堆污染

当方法的形参是个数可变的,则相当于形参是数组,并且又是泛型,但系统不允许创建泛型数组,所以这里默认有擦除

这种转换在赋值时不会发生错误,但只要访问,就会出错

可以使用一些方法抑制编译器的警告,但程序运行到错误的地方该停还会停

可以使用@SafeVarargs来修饰引发该警告的方法或构造器

或使用@SupperssWarnings(”unchecked“)修饰,


9.2 JDK的元Annotation

java还在java.lang.annotation包下提供了4个Meta Annotation(元Annotation),这个4个注解用于修饰其他注解


9.2.1 使用@Retention

用于修饰一个Annotation,指定被修饰的Annotation可以保存多长时间

使用格式 @Retention(值)

​ 这个值有三种:

​ RerentionPolicy.CLASS 编译器把Annotation记录在class文件中,当运行时,JVM不再保留Anntation,默认值

​ RetentionPoilcy.RUNTIME 编译器把Annotation记录在class文件中,运行时,JVM会保留Annotation,程序可以通过反射获得Annotation

​ RetentionPolicy.SOURCE 只把Annotation保留在源码中,编译器直接丢弃Annotation


9.2.2 使用@Target

用于修饰一个Annotation,指定被修饰的Annotation可以修饰那些程序单元,

格式: @Target(值)

​ 值的选择范围:

​ ElementType.ANNOTATION_TYPE 只能修饰Annotation

​ ElementType.CONSTRUCTOR 只能修饰构造器

​ ElementType.FIELD 只能修饰成员变量

​ ElementType.LOCAL_VARIABLE 只能修饰局部变量

​ ElementType.METHOD 只能修饰方法定义

​ ElementType.PACKAGE 只能修饰包定义

​ ElementType.PARAMETER 可以修饰参数

​ ElementType.TYPE 可以修饰类,接口(包括注解类型)或枚举类定义


9.2.3 使用@Documented

被修饰的注解,将会被javadoc文档注释识别,提取后出现在API文档中


9.2.4 使用@Inherited

被修饰的注解将具有继承性,如果有一个被修饰的注解A,注解A修饰了B类,B类的子类C将自动拥有注解A,因为注解A被@Inherited修饰


9.3 自定义Annotation

9.3.1 定义Annotation

定义一个注解,需要用到关键字@interface(就是接口的关键字,加了个@)

可以在定义的注解内定义成员变量,并赋初始值

                格式为:类型 名字() default 默认值;

如果定义了成员变量,在使用该注解时,应该为变量赋值,如有默认值,也可忽略

基本格式:

                    权限修饰符 @interface 名字{

                                类型    名字()default 默认值;

                                ……

                }

定义的这个注解,其实继承了Annotation接口

根据Annotation内是否有成员变量,可分为两类:

                            标记Annotation                    利用自身存在与否来提供信息

                            元数据Annotation                可以利用成员变量来提供更多数据

9.3.2 提取Annotation信息

当使用注解修饰一个程序元素后,注解并不会自己生效,而是需要开发者提供对应的工具来提取和处理这些Annotaion

java5在java.lang.reflect包下新增了Annotation接口,该接口可以接受注解的程序元素

该接口的实现类

                Class                                 类定义

                Constructor                       构造器定义

                Field                                    类的成员变量定义

                Method                               类的方法定义

                Package                             类的包定义

当一个注解被@Retention(​ RetentionPoilcy.RUNTIME)修饰时(运行时保存注解),才能在JVM运行时,通过反射获取注解信息

AnnotatedElement接口是所有程序元素(如Class、Method、Constructor等)的父接口

如果在运行时获取到了某个类的AnnotationElement对象(如Class,Method,Constructor),就可以使用方法来获取注解的信息

  AnnotationElenemt对象的方法:

                              getAnnotation(Class<T>annotationClass) 返回程序元素上存在的指定类型的注解,如无,返回null

                                getAnnotaions() 返回该程序元素上存在的所有注解(返回值是一个Annotation数组)

                                isAnnotaionPresent(Class<? extends Annotation>annotationClass) 判断该程序元素上是否存在指定类型的注解(返回值为boolean类型)

9.3.3 使用Annotation的示例

这里的例子比较复杂,使用到了反射,大概简述一下

        自己手动创建注解,使用java提供的4个Meta Annotaion来修饰,之后在程序加载到JVM中时,通过反射获取注解,然后执行对应的方法来处理注解修饰的内容

9.4 编译时处理Annotation

APT(注解处理器)是一种处理注解的工具,APT会在文件编译阶段,对源文件进行检测,找到Annotation并对其进行额外处理,

开发者可以自定义自己的Annotation处理器,两种方法

                                      实现javax.annotation.processing包下的Processor接口

                                      继承AbstractProcessor(推荐,开发量较小)

APT可以根据注解,(先编写java,后编译)生成class文件或其他文件,有一定的价值,那么我猜测,spring中的注解,其实就是自己的编写的Annotation处理器来生成类对象,从而实现控制反转

在java编译时时,javac.exe命令有 -processor选项,可以指定一个Annotation处理器


关于APT的扩展

注解处理器运行在它自己的 JVM 中。
是的,你没看错。javac 启动了一个完整的 java 虚拟机来运行注解处理器。
这意味着什么?你可以使用任何你在普通 java 程序中使用的东西。
使用 guava! 你可以使用依赖注入工具,比如dagger或者任何其他你想使用的类库

扩展-关于一个诡异的输出

String str = ",a,,b,";
String[] splitArr = str.split(",");
Arrays.stream(splitArr).forEach(System.out::println);

这段代码将遍历字符数组输出,a,b 其中的双冒号输出语句是函数式接口的简写

上面先是创建了一个字符串,通过split方法切割,形成了一个新的string数组

再通过Arrays.stream(),将一个字符串数组转换为“流”(值得注意的是,stream也是java8新增加的特性),再使用forEach 遍历

在java8时还增加了一种新的特性,称为 Lambda表达式和函数式接口,这种新特新规定

仅仅有一个未实现方法的接口,可以直接写作(参数列表) -> {方法体}这种形式
例如:@FunctionalInterface
public interface FuncA {
void doSomeThing(String str);}

那么上面这种接口就可以直接写作:
FuncA funcA = (str) -> {System.out.println(“hello”);};
forEach方法提供一个某种类型的Object,而System.out.println可以接受一个Object
因此,forEach提供的参数和System.out.println的参数类型是一致的,可以进行这种简写。

具体来说就是:原本应该写为:.forEach(element -> {System.out.println(element)})
但System.out.println的参数和传递的参数element 的类型完全匹配,
所以这样的时候就可以简化为:.forEach(System.out::println)
<br>
总结一下,这里就是  函数式接口 -> Lambda表达式 -> 方法引用

java的并行API发展

Java 的并行 API 演变历程基本如下:

  1. 1.0-1.4 中的 java.lang.Thread
  2. 5.0 中的 java.util.concurrent
  3. 6.0 中的 Phasers 等
  4. 7.0 中的 Fork/Join 框架
  5. 8.0 中的 Lambda


## 后言 月亮正亮的起劲,若此刻不想你,倒显得的我不解风情
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值