Java注解基础

一、注解基础

Annotation 是从JDK1.5引入的新技术; 可以在编译类加载运行时被读取; 注解处理器编译期间注解做处理; 注解以 @注解名(可以携带参数) 在代码中存在。

包位置信息java.lang.*


1.1 Annotation 可以在什么地方使用

可以用在·packagefieldmethodclass等上面;相当于给它们添加了辅助信息;可以通过反射机制访问这些元数据信息。

1.2 内置注解

注解名 描述
@Override 限定重写父类方法。对于子类中被@Override 修饰的方法,如果存在对应的被重写的父类方法,则正确;如果不存在则报错。@Override 只能作用于方法,不能作用于其他程序元素
@Deprecated 用于表示某个程序元素(类、方法等)已过时。如果使用了被@Deprecated修饰的类或方法等,编译器会发出警告
@SuppressWarnings 抑制编译器警告

@SuppressWarnings 需要配合下面一个参数才能使用:

参数 描述
deprecation 使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告)
unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告
fallthrough 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path 在类路径、源文件路径等中有不存在的路径时的警告
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告
finally 任何 finally 子句不能正常完成时的警告
all 关于以上所有情况的警告

案例分析:

Java注解基础_第1张图片


查看SuppressWarnings源码:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

@Target@Retention 这两个修饰@SuppressWarnings的注解是什么 ?


1.3 元注解

注解的注解叫元注解

注解名 描述
@Target 指示注释类型的注释要保留多久;保留策取值 RetentionPolicy.CLASS
@Retention 指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制
@Documented 将此注解包含在Javadoc中;生成API文档时,提前对应注解
@Inherited 允许子类继承父类中的注解;使用该元注解修饰的自定义注解去修饰的类,修饰类的子类也同时拥有父类的那个注解

注:在进阶篇中,将给出@Inherited一个例子。


@Retention 的取值在下:

参数 描述
CLASS 编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释;class文件中有效
RUNTIME 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取;即运行时有效
SOURCE 只保留在源码阶段;编译阶段将抛弃;即源文件中有效

源文件通过javac编译成class文件;再经过类加载器的加载、连接(验证+准备+解析+)、初始化等一系列操作到内存中;让程序能够运行使用


@Target 的取值在下:

参数 描述
ANNOTATION_TYPE 注解类型声明
CONSTRUCTOR 构造方法声明
FIELD 字段声明(包括枚举常量)
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明;方法参数
TYPE 类、接口(包括注释类型)或枚举声明

补充:类的Target 取值是Type;而不是 class ?

Type 是 Java 编程语言中所有类型的公共高级接口它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。所以使用Type 来描述更加精准。


JDK中对Type的描述:

Java注解基础_第2张图片


Target()注解是接收的值是一个 数组 类型 , 即{}如果值只有 一个 使用 等号也是可以的


1.4 内置注解的保留策略和修饰的元素类型

注解名 描述
@Override 一、 @Override, 保留在源程序阶段,@Override编译器查看,编译完成就没有用了;
二、 用于方法重写;保留策略只有ElementType.METHOD
@SuppressWarnings 一、@SuppressWarnings ,也是给编译器查看的,编译完成就没有用了,也是源码阶段;
二、TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE; 无ANNOTATION_TYPEpackage
@Deprecated 一、@Deprecated, 这个是程序中需要调用其他已经写好的字节码来查看是否是过时的。编译器是需要获取其他的字节码。所以需要保留在运行时;
二、除了ANNOTATION_TYPE 所有其他元素类型

1.5 注解可以使用那些类型

八个基本类型String ,Class, 枚举,Annotation以及前面这些类型的数组。注解也可以作为元素类型,也就是注解嵌套任何包装类型是不可以的

Java注解基础_第3张图片


1.6 其他

没有任何元素的注解称为 标记注解(marker annotation)


了解了注解的基本知识点,如何自定义一个注解呢?

二、自定义注解

使用@interface,自动继承 java.lang.annotation.Annotation, 格式: public @interface 注解名 {.定义主体..}

其中的每一个方法实际上是声明了一个配置参数

  • 方法的名称就是参数的名称;
  • 返回值类型就是参数的类型;
  • 可以通过default来声明参数的默认值;
  • 如果只有一个参数成员,一般参数名为value.

