《JAVA编程思想》学习笔记:第20章(注解)

第二十章、注解

前言

注解Annotation:又叫元数据,JDK5引入的一种以通用格式为程序提供配置信息的方式。

使用注解Annotation可以使元数据写在程序源码中,使得代码看起来简洁,同时编译器也提供了对注解Annotation的类型检查,使得在编译期间就可以排除语法错误。

注解使得我们能够以将由编译器来测试和验证的格式,存储有关程序的额外信息。

注解可以用来生成描述符文件,甚至是新的类定义,并且有助于减轻编写"样板"代码的负担。

JDK内置的3中Annotation

在JDK5中,内置了3个通用目的的注解Annotation,这三个内置的注解在java.lang包下:

@Override:这个注解常用在继承类或实现接口的子类方法上,表面该方法是子类覆盖父类的方法,该方法的方法签名要遵循覆盖方法的原则:即访问控制权限必能比父类更严格,不能比父类抛出更多的异常。

@Deprecated:这个注解告诉编译器该元素是过时的,即在目前的JDK版本中已经有新的元素代替该元素。

@SuppressWarnings:该注解关闭编译器中不合适的警告,即强行压制编译器的警告提示。

1. 基本语法

1.1 定义注解

标记注解(marker annotation):没有元素的注解,如@Test void test(){}

元注解(meta-annotation):在声明注解的时候往往需要使用@Target,@Retention等注解,这种注解被称为注解的注解(元数据注解),即是专门用于处理注解Annotation本身的。

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Test{}

1.2 元注解

Java只内置三个标准注解(如上所述),和下列4个元注解:

@Target注解

用于指示注解所应用的目标程序元素种类,该注解常和ElementType枚举类型一起联合使用,ElementType枚举提供了java程序中声明的元素类型如下:

ANNOTATION_TYPE:注释类型声明。

CONSTRUCTOR:构造方法声明。

FIELD:字段声明(包括枚举常量)。

LOCAL_VARIABLE:局部变量声明。

METHOD:方法声明。

PACKAGE:包声明。

PARAMETER:参数声明。

TYPE::类,接口或枚举声明。

@Retention注解

该注解用于指示所定义的注解类型的注释在程序声明周期中得保留范围,该注解常和RetentionPolicy枚举联合使用。RetentionPolicy枚举常量定义了注解在代码中的保留策略

CLASS:编译器把注解记录在类文件中,但在运行时JVM不需要保留注解。

RUNTIME:编译器把注解记录在类文件中,在运行时JVM将保留注解,因此可以通过反射机制读取注解。

SOURCE:仅保留在源码中,编译器在编译时就要丢弃掉该注解。

@Documented

将此注解包含在Javadoc中

@Inherited

允许子类继承父类中的注解

2. 编写注解处理器

使用注解的时候,很重要的一部分就是创建&使用注解处理器。Java提供了一个外部工具apt用来解析带有注解的Java源代码包。

正常使用注解时,需要在注解中定义元素,用于接收程序设置的值,正常定义注解的例子如下:

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface UseCase{

    public int id();

    public String description() default “no description”; //可以定义属性默认值

}

正常定义注解Annotation的方式类似定义接口,id和description是注解UseCase的属性,而不是方法,注解中不能定义方法只能定义属性。其中description属性有默认的值“no description“,即在使用时如果没有指定description的值,则程序使用其默认值。

上面UseCase注解用于跟踪方法的测试用例说明,使用上面注解的例子如下:

public class PasswordUtils{

    @UseCase(id = 47, description = “Passwords must contain at least one numeric”)

    public Boolean validatePassword(String password){

        return (password.mathes(“\\w*\\d\\w*”));

    }

    @UseCase(id = 48)

    public String encryptPassword(Srring password){

        return new StringBuilder(password).reverse().toString();

    }

    @UseCase(id = 49, description = “New passwords can’t equal previously used ones”)

    public Boolean checkForNewPassword(List<String> prevPasswords, String password){

        return !prevPasswords.contains(password);

    }

}

JDK5中提供了Annotation相关的API,结合使用java的反射机制可以实现自定义的Annotation注解处理器(JDK中也提供了使用APT,Annotationprocess tool方式处理注解,在后面会讲解),处理上述Annotation的例子如下:

