Java基础-注解

159 阅读5分钟

个人文章均用作个人学习记录,所以里面包含了很多自言自语的话。抱歉。

注解

写在前面的话,个人对于 注解 这个概念的理解。

什么是注解?从本质上来说,注解就是一个标签,实际上可以将其认为是一种特殊的注释,如果没有任何代码解析它的话, 那么它并没有任何实际的意义和用途。(甚至不如普通的注释)

我个人认为,注解只是某些数据的载体,而并不是用于执行某个动作,所以很多时候我们自定义注解之后(一般将自定义注解的有效性设置为RUNTIME,即运行时有效),仍然需要通过 反射 来进行一些额外的操作,使得注解变得“有用”起来

感觉对于注解的内核有了一定程度的认识,原来只知道注解长这样,完全不知道内在是什么。

自定义注解

自定义注解的语法要求

下面是自定义注解的一个示例


@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description{
    String desc();
    String author();
    int age() default 18;
}
​

下面是对其解释:

  • @interface :如果要自定义注解,那么必须使用@interface关键字来定义注解

  • String desc();String author()int age() default 18;

    • 以上三个可不是什么所谓的成员方法,首先必须明确: 自定义的注解中没有 也不能包含所谓的方法
    • 以上三个实际上是注解的元素,他们是注解的一部分,用于存储注解的配置数据。
    • 这些元素在定义时确实需要使用括号 (),但这不是方法调用的括号,而是注解元素的默认值定义。(个人认为简单理解为 成员变量可能还稍微可行)
    • 可以使用default关键字给成员指定一个默认值 比如上面的int age() default 18;
    • ▲ 成员的类型实际上是受限的,合法的类型包括基本数据类型 、String、Class、Annotation、Enumeration ; 如果注解只有一个成员,那么成员名必须取名为value(),在使用的时候可以忽略成员名和赋值号( = )
    • 注解类可以没有成员,没有成员的注解称为标识注解。

元注解

元注解,简单来说就是注解的注解。

仍然是上面的例子:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description{
    String desc();
    String author();
    int age() default 18;
}
  1. @Target指定注解可以修饰的元素类型

    • ElementType 是一个枚举类型,它定义了被 @Target 修饰的注解可以应用的范围:

      • ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型。
      • ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数。
      • ElementType.FIELD - 标记的注解可以应用于字段或属性。
      • ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量。
      • ElementType.METHOD - 标记的注解可以应用于方法。
      • ElementType.PACKAGE - 标记的注解可以应用于包声明。
      • ElementType.PARAMETER - 标记的注解可以应用于方法的参数。
      • ElementType.TYPE - 标记的注解可以应用于类的任何元素。
  2. @Retention 指明注解的保留级别

    • RetentionPolicy 是一个枚举类型,它定义了被 @Retention 修饰的注解所支持的保留级别:

      • RetentionPolicy.SOURCE - 标记的注解仅在源文件中有效,编译器会忽略。
      • RetentionPolicy.CLASS - 标记的注解在 class 文件中有效,JVM 会忽略。
      • RetentionPolicy.RUNTIME - 标记的注解在运行时有效。
  3. @Inherited 表示注解类型可以被继承(默认情况下不是这样)。

    • 表示自动继承注解类型。 如果注解类型声明中存在 @Inherited 元注解,则注解所修饰类的所有子类都将会继承此注解。
  4. @Documented 表示无论何时使用指定的注解,都应使用 Javadoc(默认情况下,注释不包含在 Javadoc 中)

自定义注解例子

1、自定义注解@Table

package com.nylonmin.AnnotationTest;
​
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
/**
 *  自定义注解Table
 *  该注解可放在类上@Target({ElementType.TYPE})
 *  并且设置为运行时有效@Retention(RetentionPolicy.RUNTIME)
 * @author nylonmin
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}
​

2、自定义注解@Column

package com.nylonmin.AnnotationTest;
​
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
/**
 *  自定义注解Table
 *  该注解可放在成员对象上@Target({ElementType.FIELD})
 *  并且设置为运行时有效@Retention(RetentionPolicy.RUNTIME)
 * @author nylonmin
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String value();
}
​

3、定义类Filter 并将注解加上去

package com.nylonmin.AnnotationTest;
​
@Table("user")
public class Filter {
    @Column("id")
    private int id;
    @Column("userName")
    private String userName;
    @Column("nickName")
    private String nickName;
    @Column("age")
    private int age;
​
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    public String getUserName() {
        return userName;
    }
​
    public void setUserName(String userName) {
        this.userName = userName;
    }
​
    public String getNickName() {
        return nickName;
    }
​
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
}
​

4、定义Test类 其中一个main方法 另一个静态方法query 将Filter类的对象中的成员变量变为sql查询语句的条件

package com.nylonmin.AnnotationTest;
​
import java.lang.reflect.Field;
import java.lang.reflect.Method;
​
public class Test {
    public static void main(String[] args) throws Exception {
        Filter filter = new Filter();
        filter.setAge(12);
        filter.setUserName("张三");
        String query = query(filter);
        System.out.println(query);
    }
​
    /**
     * 用于将对象Filter f中的成员变量转化为一个sql查询语句
     * @param f 传入的Filter类的对象
     * @return 返回sql语句 String类型
     * @author nylonmin
     */
    public static String query(Filter f) throws Exception {
        //创建StringBuilder类对象result,表示最终返回的sql语句
        StringBuilder result =new StringBuilder();
        //Step1:获取Class
        Class c = f.getClass();
        //Step2: 获取注解中的table名称
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if(!annotationPresent){
            System.out.println("无Table注解,无法获取表名称");
            return null;
        }
        Table table = (Table)c.getAnnotation(Table.class);
        //这里挺神奇的,在末尾加上where 1=1
        result.append("select * from ").append(table.value()).append(" where 1=1");
        //Step3 : 获取所有的Field(成员变量) 及其上面的注解
        Field[] Fields = c.getDeclaredFields();
        for(Field field:Fields){
            boolean fieldAnnotationPresent = field.isAnnotationPresent(Column.class);
            if(!fieldAnnotationPresent){
                continue;
            }
            Column fieldAnnotation = field.getAnnotation(Column.class);
            //拿到注解的值 (实际上这里指代sql语句中的字段名称了)
            String columnName = fieldAnnotation.value();
            //拿到字段的值
            //首先要拿到对应的get方法
            //比方说我们取到的是id 那么其get方法的名称应该是 getId  即字段名第一个字母要大写
            String methodName = "get"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
            Method method = c.getDeclaredMethod(methodName);
            //调用get方法获取对应字段的值
            Object fieldValue = method.invoke(f,null);
            //如果是null 或者返回值为Integer类型且值为0  跳过
            if(fieldValue == null || (fieldValue instanceof Integer && ((Integer) fieldValue).intValue()==0)){
                continue;
            }
            //如果值为String类型 则要加上 单引号
            if(fieldValue instanceof  String){
                result.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
            }
            //否则就直接=
            else {
                result.append(" and ").append(columnName).append("=").append(fieldValue);
            }
​
​
        }
        return result.toString();
​
    }
}
​