疯狂java14

tech2025-09-14  19

Anntation(注解)

​ Anntation提供了一种为程序元素设置元数据的方法,从某些方面来看,Anntation就像修饰符一样,可以用于修饰包,类,构造器,方法,成员变量,参数,局部变量,的声明,这些信息存储在Anntation的 “name=value” 对中。另外程序可以通过反射获取指定元素的Anntation对象,然后通过Anntation对象来获取注解里的元数据。


基本Anntation

​ java提供5个基本Anntation的用法,使用Anntation时要在其前面增加@符号,并把该Anntation当成一个修饰符使用,用于修饰它支持的程序元素。

5个基本Anntation如下,他们都定义在java.lang包下:

@Override@Deprecated@SuppressWarnings@SafeVarargs@FunctionalInterface

限定重写父类方法:@Override

​ @Override就是用来指定方法覆盖的,它可以强制一个子类必须覆盖父类的方法。

public class Fruit { public void info(){ System.out.println("lallala"); } } class Apple extends Fruit{ //使用 @Override指定下面方法必须重写父类方法 @Override public void info() { System.out.println("重写lalalala"); } } //@Override的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则报错

注意: @Override只能修饰方法,不能修饰其他程序元素


标识已过时:@Deprecated

​ @Deprecated用于表示某个程序元素(类,方法等)已过时,当其他程序使用已过时的类,方法时,编译器将给出警告。

class Apple2{ @Deprecated public void info(){ System.out.println("过时"); } } public class DeprecatedTest { public static void main(String[] args) { //下面使用info()方法会被编译器警告 new Apple2().info(); } }

抑制编译器警告:@SuppressWarnings

​ @SuppressWarnings指示被该Anntation修饰的程序元素(以及程序元素中所有的子元素),取消显示指定编译器警告。@SuppressWarnings会一直作用于该程序元素的所有子元素。

​ 通常程序中使用泛型限制的集合会引起编译器警告,为了避免,可以使用@SuppressWarnings修饰

//关闭整个类里的编译器警告 @SuppressWarnings(value = "unchecked") public class SuppressWarningsTest { public static void main(String[] args) { List<String> myList=new ArrayList<>(); } }

使用@SuppressWarnings Anntation来关闭编译器警告时,一定要在括号内使用name=value的形式为该Anntation的成员变量设置值


java7的 堆污染 警告与@SafeVarargs

List list=new ArrayList<Integer>(); //添加元素时引发uncheckedy异常 list.add(20); //下面代码会引发 未经检查的转换 的警告,编译,运行时完全正常 List<String> ls=list; System.out.println(ls.get(0));

​ java把引发这种错误的原因称为 “堆污染”,当把一个不带泛型的对象赋给一个带泛型的变量时,往往会发生这种“堆污染”

如果开发者不想看到这个警告,则可以使用如下三种方式来抑制这个警告:

使用@SafeVarargs修饰引发警告的方法和构造器使用@SuppressWarnings(“unchecked”)修饰编译时使用-Xlint:varargs选项

java8的函数式接口与@Functionallnterface

​ 函数式接口:接口中只有一个抽象方法(可以包含多个默认方法或多个static方法)

​ @Functionallnterface:就是用来指定某个接口必须实现函数式接口

@FunctionalInterface public interface FunInterface { static void foo(){ System.out.println("foo类方法"); } default void bar(){ System.out.println("bar默认方法"); } void test();//之定义一个抽象方法 } //@FunctionalInterface只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则编译错误

注意:@FunctionalInterface只能修饰接口,不能修饰其他程序元素


JDK的元Anntation

​ java.lang.annotation包下提供了6个Meta Annotation(元Annotation),其中五个元Annotation都用于修饰其他的Annotation定义。


使用@Retention

​ @Retention只能用于修饰Annotation定义,用于指定被修饰Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。

value成员变量的值只能是如下三个:

RetentionPolicy.CLASS:编译器把Anntation记录在class文件中。运行java程序时,JVM不可获取Annotation信息。这是默认值RetentionPolicy.RUNTIME:编译器把Annotation记录在class文件中,运行java程序时,JVM也可以获取Anntation信息,程序可以通过反射获取该Anntation信息RetentionPolicy.SOURCE:Anntation只保留在源代码中,编译器直接丢弃这种Anntation

注意:如果使用注解时需要为value成员变量指定值,则使用该注解时可以直接在该注解后的括号指定value成员变量值,无须使用“value=变量值”的形式


使用@Target

​ @Target:用于指定被修饰的Annotation能用于修饰哪些程序单元。

​ @Target元Annotation也包含一个value的成员变量,成员变量的值只能有如下几个:


使用@Documented

​ @Documented:用于指定该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用@Documented修饰,则所有使用Annotation修饰的程序元素的API文档将会被包含进Annotation说明


使用@Inherited

​ @Inherited:指定它修饰Annotation将具有继承性—如果某个类使用@Xxx注解(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动被@Xxx修饰。

package test001; import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited //该Annotation将具有继承性 @interface Inheritable{} //下面Base类的子类将会默认使用@Inheritable修饰 @Inheritable class Base{ } //InheritableTest类只是继承了Base类 //并未直接使用@Inheritable Annotation修饰 public class InheritableTest extends Base { public static void main(String[] args) { System.out.println(InheritableTest.class.isAnnotationPresent(Inheritable.class)); } }

定义Annotation

​ 使用@interface关键字定义一个新的Annotation类型与定义一个接口非常像。

//定义一个简单的Annotation类型 public @interface Test{}

​ 使用Annotation的语法非常类似于public,final这样的修饰符,通常可以用于修饰程序中的类,方法,变量,接口等定义。通常会把Annotation放在修饰符之前,而且使用Annotation时可能还需要为成员变量指定值,因而Annotation的长度可能较长,所以通常把Annotation另放一行

//使用Test修饰类定义 @Test public class myClass{ }

​ Annotation还可以带成员变量,Annotation的成员变量在定义中以无形参的形式来声明,其方法名和返回值定义了该成员变量的名字和类型

//定义一个带成员变量的Annotation public @interface MyTag{ //定义两个成员变量的Annotation //Annotation中的成员变量以方法的形式来定义 String name(); int age(); } //使用带成员变量的Annotation public class Test{ //使用带成员变量的Annotation时,需要为成员变量赋值 @MyTag(name="xx",age=100) public void info(){ } }

注意:使用@interface定义的Annotation的确非常像定义了一个注解接口,这个注解接口继承了Annotation接口,这一点可以通过反射看到MyTag接口里包含了Annotation接口里的方法

​ 使用default关键字可以指定成员变量的初始值。如果Annotation指定了默认值,使用该Annotation时则可以不为这些成员变量指定值,而是直接使用默认值。

//定义一个带成员变量的Annotation public @interface MyTag{ //定义两个成员变量的Annotation //使用default为两个成员变量指定初始值 String name()default "hehe"; int age()default 100; }

根据Annotation是否可以包含成员变量,可以把Annotation分为如下两类

标记Annotation:没有定义成员变量的Annotation类型被称为标记。这种Annotation仅利用自身的存在与否来提供信息。元数据Annotation:包含成员变量的Annotation,因为他们可以接受更多的元数据,所有被称为元数据Annotation

提取Annotation信息

​ java使用Annotation接口来代表程序元素前面的注解,该接口是所有注解的父接口。java5在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。该接口主要有如下实现:

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


java8新增的重复注解

​ java8允许使用多个相同类型的Annotation来修饰同一个类。

//java8之前使用多个Annotation方式 @Results({@Result(name="ffff",location="fail.jsp")}, {@Result(name="cccc",location="cccsssw.jsp")}) public Acton FooActon{} //java8开始简化了上面的代码 @Result(name="ffff",location="fail.jsp") @Result(name="cccc",location="cccsssw.jsp") public Acton FooActon{}

开发重复注解要使用@Repeatable修饰

//示例 //指定该注解信息会保留到运行时 @Repeatable(FkTags.class) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FkTag { //为注解定义2个成员变量 String name() default "疯狂软件"; int age(); } //上面定义的FkTag注解,包含两个成员变量,但是该注解不能作为重复注解使用,如果使用两个以上的注解修饰同一个类,编译器会报错

使用@Repeatable时必须为value成员变量指定值,该成员变量的值应该是一个“容器”注解 ----该容器可以包含多个@FkTag,因此需要定义如下容器

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FkTags { //定义value成员变量,该成员变量可接受多个@FkTag注解 FkTag[] value(); }

//演示重复注解本质 @FkTag(age=10) @FkTag(name = "sss",age=100) public class test { public static void main(String[] args) { Class<test> clazz=test.class; //使用java8新增的getDeclaredAnnotationByType()方法获取 //修饰test类的多个@FkTag注解 FkTag[] tags=clazz.getDeclaredAnnotationsByType(FkTag.class); //遍历修饰test类的多个@FkTga注解 for(FkTag tag:tags){ System.out.println(tag.name()+"---->"+tag.age()); } //使用传统的getDeclaredAnntation()方法获取 //修饰test类的@FkTag注解 FkTags contatiner=clazz.getDeclaredAnnotation(FkTags.class); System.out.println(contatiner); } }


java8新增的Type Annotation

​ java8为ElementType枚举新增了TYPE_PARAMETER,TYPE_USE两个枚举值,这样就允许定义枚举时使用@Target(ElementType.TYPE_USE)修饰,这种注解被称Type Annotation,它可以用在任何用到的类型的地方。

创建对象(用new关键字创建)类型转换使用implement实现接口使用throws声明抛出异常


编译时处理Annotation

​ APT是一种注解处理工具,对源代码文件进行检测,并找出源文件所包含的Annotation信息,然后针对Annotation信息进行额外的处理。

​ 使用APT的主要目的是简化开发者的工作量,因为APT可以在编译程序源代码中同时生成一些附属文件(比如源文件,类文件,程序发布描述文件等等),这些附属文件的内容也都与源代码相关。

的类型的地方。

创建对象(用new关键字创建)类型转换使用implement实现接口使用throws声明抛出异常

[外链图片转存中…(img-BVregHSx-1599188010930)]

[外链图片转存中…(img-K6MzZIKY-1599188010931)]


编译时处理Annotation

​ APT是一种注解处理工具,对源代码文件进行检测,并找出源文件所包含的Annotation信息,然后针对Annotation信息进行额外的处理。

​ 使用APT的主要目的是简化开发者的工作量,因为APT可以在编译程序源代码中同时生成一些附属文件(比如源文件,类文件,程序发布描述文件等等),这些附属文件的内容也都与源代码相关。


最新回复(0)