文档开发者中心
我的

关于Java注解的那些事

一、注解基础

1.1、 看个例子

如何申明一个注解,来看看常常会见到的@Deprecated注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@Retention用于表示注解的生命周期,@Target用于表示注解使用的地方;从代码上看注解的定义很像接口的定义,确实如此,毕竟在编译后也会生成 Deprecated.class 文件。

@Documented、@Retention、@Target是由Java提供的元注解,用于标注其他注解的注解。Java提供的元注解还有一个是@Inherited,1.8之后还出现了一个@Repeatable。

1.2、注解使用的地方

@Target用来约束注解使用的地方,ElementType这个枚举类型中有说明,常用的有:

类型

说明



TYPE

Class, interface (including annotation type), or enum declaration

FIELD

Field declaration (includes enum constants)

METHOD

Method declaration

PARAMETER

Formal parameter declaration

CONSTRUCTOR

Constructor declaration

LOCAL_VARIABLE

Local variable declaration

ANNOTATION_TYPE

Annotation type declaration

PACKAGE

Package declaration

这里几个1.8版本出现的类型未进行罗列,感兴趣可以看下源码。

1.3、注解的生命周期

生命周期

含义



SOURCE

注解信息只会保留在源码里,编译后注解信息被丢弃。

CLASS

存在于.class文件

RUNTIME

存在于内存中的.class文件

用@Retention用来约束注解的生命周期, 当注解未定义 Retention 值时,默认值是 CLASS ,如 Java 内置注解,@Override、@Deprecated、@SuppressWarnning等 。

RUNTIME在Java的后端应用中常见, @Controller、@Autowired等。

1.4、注解元素

@Deprecated例子中,内部没有定义其他元素,因此@Deprecated也被称作标记注解(marker annotation),一般自定义注解,会包含一些其他元素。

所有基本类型、String、Class、enum、Annotation以及这些类型的数组。

1.5、注解不支持继承

但是注解在编译后,编译器会去继承 java.lang.annotation.Annotation 接口,所以可以这么说注解=接口。

这里来说一下前面没有说的的@Inherited这个元注解, 这里貌似注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类 Class 对象使用getAnnotations()获取到父类的注解。如果该注解没有被@Inherited修饰,那么无法获取。

1.6、注解与反射机制

Java所有注解都继承了 Annotation 接口,也就是说 Java 使用 Annotation 接口代表注解元素,该接口是所有Annotation类型的父接口。同时为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术读取注解的信息。

二、.class文件的结构

一个完整的class字节码文件由七部分组成,

它是二进制文件以一个字节(8bit)为最小存储单位,所以不可读,并由虚拟机解释执行。

各部分详细介绍

三、javap工具的使用

Java 自带的反编译工具,根据 class字节码文件,反编译出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

javap用法

javap <options> <classes>

options选项(常用)

-v 输出行号、本地变量表、反编译汇编代码、当前类常量池信息等

-l 行号本地变量表

-c 反编译汇编代码

-help 了解更多用法

四、demo

构造一个带运行时注解的简单java类,

// 适用类、接口(包括注解类型)或枚举
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassInfo {
String value();
}
// 适用field属性,也包括enum常量
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldInfo {
int[] value();
}
// 适用方法
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodInfo {
String name() default 'long';
String data();
int age() default 100;
}

写两个类,一个类带上注解,另一个不带注释。

通过javap看它的常量池和字节码,有较大的区别。

class文件的16进制比较,


从二进制对比的结果,可以直观的看出TYPE、FIELD、METHOD注解分别出现在class文件不同位置。

五、参考网址

Class文件二进制分析

图解JVM的Class文件格式(详细版)

自定义注解之运行时注解

Java 如何优雅的使用注解

深入理解Java注解类型(@Annotation)

javap命令

六、TODO

可以分析下class文件中,注解该如何正确的解析。

——————————————————

BY:HW-SHZ

点赞
收藏
回复
1
分享
举报
浏览545 发布于2019-11-06 08:54未知归属地
全部评论
最多点赞
最新发布
最早发布

本文讲述的是Java注解的内容

1楼回复于2019-11-06 09:09 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。