Java注解及自定义注解
注解/元数据(Annotation),是对代码级别的说明;在JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释;主要可以用于创建文档,跟踪代码中的依赖性,执行基本编译时检查;注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类;它们都不会直接影响到程序的语义,只是作为标识存在,可以通过反射机制实现对这些元数据(用来描述数据的数据)的访问并且可以在编译时选择代码里的注解是否只存在于源码级,或者也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。
一、按照作用Annotation有三种作用
1、编写文档(文档注解):通过代码里标识的注解生成文档【生成doc文档,通过 javadoc 类文件名称 的方式生成对应的html文档,文档中会将文档注解进行解析生成内容】
编写类文件,并使用文档注解进行标注:
使用 javadoc 类文件名称 进行生成文档(这一步可能会报错 错误: 编码GBK的不可映射字符 ,原因是 java程序在编译的时候,需要使用JDK开发工具包中的JAVAC.EXE命令,默认编码格式为UNICODE,而我们的代码并非这个编码格式,解决方案是比如使用 notepad++ 编写Java代码前,先将Java文件的编码格式改为 ANSI编码格式 ,然后编写代码并保存,重新执行javadoc即可(如果没有报错但生成的文档打开中文乱码也可以采用该方法解决)):
打开 index.html 文件如下:
2、代码分析(功能注解):通过代码里标识的注解对代码进行分析【使用反射实现该功能,最重要的功能】
3、编译检查(编译验证注解):通过代码里标识的注解让编译器能够实现基本的编译检查【如:Override,表示这个方法是对父类方法的重写,如果被@Override注解标记的方法父类不存在该方法,那么编译无法通过、编码软件也会提示错误】
二、Java常见内置注解
1、@Override:作用是对覆盖超类中方法的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出错误警告。
2、@Deprecated:作用是对不应该再使用的方法进行标记(标记方法过时,比如有新版本的方法之后不建议使用以前版本的方法),使用这些方法时,将会在编译时显示提示信息,这些过时方法也是可以正常使用的,只是被提示标记而已,与javadoc里的@deprecated注解的功能相同,但 javadoc @deprecated 可以支持参数。
3、@SuppressWarnings:作用是压制警告;可选值有以下几种(写哪种值就代表压制哪种类型的警告):
deprecation:使用了过时的类或方法时的警告(即被@Deprecated标记的类或方法)
unchecked:执行了未检查的转换时的警告(如使用集合时没有用泛型 (Generics) 来指定集合保存的类型)
fallthrough:当 switch 程序块直接通往下一种情况而没有 break 时的警告
path:在类路径、源文件路径等中有不存在的路径时的警告
serial:当在可序列化的类上缺少serialVersionUID 定义时的警告
finally :任何 finally 子句不能正常完成时的警告
rawtypes:泛型类型未指明
unused 引用定义了,但是没有被使用
all:关于以上所有情况的警告
三、元注解
元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型他们被用来提供对其他annotation类型作说明,
这些类型和它们所支持的类在java.langannotation包中可以找到(@Target,@Retention,@Documented,@Inherited )。
@Target:用于描述注解的使用范围(即:@Target元注解用来设置注解可以用在什么地方),经常使用最主要的就三个:TYPE、METHOD、FIELD
package java.lang.annotation;
/**
* The constants of this enumerated type provide a simple classification of the
* syntactic locations where annotations may appear in a Java program. These
* constants are used in {@link Target java.lang.annotation.Target}
* meta-annotations to specify where it is legal to write annotations of a
* given type.
*
* <p>The syntactic locations where annotations may appear are split into
* <em>declaration contexts</em> , where annotations apply to declarations, and
* <em>type contexts</em> , where annotations apply to types used in
* declarations and expressions.
*
* <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
* #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
* {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
* to the declaration contexts in JLS 9.6.4.1.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
* field declaration.
*
* <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
* 4.11, as well as to two declaration contexts: type declarations (including
* annotation type declarations) and type parameter declarations.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field
* (or within the type of the field, if it is a nested, parameterized, or array
* type), and may also appear as a modifier for, say, a class declaration.
*
* <p>The {@code TYPE_USE} constant includes type declarations and type
* parameter declarations as a convenience for designers of type checkers which
* give semantics to annotation types. For example, if the annotation type
* {@code NonNull} is meta-annotated with
* {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull}
* {@code class C {...}} could be treated by a type checker as indicating that
* all variables of class {@code C} are non-null, while still allowing
* variables of other classes to be non-null or not non-null based on whether
* {@code @NonNull} appears at the variable's declaration.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 4.1 The Kinds of Types and Values
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration 类, 接口 (包括注释类型), 或 枚举 声明*/
TYPE,
/** Field declaration (includes enum constants) 字段声明(包括枚举常量)*/
FIELD,
/** Method declaration 方法声明(Method declaration)*/
METHOD,
/** Formal parameter declaration 正式的参数声明*/
PARAMETER,
/** Constructor declaration 构造函数声明*/
CONSTRUCTOR,
/** Local variable declaration 局部变量声明*/
LOCAL_VARIABLE,
/** Annotation type declaration 注解类型声明*/
ANNOTATION_TYPE,
/** Package declaration 包声明*/
PACKAGE,
/**
* Type parameter declaration 类型参数声明
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type 使用的类型
*
* @since 1.8
*/
TYPE_USE
}
@Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期 (可选值:SOURCE <CLASS < RUNTIME)
@Documented:说明该注解将被包含在javadoc中(即注解是否被抽取到api文档中,使用 javadoc 类文件名称 命令生成文档时将该注解是否保留在文档中)
@Inherited:说明子类可以继承父类中使用的被@Inherited注解标记的注解(比如:A类是B类的父类,如果在A类上使用被@Inherited元注解声明的注解,那么B类也会继承该注解,会自动加上该注解)
四、自定义注解
1、格式:
元注解
public @interface 注解名称{
注解属性列表(即接口中的抽象方法)
}
package com.database.pool.testpool.annotation;
import java.lang.annotation.*;
/**
* 测试注解1
*/
@Target(value = ElementType.TYPE) //元注解定义当前注解可使用范围
@Retention(value = RetentionPolicy.RUNTIME) //元注解定义当前注解生命周期
@Inherited //元注解定义子类可以继承父类中使用的该注解
public @interface Test1Annotation {
//属性
String value();
}
2、本质 :注解本质上就是一个接口,该接口默认继承Annotation接口
public interface Test1Annotation extends java.lang.annotation.Annotation {}
3、注解属性说明 : 接口中的抽象方法
a、属性(抽象方法)返回值要求,只能是以下几种类型:
- 八大基本数据类型
- String
- 枚举
- 注解
- 以上四种类型的数组
b、使用注解时需要给属性赋值要求如下:
- 定义了属性使用时就必须给属性赋值(格式:@注解名(属性名1=属性值1,属性名2=属性值2))。
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行该属性的赋值会自动使用默认值。
- 使用注解时如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可也就是说注解只有一个value名称的属性或注解有多个属性,但这些属性都有默认值,所以使用时只需要对value属性赋值的话也可以直接写值。
- 数组属性赋值时,值使用{}包裹属性值,如果数组中只有一个值,则{}省略,直接写值。
建议注解如果只有一个属性,那么将该属性名定义为value。
示例:
自定义注解:
package com.database.pool.testpool.annotation;
import com.database.pool.testpool.User;
import java.lang.annotation.*;
/**
* 测试注解1
*/
@Target(value = ElementType.TYPE) //元注解定义当前注解可使用范围
@Retention(value = RetentionPolicy.RUNTIME) //元注解定义当前注解生命周期
@Inherited //元注解定义子类可以继承父类中的该注解,子类可以继承父类使用的注解
public @interface Test1Annotation {
/**
* 属性(抽象方法)定义
* @return
*/
int paramInt();
//default 标记属性默认值,使用注解时该属性如果不做更改使用默认值时可以不设置该属性的值
String paramStr() default "c";
//返回类型是另一个注解类型
Test1Annotation2 paramAnn();
//返回类型是枚举
User paramUser();
String[] paramStrArr();
//value名称的属性,在使用注解时如果只有value属性设置值则可以省略属性名value直接写值
String value();
}
测试注解2:
package com.database.pool.testpool.annotation;
import java.lang.annotation.*;
/**
* 测试注解2,用于给测试注解1的属性设置返回值为注解类型
*/
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Test1Annotation2 {
}
枚举:
package com.database.pool.testpool;
/**
* 定义枚举,用于设置测试注解1的属性返回值为枚举类型
*/
public enum User {
U1,U2;
}
使用注解:
package com.database.pool.testpool;
import com.database.pool.testpool.annotation.Test1Annotation;
import com.database.pool.testpool.annotation.Test1Annotation2;
/**
* 测试注解1使用类
*/
@Test1Annotation(paramInt = 1,paramAnn = @Test1Annotation2,paramStrArr = {"a","b"},paramUser = User.U1,value = "x")
public class TestAnn {
}
解析注解:
package com.database.pool.testpool;
import com.database.pool.testpool.annotation.Test1Annotation;
/**
* 反射解析自定义注解---测试注解1
*/
public class Test {
public static void main(String[] args) {
//获取Class对象
Class<TestAnn> aClass = TestAnn.class;
//获取类级别的Test1Annotation类型的注解,实际上返回值是一个实现了Test1Annotation注解接口的类的对象,里面有获取各个属性的值的方法
Test1Annotation annotation = aClass.getAnnotation(Test1Annotation.class);
//拿到注解信息后就可以根据注解信息做各种操作
int i = annotation.paramInt();
System.out.println("paramInt:"+i);
String s = annotation.paramStr();
System.out.println("paramStr:"+s);
String value = annotation.value();
System.out.println("value:"+value);
}
}
总结:
1、程序开发中大部分时候是使用注解,而不是自定义注解
2、注解给谁用 ? 编译器(编译器识别注解),给解析程序用(解析程序通过解析注解实现自定义功能)
3、注解不是程序的一部分(相当于是给程序做了一个标记,然后通过反射机制实现注解功能,对程序原有功能做增强等)
Java反射
Java泛型