<>注解

文末有彩蛋。

<>一、什么是注解?

Annotaion

*
注解(Annotaion)是从JDK5.0开始引入的一种新技术称之为注解机制。

*
注解(Annotaion)的格式:

* 注解是以"@注释名"在代码中使用的,可以添加一些参数值,例如:@GetMapping("/get")
*
注解(Annotaion)可以使用的范围:

*
可以在package、class、method、field等上面使用。例如:

* @Controller public class RequestController { @DeleteMapping("/delete")
@ResponseBody public String delete(String name,Integer id){ JSONObject json =
new JSONObject(); json.put("requestType","deleteType"); json.put("name",name);
json.put("id",id); return json.toString(); } }
*
我们可以通过反射机制编程对这些元数据的访问。

*
注解有一些特定的功能,例如:

*
当你如果要重写toString()方法的时候,不是按照规定的名字来写的话,就会报错:

*
*
正常的话,是不会报错的:

*

*
说明@Override注解带有检查的作用。

<>二、内置注解

Java内部定义了一套注解,共有7个:

注解名称 作用
@Override 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(元注解):在java.lang.annotaion包中

注解名称 作用
@Retention 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented 标记这些注解是否包含在用户文档中。
@Target 标记这个注解应该是哪种 Java 成员。
@Inherited 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
从 Java 7 开始,额外添加了 3 个注解:

注解名称 作用
@SafeVarargs Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
<>三、元注解的使用

<>(一)前期准备

如果你想自定义注解,那么元注解是必知必会必懂的。

元注解就是注解自定义注解的注解。可能有点饶,一会看例子就明白了,直白点就是给你自定义的注解上一定要加的注解。

作用在其他注解的注解(元注解):在java.lang.annotaion包中

注解名称 作用
@Retention 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented 标记这些注解是否包含在用户文档中。
@Target 标记这个注解应该是哪种 Java 成员。
@Inherited 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
我们定义一个类,类的作用就是用来测试我们定义的注解:
/** * @Auther: truedei * @Date: 2020 /2020/9/6 18:11 * @Description: 自定义注解测试 */
public class Test { }
然后创建一个类,把class标识符改成@interface:这就是自定义好的注解了。
/** * @Auther: truedei * @Date: 2020 /2020/9/6 19:00 * @Description: 自定义注解 */
public @interface MyAnnotaion { }
现在可以看到就可以使用了,只不过没有任何的功能:

<>(二)@Target的用法详解

我们来赋予一定的功能,来标识这个注解的作用:

加上元注解:@Target,就是用来指出对什么生效,作用的目标是什么,可以在什么地方使用。

可以看到传递的是一个E

可以点看@Target的源码看一下:

可以看到下面这种情况。

需要提前知道的是,value()是接收的参数,并不是一个方法。

那么Target就需要接收一个ElementType[]的数组。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.
ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
没加之前,先改造一下:

可以看到这个注解什么都没加,既可以加在类上,也可以加在方法上,也可以加在变量上。

如果我们想做限制呢?只允许我们的这个注解对类生效

我们先来看一下Controller注解这个应该不陌生,就不解释了。

那么我们也可以加上这个:

可以看到效果了,只要类上的生效了,其余的都报错了。

那么这就是元注解Target的作用。

看一下ElementType.java中枚举的参数,这些都可以使用,了解下就好。
package java.lang.annotation; public enum ElementType { TYPE, /*
类、接口(包括注释类型)或枚举声明 */ FIELD, /* 字段声明(包括枚举常量) */ METHOD, /* 方法声明 */ PARAMETER, /*
参数声明 */ CONSTRUCTOR, /* 构造方法声明 */ LOCAL_VARIABLE, /* 局部变量声明 */ ANNOTATION_TYPE,
/* 注释类型声明 */ PACKAGE /* 包声明 */ /** * Type parameter declaration * * @since 1.8
*/ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE }
如果你想让你的注解对什么生效,就指定好就OK:

<>(三)@Retention的用法详解

Retention中需要传递RetentionPolicy。

RetentionPolicy.java有三个枚举参数,如下:
package java.lang.annotation; public enum RetentionPolicy { SOURCE, /*
Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */ CLASS, /*
编译器将Annotation存储于类对应的.class文件中。默认行为 */ RUNTIME /*
编译器将Annotation存储于class文件中,并且可由JVM读入 */ }
一般都是调用**RUNTIME**,调用RUNTIME我们可以通过反射拿到相关的数据,来进行处理等。

