一、注解
1.1注解的概念
注解(Annotation),也叫 元数据 (metadata)。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。
Java 语言中的类、 构造器、 方法、成员变量、局部变量、方法参数等都可以被注解进行标记,然后做特殊的处理。
1.2注解的应用
- 生成文档:java最早的提供的注解
- 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
- 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
- 替代配置文件:跟踪代码依赖性,实现替代配置文件功能,比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置
1.3注解的分类
1.3.1基本内置注解
@Override : 它的作用是对覆盖超类中方法的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出错误警告。
public class Parent {
public Parent(){
System.out.println("父亲构造器");
}
static {
System.out.println("父亲静态块");
}
{
System.out.println("父亲代码块");
}
public void test(){
System.out.println("Parent");
}
public static void main(String[] args) {
Parent parent = new Child();
parent.test();
/* 输出结果:
父亲静态块
儿子静态块
父亲代码块
父亲构造器
父亲代码块
儿子构造器
Child
*/
}
}
class Child extends Parent{
public Child(){
System.out.println("儿子构造器");
}
static {
System.out.println("儿子静态块");
}
{
System.out.println("父亲代码块");
}
/**
* 放开下面的注释,编译时会告警
*/
@Override
public void test() {
System.out.println("Child");
}
}
@Deprecated : 它的作用是对不应该再使用的方法添加注解,当编程人员使用这些方法时,将会在编译时显示提示信息
@SuppressWarnings (“unchecked”): 用于关闭对类、方法、成员编译时产生的特定警告。
其参数有:
参数 | 作用 |
---|---|
deprecation | 使用了过时的类或方法时的警告 |
unchecked | 执行了未检查的转换时的警告 |
fallthrough | 当 switch 程序块直接通往下一种情况而没有 break 时的警告 |
path | 在类路径、源文件路径等中有不存在的路径时的警告 |
serial | 当在可序列化的类上缺少serialVersionUID 定义时的警告 |
finally | 任何 finally 子句不能正常完成时的警告 |
all | 关于以上所有情况的警告 |
@FunctionalInterface: Java8支持,标志一个匿名函数或者函数式接口。 有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
1.3.2自定义注解
概念: 自己做一个注解来使用
格式:
public @interface 注解名称{
public 属性类型 属性名称() default 默认值;
}
流程:
- 定义注解——相当于定义标记;
- 配置注解——把标记打在需要用到的程序代码中;
- 解析注解——在编译期或运行时检测到标记,并进行特殊操作。
注意:
- 访问修饰符必须为public,不写默认为public;
- 该元素的类型只能是一部分数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;
- 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
- ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
- default代表默认值,值必须和第2点定义的类型一致;
- 如果没有默认值,代表后续使用注解时必须给该类型元素赋值。
1.3.3元注解
概念: 修饰注解的注解
元注解 | 功能描述 |
---|---|
@Retention | 申明注解的生命周期 |
@Target | 约束自定义注解只能在哪些地方使用 |
@Inherited | 标注注解可以被继承类获取 |
@Repeatable | 表示注解可以重复使用。 |
@Documented | 生成文档信息的时候保留注解,对类作辅助说明 |
@Target(ElementType.FIELD)
/**
可使用的值都在ElementType枚举类中,常用的值如下:
TYPE:类、接口
CONSTRUCTOR:构造器
FIFLD:成员变量
METHOD:成员方法
PARAMETER:方法参数
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解类型
PACKAGE:包
*/
@Retention(RetentionPolicy.RUNTIME)
/**
可使用的值都在RetentionPolicy枚举类中,常用的值如下:
SOURCE:注解只作用在源码阶段,编译就丢弃,生成的字节码文件中不存在。
CLASS:注解只作用在源码阶段\字节码文件阶段,运行阶段(jvm加载)不存在(默认值)。
RUNTIME:注解作用在源码阶段\字节码文件阶段\运行阶段(开发常用)。
*/
1.4注解的解析
注解的解析就是判断类、成员变量等上面是否存在注解,存在注解就解析出内容。
注解的解析相关的接口:
Annotation: 注解的顶级接口,注解都是Annotation类型的对象;
AnnotatedElement:该接口定义了与注解解析相关的解析方法;
方法 | 说明 |
---|---|
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组 |
T getDeclaredAnnotation(Class<T> annotationClass) | 根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
Method[] getDeclaredMethods() | 获取Class对象的所有方法 |
Field[] getDeclaredFields() | 获取Class对象的所有属性 |
Class<?>[] getDeclaredClasses() | 获取Class对象的所有Claas对象 |
所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力
解析注解的技巧:
注解在哪个成分上,我们就先拿哪个成分对象。
- 注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解;
- 注解作用在类上,则要该类的Class对象,再来拿上面的注解;
- 注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解;
二、自定义注解
2.1概念
当我们理解了内置注解, 元注解和获取注解的反射接口后,我们便可以开始自定义注解了。
创建自定义注解和创建一个接口相似,但是注解的interface关键字需要以@符号开头,我们可以为注解声明方法。
格式:
// 元注解
public @interface 注解名称{
// 属性列表
public 属性类型 属性名称() default 默认值;
}
2.2自定义注解的使用
自定义注解:
import java.lang.annotation.*;
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DemoAnnotaion {
String value() default "";
}
数据模型使用注解:
import com.shenxm.annotation.DemoAnnotaion;
import com.shenxm.common.PoBase;
import lombok.Data;
@Data
public class User extends PoBase {
@DemoAnnotaion(value = "xiaoming")
private String name;
@DemoAnnotaion(value = "18")
private String age;
@DemoAnnotaion("22")
public User setAge(String age) {
this.age = age;
return this;
}
}
注解解析:
import com.shenxm.annotation.DemoAnnotaion;
import com.shenxm.po.User;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class DemoTest {
@Test
public void test1() {
//反射获取Class对象
User user = new User();
Class<? extends User> userClazz = user.getClass();
// 获取User类的所有方法和属性
Method[] methods = userClazz.getMethods();
Field[] declaredFields = userClazz.getDeclaredFields();
try {
// 遍历方法
for (Method m : methods) {
// 判断是否被注解标注的方法
if (m.isAnnotationPresent(DemoAnnotaion.class)) {
//获取注解
DemoAnnotaion annotation = m.getAnnotation(DemoAnnotaion.class);
//调用这个方法
m.invoke(user, annotation.value());
}
}
// 遍历属性
for (Field f:declaredFields) {
if (f.isAnnotationPresent(DemoAnnotaion.class)){
DemoAnnotaion annotation = f.getAnnotation(DemoAnnotaion.class);
// 启动访问
f.setAccessible(true);
// 为属性设置新值
f.set(user,annotation.value());
}
}
} catch (Exception e) {
System.out.println("调用异常");
}
//结果:User(name=xiaoming, age=18)
System.out.println(user);
}
}