一文读懂Java注解
什么是注解
Java官方文档上说,注解是元数据的一种形式,它提供不属于程序一部分的数据,注解对被注解的代码没有直接的影响。
准确上说,注解只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。
主要用途
注解有很多种用途,其中包括:
- 提供编译器使用信息
编译器可以使用这些注解来检查错误或者禁止显示告警,如 @Override、@Deprecated、@SuppressWarnings
- 编译或部署时处理
可以通过注解信息生产相关代码,如lombok的 @Data、@ToString等注解
- 运行时处理
在运行时处理的逻辑,如常用的Spring框架中的 @Service、@Component、@SpringBootApplication等注解
Java 内置的注解
Java一开始有定义了一套注解,共8个,3个在java.lang中,剩下4个在java.lang.annotation中
其中,作用于代码的注解有
- @Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说是元注解)有
@Retention - 注解的声明周期,标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
value为RetentionPolicy类型,有以下三种
- RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
- RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
- RetentionPolicy.RUNTIME:永久保存,可以反射获取
@Target - 注解的作用目标,标记这个注解应该是哪种 Java 成员。
value为ElementType数组,ElementType类型如下
- ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
- ElementType.FIELD:允许作用在属性字段上
- ElementType.METHOD:允许作用在方法上
- ElementType.PARAMETER:允许作用在方法参数上
- ElementType.CONSTRUCTOR:允许作用在构造器上
- ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
- ElementType.ANNOTATION_TYPE:允许作用在注解上
- ElementType.PACKAGE:允许作用在包上
@Inherited - 阐述了某个被标注的类型是被继承的。
@Documented - 标记这些注解是否包含在用户文档中。
从java7开始,又额外添加了3个注解
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
自定义注解
自定义注解,是使用注解 元数据 来实现的,和java内置注解一样,是使用 @interface 关键字来声明的。
定义注解格式
public @interface 注解名 {定义体}
如:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DemoAnnotation {
String value(); //public 关键字可以去掉,默认是public的
String name() default "write";
}
参数只有public或默认(default)这两个访问修饰符,例如 String value() 这里把方法设为 default 类型
注解参数可支持的数据类型有:
所有基本数据类型(int,float,boolean,byte,double,char,long,short)
String类型
Class类型
enum类型
Annotation类型
以上所有类型的数组
其中当value属性存在的时候,且只写value属性是,可以省略value,如spring的 @Component 注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
使用的时候可以是 @Component、@Component("jfound")、@Component(value="jfound");
注解与反射
通过过反射是可以获取相应的注解的,然后获取注解后根据注解或者注解里面的属性来进一步逻辑处理。
如Class类中提供了以下一些方法用于反射注解:
- getAnnotation:返回指定的注解
- isAnnotationPresent:判定当前元素是否被指定注解修饰
- getAnnotations:返回所有的注解
- getDeclaredAnnotation:返回本元素的指定注解
- getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
相应的,Field、Method等类也有同样的方法来反射注解
注解使用案例
- 定义注解
package jfound;
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.FIELD, ElementType.TYPE})
public @interface CustomName {
String value();
}
- 定义实体类
package jfound;
@CustomName("person")
public class Person {
@CustomName("user_name")
private String userName;
@CustomName("gender")
private String sex;
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getSex() {
return sex;
}
}
- 反射使用
package jfound;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class AnnotationDemo {
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setUserName("JFound");
person.setSex("boy");
String personKeyName = "";
CustomName annotation = Person.class.getAnnotation(CustomName.class);
if (annotation != null) {
personKeyName = annotation.value();
}
Map<String, Object> data = new HashMap<>();
for (Field field : Person.class.getDeclaredFields()) {
CustomName fieldAnnotation = field.getAnnotation(CustomName.class);
String key;
if (fieldAnnotation == null) {
key = field.getName();
} else {
key = fieldAnnotation.value();
}
field.setAccessible(true);
Object value = field.get(person);
data.put(key, value);
}
System.out.println("person key name:" + personKeyName);
for (Map.Entry<String, Object> entry : data.entrySet()) {
System.out.println("key:" + entry.getKey() + " value:" + entry.getValue());
}
}
}
输出
person key name:person
key:gender value:boy
key:user_name value:JFound
上面的例子至少个简单的注解使用,注解使用的场景还是比较多的,如一开始的用途例子中的lombok用于代码生成,spring中的@Component用于bean的生产,jpa中的@Table 用于数据表的识别等等。
总结
- java使用 @interface 来定义注解
- 注解可以定义两个或多个参数和默认值,核心参数使用 value 名称
- 应当设置 @Retention(RetentionPolicy.RUNTIME) 便于运行期读取该Annotation。
- 注解是可以通过反射来使用的
一文读懂Java注解的更多相关文章
- 一文读懂Java动态代理
作者 :潘潘 日期 :2020-11-22 事实上,对于很多Java编程人员来说,可能只需要达到从入门到上手的编程水准,就能很好的完成大部分研发工作.除非自己强主动获取,或者工作倒逼你学习,否则我们好 ...
- 一文读懂JAVA多线程
背景渊源 摩尔定律 提到多线程好多书上都会提到摩尔定律,它是由英特尔创始人之一Gordon Moore提出来的.其内容为:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍 ...
- 一文读懂Java中的动态代理
从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...
- 夯实Java基础系列16:一文读懂Java IO流和常见面试题
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...
- 一文读懂 Java 异常体系
写程序的时候,编辑器会提示错误,关键字拼错了,语法不符合规则,不符合泛型:程序编译的时候,编译器会提示错误,检查是否符合 Java 的语法规范,没有通过编译器检查的程序就无法编译,也就无法运行.这些都 ...
- [No0000196]一文读懂Java 11的ZGC为何如此高效
导读:GC是大部分现代语言内置的特性,Java 11 新加入的ZGC号称可以达到10ms 以下的 GC 停顿,本文作者对这一新功能进行了深入解析.同时还对还对这一新功能带来的其他可能性做了展望.ZGC ...
- 夯实Java基础系列7:一文读懂Java 代码块和执行顺序
目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块 ...
- 一文读懂Java线程状态转换
前言 本文描述Java线程线程状态及状态转换,不会涉及过多理论,主要以代码示例说明线程状态如何转换. 基础知识 1. 线程状态 Thread源码中的状态说明: 线程可以有6种状态: New(新建) R ...
- 别指望一文读懂Java并发之从一个线程开始
Understanding concurrent programming is on the same order of difficulty as understanding object-orie ...
随机推荐
- L3.二.return
# 函数的返回值 def get_max(a,b,c): max_num=a if b > max_num: max_num = b if c > max_num: max_num = c ...
- Unity(GameObject)
####1. 这个方法用于发送一个数据到指定的方法中,第三个参数是是否强制接收 以下三种方法发送消息的形式,各有不同的效果,可以通过第一个参数指定要发送的方法名名,第二个是发送的参数值,第三个参数是是 ...
- P1458 顺序的分数 Ordered Fractions(有技巧的枚举)+C++类封装=精简代码
题目描述 输入一个自然数N,对于一个最简分数a/b(分子和分母互质的分数),满足1<=b<=N,0<=a/b<=1,请找出所有满足条件的分数. 这有一个例子,当N=5时,所有解 ...
- Spring依赖注入—@Resource注解使用
1.@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowire ...
- 在Vue中使用iview的Select控件实现一个多级选项列表
前言 今天项目要实现一个多级选项列表,发现iview官网上没有写这个例子,于是自己就实现了,如果对你有帮助请点个赞 ‘ * ’!! 解决方法:下面我们就来使用V-for 来定义一个二级选项列表 ,代码 ...
- 重新认识 Spring IOC
spring IOC 剖析 再品IOC与DI IOC(Inversion of Control) 控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器来帮忙实现. ...
- dbcp数据源连接池
一.数据源连接池 我们之前利用jdbc连接数据库,每次都要创建连接对象,销毁连接对象,如果并发访问量比较大,这样肯定比较辣 浪费数据库的效率,我们可以像之前mybatis中缓存查询到的数据一样,可以把 ...
- [C#] StringFormat详解之文本方向、对齐
在使用GDI方式处理文本时,往往会用到StringFormat.里面的某些点有点反直觉,不够直观,所以本篇就通过图文的方式去讲解一下. 本篇内容仅涉及到文本方向.对齐的相关内容. 如有错误.不妥之处, ...
- [hdu5372 Segment Game]树状数组
题意:有两种操作:(1)插入线段,第i次插入的线段左边界为Li,长度为i (2)删除线段,删除第x次插入的线段.每次插入线段之前询问有多少条线段被它覆盖. 思路:由于插入的线段长度是递增的,所以第i次 ...
- Mysql常用sql语句(19)- in / exists 子查询
测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 子查询在我们查询方法中是比较常用的,通过子查询可 ...