Java注解基础_第4张图片


自定义注解:

在定义的时候是以方法的形式,使用注解的时候,以属性赋值的形式。取值的时候也像调用方法一样调用

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 自定义注解; Retention在运行时;应用在方法和构造器上;
 *
 * 如果只有一个参数,推荐使用value;在使用该注解的时候,可以不指定参数名,直接给参数值即可。
 * 如果有多个参数,在使用的的时候,要写清楚名称和值(name=value,name=value)
 *
 * 在定义注解时,经常使用空字符串、0 做为默认值,经常使用负数(-1)表示不存在
 *
 */
@Retention(value = RUNTIME)
@Target({METHOD, CONSTRUCTOR})
public @interface MyAnnotation {

    //使用 default 给定默认值
    String studentName() default "";

    // 定义数组类型,数组类型的默认值{}
    String[] hobbies() default {"program","game"};

    //定义一个参数;不给默认值,在使用的时候就必须传递一个值;
    int  age();
}

class TestMyAnnotation {

    @MyAnnotation(age=10,studentName = "MR.HU")
    public  void  getInfo() {
    }

}

光定义注解是没什么用的,还需要给这些注解附上解析,通过其他程序解析,这样才发挥其作用;否则,用处不大。

三、反射解析注解

3.1 API

接口 描述
AnnotatedElement(接口) 是所有程序元素(Class、Method、Constructor等)的父接口
< T extends Annotation> T getAnnotation(Class< T > annotationClass) 如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
getAnnotations() 返回此元素上存在的所有注解。
getDeclaredAnnotations() 返回直接存在于此元素上的所有注释
isAnnotationPresent(Class annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。

:这四个方法都是在java.lang.reflect.AnnotatedElement接口中定义的方法

所有实现类:AccessibleObject, Class, Constructor,Field, Method, Package


3.2 案例分析

使用注解完成数据表之间的映射关系。应用案例如下:

  • 定义两个注解,字段 注解

  • 表注解有一个表名参数(用于解析数据表名称),字段注解有三个参数:name、type、length(三个必要属性)

  • 通过反射解析这个两个注解,并拼接一个简单的sql语句。


(一)表注解

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
public @interface TableAnnotation {

    String  tableName() default  "";
}

(二)字段注解

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD })
public @interface FieldAnnotation {

    String columnName() default "";

    String columnType() default "";

    int  columnLenth() default 0;

}

(三)注解解析

public class ParseAnnotation {

    //拼接创建表的简单语句
    public static String createTableSql(String className) {
        Class clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //获取的类的指定注解
        TableAnnotation tableAnnotation = (TableAnnotation) clazz.getAnnotation(TableAnnotation.class);
        String tableName = tableAnnotation.tableName();
        //获取类属性的所有注解;拼接一个字字符串
        StringBuffer sqlBody = new StringBuffer();
        StringBuffer fullSql = new StringBuffer();
        Field[] fields = clazz.getDeclaredFields();//获取所有属性,包括private
        for(Field field:fields) {
            FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
            String name = fieldAnnotation.columnName();
            String type  = fieldAnnotation.columnType();
            int length = fieldAnnotation.columnLenth();
            sqlBody.append(name).append(" ").append(type).append(" ").append("(").append(length).append(")").append(",");
        }
        //去掉sqlBody 最后一个逗号
        String sqlContent  = (String) sqlBody.subSequence(0,sqlBody.length()-1);
        //拼接一个完整的创建语句
        fullSql.append("create table ").append(tableName).append("(").append(sqlContent).append(")");
        return fullSql.toString();
    }

}

(四) 应用测试

public class TestAnnotation {
    public static void main(String[] args) throws ClassNotFoundException {
        String sql = ParseAnnotation.createTableSql("orm.Student");
        //create table t_student(id int (10),name varchar (20),age int (3))
        System.out.print(sql);
        //通过JDBC创建表了
    }
}

总结

用一张图来总结注解

Java注解基础_第5张图片

图片借用地址:深入理解Java:注解(Annotation)–注解处理器

你可能感兴趣的:(注解)