java注解--史上最清晰,讲解最全!!!
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了java注解--史上最清晰,讲解最全!!!,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含9105字,纯文字阅读大概需要14分钟。
内容图文
![java注解--史上最清晰,讲解最全!!!](/upload/InfoBanner/zyjiaocheng/607/3221232f8b034ea9814db5ae7a50b532.jpg)
java注解--史上最清晰,讲解最全!!!
前言
在学习注解
前,先来认识一下注释
,我们都知道注释是是用来说明代码的,不会经过编译,注释的主要目的是方便程序员阅读!
同样,注解也是用来说明代码的,只不过注解是方便计算机阅读;
声明:文章篇幅可能有点长,希望耐心阅读,相信定有收获!!
一、注解是什么及其本质?
(1)注解的本质
简单来说,注解是JDK1.5之后引入的新特性,用来标注程序的,提供与程序有关的数据,但与程序本省无关,可以作用在包,类,方法,字段等前面,使用注解时用@+注解名称
注解的本质是接口
我们可以用反编译来验证
public @interface TestAnno {
}
先编译为字节码文件,再用javap反编译可以知道:
注解的本质是一个interface接口,并且继承了Annotation接口
通过查看Annotation的架构图进一步验证
可得知Annotation接口是所有注解的父接口
(2)注解的分类
JDK内置注解
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中
java.lang包中的三个注解:
- @Override - 检查该方法是否是重写方法。如果发现其父类或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告
java.Annotation中的4个注解,也叫元注解
所谓元注解:是用来描述注解的注解,简单来说,元注解规定了其他注解作用的地方,时间等;
- @Target:表示该注解作用的地方,是在方法上,还是成员变量上,还是类上
- @Rentation:表示该注解保留的阶段,是在源代码阶段,还是class类对象阶段,还是runtime运行时阶段
- @Documented:表示该注解是否可以被抽取到文档中
- @Inherited:表示该注解是是否可以被继承
@Target注解:
返回值类型是枚举类型
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
一般使用一下3个较多:
TYPE:表示被@Target元注解标注的注解只能作用在类上,如果作用在其他地方会报错
FIELD:表示被@Target元注解标注的注解只能作用在成员变量上,如果作用在其他地方会报错
METHOD:表示被@Target元注解标注的注解只能作用在方法上,如果作用在其他地方会报错
@Rentetion注解:
同样@Rentetion中的RentetionPolicy属性也是枚举类型
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
注意:RetentionPolicy的取值一般是RUNTIME
@Documented注解:
类和方法的 注解 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 注解,则表示它可以出现在 javadoc 中。
定义 注解时,@Documented 可有可无;若没有定义,则 注解不会出现在 javadoc 中。
比如这里我们自定义了MyAnno注解并且使用了@Documented元注解:
然后在类中使用该注解:
用javadoc命令生成文档,可以发现此时多出了@MyAnno注解
@Inherited注解:
如果一个类使用了被该元注解标注的注解,那么这个类的子类同样会拥有这个注解
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;
/**
* 自定义的Annotation。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Inheritable
{
}
@Inheritable
class InheritableFather
{
public InheritableFather() {
// InheritableBase是否具有 Inheritable Annotation
System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
}
}
/**
* InheritableSon 类只是继承于 InheritableFather,
*/
public class InheritableSon extends InheritableFather
{
public InheritableSon() {
super(); // 调用父类的构造函数
// InheritableSon类是否具有 Inheritable Annotation
System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
}
public static void main(String[] args)
{
InheritableSon is = new InheritableSon();
}
}
运行结果:
InheritableFather:true
InheritableSon:true
现在,我们对 InheritableSon.java 进行修改:注释掉 “Inheritable 的 @Inherited 注解”。
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;
/**
* 自定义的Annotation。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//@Inherited
@interface Inheritable
{
}
@Inheritable
class InheritableFather
{
public InheritableFather() {
// InheritableBase是否具有 Inheritable Annotation
System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
}
}
/**
* InheritableSon 类只是继承于 InheritableFather,
*/
public class InheritableSon extends InheritableFather
{
public InheritableSon() {
super(); // 调用父类的构造函数
// InheritableSon类是否具有 Inheritable Annotation
System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
}
public static void main(String[] args)
{
InheritableSon is = new InheritableSon();
}
}
运行结果:
InheritableFather:true
InheritableSon:false
从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
(3)注解的作用
1.可以用于生成文档
当我们在注释中使用诸如@author,@since等注解时,通过javadoc工具会生成相应的文档说明,我们平时阅读的jdk文档就是这样生成的;
可以发现我们在代码注释中加上的注解通过javadoc后在相应位置增添了许多额外信息,这就是注解的生成文档的作用!!
2.进行编译检查
若某个方法被 @Override 的标注,则意味着该方法会覆盖父类中的同名方法。如果有方法被 @Override 标示,但父类中却没有"被 @Override 标注"的同名方法,则编译器会报错;实例如下:
这里Override继承Object类,由于@override注解标注了getString方法,表名该方法是重写的父类的,但是Object中没有该方法,因此会报错
3.代码分析
通过代码里标识的注解对代码进行分析(利用反射)
再讲解例子之前,我们先来学习一下注解的解析
所谓注解解析:就是可以获取注解中给属性赋的值
这里我们定义了一个注解:
@Target(ElementType.TYPE)
@Rentetion(RentetionPolicy.RUNTIME)
public @interface Pro{
String className();
String method();
}
我们在类上使用Pro注解,并且赋予初值,
为了获取该值,我们首先要
- 获取注解定义的位置的对象(class对象,method对象…)–如果注解定义在方法上,我们就要先通过反射获取method对象
- 之后通过class类对象获取该注解对象,之后通过注解对象去调用方法得到属性值
注意:getAnnotation()获取注解对象的本质是在内存中生成了一个注解接口的实现对象,并重写了方法,方法的返回值就是属性值;
以下面实际例子:
小明定义了一个Calculator类,其中有一些方法,现在小明需要让你写一个测试框架,来帮助小明测试方法,要求是能说明
- 哪些方法产生了异常
- 异常的名称
- 异常出现了哪些次数
这里自定义了一个Check注解
这是小明需要被测试的代码
下面是写的测试框架的代码:
public class TestCheck{
public static void main(String[] args) {
//1.创建对象
Calculator c = new Calculator();
//2.获取字节码对象
Class cls = c.getClass();
//3.获取所有方法
Method[] methods = cls.getMethods();
int num = 0;//出现异常的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
for(Method method:methods){
//4.判断方法上是否有Check注解
if(method.isAnnotationPresent(Check.class)){
try{
//5.有注解,则执行方法
method.invoke(c);
}catch(Exception e){
number++;
//6.捕获异常,把异常信息记录到文件中
bw.write(method.getName()+"方法出异常了");
bw.newLine();
bw.write("异常产生的原因:"+e.getCause().getMessage());
bw.newLine();
}
}
}
bw.write("一共产生了"+number+"次异常");
bw.flush();
bw.close();
}
}
当我们执行main方法,该框架代码就会去测试Calculator类,并把异常信息打印到文件中;
这里的注解就起到了代码分析的作用,我们给所需要的代码加上自定义注解,然后通过反射获取class对象,进而判断方法或类上是否标注了注解,若有则进行相应的操作;
二、自定义注解
1.怎样自定义注解?
很简单:
格式:
元注解
public @interface 自定义注解名{
属性
}
我们先看jdk预定义的注解认识一下格式(以@SuppressWarnings举例):
在解释之前,我们先来认识一下注解的属性应该怎样定义?
我们已经知道,注解的本质是接口,既然是接口,就可以在接口中定义方法;
因此注解的属性是用方法来表示的;
String[] value();
这里注解中规定了方法的返回值只能是以下5种类型:
- 8种基本数据类型(byte,short,int,long,float,double,char,boolean)
- string类型
- 枚举类型(enum)
- 注解
- 以上类型的数组
所谓返回类型,可以理解为规定了属性的类型;
注意:
(1)只要注解中含有属性,那么在使用注解时必须按照属性的返回值来赋值
(2)如果注解中只有一个属性,并且这个属性名的value,那么在赋值时,value可以省略不写,如果属性是数组,但是只有一个值,则{}可以省略不写
//@SuppressWarnings(value={"deprecation"})
@SuppressWarnings("all")
public class SuppressWarningTest {
public static void doSomething(){
Date date = new Date(113, 8, 26);
System.out.println(date);
}
public static void main(String[] args) {
doSomething();
}
}
//@SuppressWarnings(value={“deprecation”})
@SuppressWarnings(“all”)
下面的写法是上面的简化
我们一把用@SuppressWarnings时属性一般赋值all,表示抑制全部警告
总结
(1)在开发中我们一般都是使用注解,很少会自定义注解,因此我们了解注解的一些相关语法即可; (2)我们写的注解一是给编译器使用,编译器识别注解,进行编译,二是给解析程序用,比如我们写的测试框架内容总结
以上是互联网集市为您收集整理的java注解--史上最清晰,讲解最全!!!全部内容,希望文章能够帮你解决java注解--史上最清晰,讲解最全!!!所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。