介绍
概述
注解和接口、类一样,都是属于数据类型。
注解可以在变量、方法、类之上加载。
注解可以有属性,也可以没有属性。
注解有作用范围(源码、编译、运行)。
- 源码期间有效:
@Author
、@See
、@Since
。 - 编译期间有效:
@Override
、@Deprecated
、@Suppresswarning
。 - 运行期间有效:
@Test
。
注解的作用
一般用作配置。
什么时候用注解来配置?
- 如果配置信息不容易发生修改,例如 servlet 映射路径,这时候就可以使用注解形式配置。
使用
元注解
Java 提供元注解的目的就是让开发者自定义注解,元注解负责注解自定义注解。
@Target
@Target
用来说明自定义的注解可以用在什么地方。
其 ElementType
取值有:
- CONSTRUCTOR:用于描述构造器。
- FIELD:用于描述域 。
- LOCAL_VARIABLE:用于描述局部变量 。
- METHOD:用于描述方法 。
- PACKAGE:用于描述包 。
- PARAMETER:用于描述参数 。
- TYPE:用于描述类、接口(包括注解类型) 或enum声明。
例:
@Target(ElementType.FIELD)
@Retention
@Retention
用来描述自定义注解的生命周期。
其 RetentionPoicy
取值有:
SOURCE
:在源文件中有效。CLASS
:在 class文件 中有效。RUNTIME
:在运行时有效。
例:
@Retention(RetentionPolicy.RUNTIME)。
@Documented
@Documented
用于表示自定义注解可以被 javadoc 之类的工具文档化,没有成员。
@Inherited
@Inherited
元注解是一个标记注解,@Inherited
阐述了某个被标注的类型是被继承的。如果一个使用了 @Inherited
修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该 class 的子类。
@Inherited
annotation 类型是被标注过的 class 的子类所继承。类并不从它所实现的接口继承 annotation,方法并不从它所重载的方法继承 annotation。
当 @Inherited
annotation 类型标注的 annotation 的 Retention
是 RetentionPolicy.RUNTIME
,则反射 API 增强了这种继承性。如果我们使用 java.lang.reflect
去查询一个 @Inherited
annotation 类型的 annotation 时,反射代码检查将展开工作:检查 class 和其父类,直到发现指定的 annotation 类型被发现,或者到达类继承结构的顶层。
自定义注解
/*
格式:
public @interface 注解名称{
public 属性类型 属性名称();
public 属性类型 属性名称() default 默认值;
}
*/
import java.util.Date;
// 自定义一个注解
public @interface MyAnnotation01 {
// 定义属性
public String test();
// 带默认值的属性
public Class c() default Date.class;
}
注解中定义的属性支持的类型有:基本数据类型、String、Class、annotation(注解类型)、enum(枚举类型)以及以上类型的一维数组类型。
java 的基本数据类型有 8 种:byte
(字节型)、short
(短整型)、int
(整型)、long
(长整型)、float
(单精度浮点型)、double
(双精度浮点型)、boolean
(布尔型)、char
(字符型)。
对应包装类:java.lang.Byte
、java.lang.Short
、java.lang.Integer
、java.lang.Long
、java.lang.Float
、java.lang.Double
、java.lang.Boolean
、java.lang.Character
。
可分为四类:
- 整型:
byte
、short
、int
、long
。 - 浮点型:
float
、double
。 - 逻辑型:
boolean
。 - 字符型:
char
。
示例
通过注解给字段赋值
// com.zze.annotation.StrField
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface StrField{
public String value();
}
// com.zze.annotation.IntField
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface IntField{
public int value();
}
// com.zze.bean.User
import com.zze.annotation.IntField;
import com.zze.annotation.StrField;
public class User {
@StrField(value = "张三")
private String name;
@IntField(value = 18)
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// com.zze.test.AnnoTest
import com.zze.annotation.IntField;
import com.zze.annotation.StrField;
import com.zze.bean.User;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@SuppressWarnings("all")
public class AnnoTest {
@Test
public void test1() throws Exception {
// 实例化对象
User user = new User();
setValues(user);
System.out.println(user);
// User{name='张三', age=18}
}
/**
* 通过对象类上的注解给对象字段赋值
*/
private void setValues(User user) throws Exception {
Class clazz = user.getClass();
// 获取所有字段
for (Field declaredField : clazz.getDeclaredFields()) {
Object currentValue = null;
// 如果当前字段上有 StrField 注解
if (declaredField.isAnnotationPresent(StrField.class)) {
StrField annotation = declaredField.getAnnotation(StrField.class);
currentValue = annotation.value();
}
// 如果当前字段上有 StrField 注解
if (declaredField.isAnnotationPresent(IntField.class)) {
IntField annotation = declaredField.getAnnotation(IntField.class);
currentValue = annotation.value();
}
// 获取字段名称
String fieldName = declaredField.getName();
// 因为字段时 private 的 不能直接访问赋值 所以要获取它的 set 方法
// 拼接 set 方法名称
String setterName = String.format("set%s", fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
// 获取到当前字段的 set 方法
Method setter = getMethodByName(setterName, clazz);
// 通过当前遍历字段对应 set 方法给当前字段赋值
setter.invoke(user, currentValue);
}
}
/**
* 根据方法名称获取方法对象
*
* @param name 方法名称
* @param clazz 方法所在的类
* @return 方法对象
*/
private Method getMethodByName(String name, Class clazz) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (name.equals(method.getName())) {
return method;
}
}
return null;
}
}
评论区