public class UseCaseTracker{

public static void traceUseCases(List<Integer> useCases, Class<?> clazz){

//获取指定类中所有声明的方法

for(Method m : clazz.getDeclaredMethods()){

//获取方法上指定类型的注解

public class UseCaseTracker{ 

    public static void traceUseCases(List<Integer> useCases, Class<?> clazz){ 

        //获取指定类中所有声明的方法 

        for(Method m : clazz.getDeclaredMethods()){ 

            //获取方法上指定类型的注解 

            UseCase uc = m.getAnnotation(UseCase.class); 

            if(uc != null){ 

                System.out.println(“Found Use Case:” + uc.id() + “ ” + uc.description()); 

                useCases.remove(new Integer(uc.id())); 

            } 

        } 

        for(int i : useCases){ 

            System.out.println(“Warning: Missing use case-” + i); 

        } 

    } 

    public static void main(String[] args){ 

        List<Integer> useCases = new ArrayLis<Integer>(); 

        Collections.addAll(useCases, 47, 48, 49, 50); 

        trackUseCases(useCases, PasswordUtils.class); 

    } 

这个程序用到了两个反射的方法:getDeclaredMethods()和m.getAnnotation(UseCase.class):

getDeclaredMethods() 返回类声明的方法

m.getAnnotation(UseCase.class) 返回指定类型的注解对象。如果被注解的方法上没有该类型的注解,则返回null值。

2.1 Annotation注解元素

Annotation注解中的元素只能是下面的数据类型:

所有的基本类型(int,float,boolean等): java的8种基本类型,如int, boolean等等,如果可以自动装箱和拆箱,则可以使用对应的对象包装类型。

String

Class

enum

Annotation

上面类型的数组

除了上面这些类型以外,如果在注解中定义其他类型的数据,编译器将会报错。

2.2 默认值限制

注意:注解中的元素要么指定默认值,要么由使用的类赋值,如果即没有默认值,使用类也没有赋值的话,注解元素是不会像普通类成员变量一样给定默认值,即必须赋值或者显示指定默认值。默认值例子如下:

@Target(ElementType.METHOD) 

@Retention(RetentionPolicy.RUNTIME) 

public @interface DefaultValue{ 

    public int id() default -1; 

    public String description() default “”; 

2.3 生成外部文件

Enterprise JavaBean这样的技术,每一个Bean都需要大量的接口和部署来描述文件,这些都属于样板文件。然而,如果我们使用注解的话,可以将所有信息都保存在javaBean中的源文件中。为此,我们需要一些新的注解,用以定义与Bean相连的数据库表的名字,以及与Bean属性管理的列的名字和SQL类型。

@Target(ElementType.TYPE)//该注解只能应用在类上 

@Retention(RetentionPolicy.RUNTIME) 

public @interface DBTable{//指定数据库名称 

    public String name() default “”; 


@Target(ElementType.FIELD) 

@Retention(RetentionPolicy.RUNTIME) 

public @interface Constraints{//数据库约束 

    boolean primaryKey() default false; 

    boolean allowNull() default true; 

    boolean unique() default false; 


@Target(ElementType.FIELD) 

@Retention(RetentionPolicy.RUNTIME) 

public @interface SQLString{//String类型数据 

    int value() default 0; 

    String name() default “”; 

    Constraints constraints() default @Constraints;//注解的属性元素也是注解 


@Target(ElementType.FIELD) 

@Retention(RetentionPolicy.RUNTIME) 

public @interface SQLInteger{//int类型数据 

    String name() default “”; 

    Constraints constraints() default @Constraints; 

2.4 注解不支持继承

不能使用关键字extends 来继承某个@interface

3 使用apt处理注解

apt:注解处理工具,是Sun为了帮助注解的处理过程而提供的工具;

同javac一样,apt被设计为操作Java源文件,而不是编译后的类。

使用apt生成注解处理器时,我们无法使用Java的反射机制,因为我们操作的是源代码,额不是编译后的文件。

4. 将观察者模式用于apt

5. 基于注解的单元测试

单元测试:是对类中的每个方法提供一个或多个测试的一种实践,目的是为了有规律地测试一个类的各个方法是否具备正确的行为。

单元测试工具:Java中,最著名的单元测试工具JUnit。

5.1 将@Unit用于泛型

5.2 不需要任何“套件”

5.3 实现@Unit

5.4 移除测试代码

添加@Test注解,并使用Javassist工具类库将字节码工程,删除所有的@Test注解。(??)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容