注解概述
注解(Annotation) 可以理解为对代码的特殊标记,这些标记可以在编译、类加载、运行时被读取,并进行相应的处理。通过注解,开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
内置注解
- @Override:覆盖,该方法将覆盖或者实现超类中的方法。
- @Deprecated:已过时的,该元素不鼓励程序员进行使用。
- @SuppressWarnings:抑制该元素编译时的警告信息。
元注解
负责注解其他注解
- @Target:指示当前注解类型适用的上下文元素类型,如字段、方法等。
- @Retention:注解的保留策略级别
public enum RetentionPolicy {
/**
* 仅用作源码中,编译器将丢弃
*/
SOURCE,
/**
* 注释将记录在 class 文件中,但不参与运行
*/
CLASS,
/**
* 参与虚拟机运行,可以被反射读取
*/
RUNTIME
}
- @Document:该注解会被包括在 javadoc 中。
- @Inherited:子类可以继承父类中的该注解
自定义注解
要点
- 使用 @interface 自定义注解,自动继承 java.lang.annotation.Annotation 接口。
- 其中的每个方法声明了一个配置参数。
- 方法的名称为参数名称(如果只需要一个参数,那么通常参数名为 value,此时在配置参数时可以省略参数名称,可以理解为默认参数)。
- 返回值类型为参数类型(返回值为基本类型、Class、String、enum)。
- 可以使用 default 声明参数的默认值。
- 声明了参数,则该参数必须有值。
实现一个简单的注解
/**
* @author: lvshui5u
* @date: 2021/9/19 17:17
* @describe: 自定义注解
*/
// 可以使用在类和字段上
@Target({ElementType.TYPE, ElementType.FIELD})
// 可以作用在运行
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
// 实现的功能
String action();
// 字段类型
String type() default "";
}
很明显,注解存在的意义是它的配置参数,到此为止,还没有用到它的配置参数。通过反射机制来实现对配置参数的获取。
反射概述
反射(Reflection) 主要实现的是在 Java 代码运行的过程中,对于类,可以知道其属性字段和方法;对于对象,可以调用其属性和方法,并且改变其属性。反射使 Java 成为 “准动态” 的语言。
反射的 “反” 主要是针对正常创建对象的方式来讲的。
正常方式:
- 引入需要的类的全限定名
- 通过关键字 new 实例化(可去了解类加载过程)
- 获得实例化对象
反射方式:
- 获得 Class 对象
- 通过 newInstance() 实例化一个对象或者获取类加载器等操作
获得 Class 对象的方式
- 已知具体的类,获得该类的 class 属性
- 已知某个类的实例对象,调用对象的 getClass() 方法
- 已知某个类的全限定名,调用 Class 类的 forName() 方法
- 已知某个类的子类的类对象,调用其 getSuperClass() 方法
package com.lvshui5u.basis.springbasis.reflect;
import lombok.Data;
/**
* @author: lvshui5u
* @date: 2021/9/19 19:42
* @describe:
*/
public class Test01 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 1. 已知具体的类
Class<User> userClass01 = User.class;
// 2. 有某个类的实例化对象
User user = new User();
Class<? extends User> userClass02 = user.getClass();
// 3. 已知某个类的全限定名
Class<?> userClass03 = Class.forName("com.lvshui5u.basis.springbasis.reflect.User");
// 4. 已知某个类的子类,得到父类的类对象
Class<? super User> superclass = userClass01.getSuperclass();
System.out.println(userClass01 + " " +userClass01.hashCode());
System.out.println(userClass02 + " " +userClass02.hashCode());
System.out.println(userClass03 + " " +userClass03.hashCode());
System.out.println(superclass + " " +superclass.hashCode());
}
}
@Data
class User{
String name;
int age;
}
结果
class com.lvshui5u.basis.springbasis.reflect.User 149928006
class com.lvshui5u.basis.springbasis.reflect.User 149928006
class com.lvshui5u.basis.springbasis.reflect.User 149928006
class java.lang.Object 713338599
通过结果可验证,三种方式得到的 Class 对象是同一个。如果不了解类加载过程和 Java 内存管理部分知识,建议了解。
小总结:不同对象的 Class 对象
Object 普通类对象 -> class java.lang.Object
Override 注解 -> interface java.lang.Override
Comparable 接口 -> interface java.lang.Comparable
int[] 一维数组 -> class [I
int[][] 二维数组 -> class [[I
ElementType 枚举 -> class java.lang.annotation.ElementType
void -> void
Class 类对象 -> class java.lang.Class
利用反射来操作注解
主要使用的 api 是 Class 对象的 getAnnotation() 方法来获取当前元素(类、字段、方法)的注解对象。
package com.lvshui5u.basis.springbasis.annotation;
import lombok.Data;
import java.lang.reflect.Field;
/**
* @author: lvshui5u
* @date: 2021/9/19 17:33
* @describe:
*/
public class Test01 {
public static void main(String[] args) {
Class<User> userClass = User.class;
MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
// 类的注解配置
System.out.println("类:"+userClass.getName()+" -> action:"+annotation.action());
Field[] fields = userClass.getDeclaredFields();
// 字段的注解配置
for (Field field : fields) {
MyAnnotation anno = field.getAnnotation(MyAnnotation.class);
System.out.println("字段:"+field.getName()+" -> action:"+anno.action()+" -> type:"+anno.type());
}
}
}
@MyAnnotation(action = "用户实体类")
@Data
class User{
@MyAnnotation(action = "用户姓名", type = "String")
String name;
@MyAnnotation(action = "用户年龄", type = "int")
int age;
}
结果:
类:com.lvshui5u.basis.springbasis.annotation.User -> action:用户实体类
字段:name -> action:用户姓名 -> type:String
字段:age -> action:用户年龄 -> type:int