由于这个不太好验证,就不一一验证了,了解就好。

<>(四)@Documented的用法详解

如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。

定义 Annotation 时,@Documented 可有可无;

若没有定义,则 Annotation 不会出现在 javadoc 中。

<>(五)@Inherited的用法详解

子类可以继承父类的注解。
/** * @Auther: truedei * @Date: 2020 /2020/9/6 19:00 * @Description: 自定义注解 */
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) @Retention(
RetentionPolicy.RUNTIME) @Documented @Inherited public @interface MyAnnotaion {
}
<>四、自定义参数详解

注解的参数:参数的类型 + 参数名 ();

如果想有默认值还需要加上default 值。

例如:
//自定义参数1 String name(); //自定义参数2 带默认值的 String type() default "";
我们给事先定义好的注解类加上一个参数,随便写名字即可:
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) @Retention(
RetentionPolicy.RUNTIME) @Documented @Inherited public @interface MyAnnotaion {
//参数 String name(); }
可以看到我们在使用的时候,就会报错了,原因是必须要给这个定义好的参数传递一个值。

我们传递过来值:
@MyAnnotaion(name = "郑晖") public void test(){ }

这个时候就有同学要问了:我定义好参数之后,可以不传递参数吗,用到的时候再传递。

答案是可以的,如下:设置一个默认值就好了:
String type() default "";

当然了,也可以传递很多类型的参数例如:
import java.lang.annotation.*; /** * @Auther: truedei * @Date: 2020 /2020/9/6
19:00 * @Description: 自定义注解 */ @Target({ElementType.TYPE,ElementType.FIELD,
ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited
public @interface MyAnnotation { //String类型 String name(); //int类型 int age()
default 0; //boolean类型 boolean bool() default false; //char 类型 char cha()
default ' '; //各种数组类型 String[] strs() default {}; //枚举类型 MyEnum myEnum() default
MyEnum.A; }
枚举类型类定义:
public enum MyEnum { A,B,C,D; }
我们在使用的时候,就可以随心所欲的使用:
/** * @Auther: truedei * @Date: 2020 /2020/9/6 18:11 * @Description: 自定义注解测试 */
public class Test { String data; @MyAnnotaion(name = "郑晖",age = 85,cha = 'A',
strs= {"aasd","xsw","你好"},myEnum = MyEnum.C) public void test(){ } }
<>五、利用反射操作注解

MyA.java:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
/** * 对于我们的作用:标识着加了这个注解的我们才允许扫描 */ @Target({ElementType.TYPE}) @Retention(
RetentionPolicy.RUNTIME) public @interface MyA { }
MyAnnotation.java:
import java.lang.annotation.*; /** * @Auther: truedei * @Date: 2020 /2020/9/6
19:00 * @Description: 自定义注解 ---->给特定的方法用的 */ @Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { //String类型
Stringname(); }
Test.java:
/** * @Auther: truedei * @Date: 2020 /2020/9/6 18:11 * @Description: 自定义注解测试 */
@MyA public class Test { String data; @MyAnnotation(name = "郑晖") public void
test(String name){ System.out.println("我的名字:"+name); } }
测试类:
package cn.annotaion; import java.lang.annotation.Annotation; import java.lang.
annotation.Target; import java.lang.annotation.ElementType; import java.lang.
annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.
lang.annotation.Inherited; import java.lang.reflect.Method; /** *测试类 */ public
class AnnotationTest { public static void main(String[] args) throws Exception {
//拿到指定的类的Test Class cl = Class.forName("cn.annotaion.Test"); //判断是否是我们特定自定义的注解
如果是就扫描 if (cl.isAnnotationPresent(MyA.class)) { Method[] methods = cl.getMethods
(); for (Method method : methods) { // 判断 somebody() 方法是否包含MyAnnotation注解 if(
method.isAnnotationPresent(MyAnnotation.class)){ // 获取该方法的MyAnnotation注解实例
MyAnnotation myAnnotation= method.getAnnotation(MyAnnotation.class); // 获取
myAnnotation的值,并打印出来 String name = myAnnotation.name(); System.out.println(name)
; //执行这个方法 method.invoke(new Test(),name); } } } } }
运行:

<>六、彩蛋

看完了之后,发现有什么问题吗?

细心的你一定注意到:最初我使用的是MyAnnotaion.java后来MyAnnotation.java

没错,少写了个t。还好不影响大局。

技术
今日推荐
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:766591547
关注微信