Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能
注解起到辅助性的作用,包含在 java.lang.annotation 包中
@Deprecated : 用于表示某个程序元素(类、方法等)已过时,当其它程序使用已过时的类、方法时,编译器将会给出警告
@SuppressWarnings : 指示被Annotation标识的程序元素(以及在该程序元素中的所有子元素)取消显示指定的编译器警告。@SuppressWarnings会一直作用于该程序元素的所有子元素,例如使用@SuppressWarnings标识的一个类来取消显示某个编译器警告,同时又标识该类里某个方法取消显示另一个编译器警告,那么在此方法中同时取消显示这两个编译器警告。通常情况下如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用@SuppressWarnings这个注解
注 : 使用注解时要在前面增加@符号,并把 Annotation 当成一个修饰符使用用于修饰它支持的程序元素
metadata(
元数据
)
元数据从metadata一词译来,就是“关于数据的数据”的意思
元数据的功能作用有很多,比如:你可能用过 Javadoc 的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过可以根据它所起的作用,大致可分为三类 :
1. 编写文档 : 通过代码里标识的元数据生成文档
2. 代码分析 : 通过代码里标识的元数据对代码进行分析
3. 编译检查 : 通过代码里标识的元数据让编译器能实现基本的编译检查
在 Java 中元数据以标签的形式存在于Java代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或针在运行时知道被运行代码的描述信息
综上所述 :
第一
:
元数据以标签的形式存在于Java代码中
第二
:
元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的
第三
:
元数据需要编译器之外的工具额外的处理用来生成其它的程序部件
第四
:
元数据可以只存在于Java源代码级别,也可以存在于编译之后的Class文件内部
Annotation 和 Annotation类型
Annotation :
使用了在 java5.0 所带来的新语法,它的行为十分类似public、final这样的修饰符。每个Annotation具有一个名字和成员个数 >= 0。每个Annotation的成员具有被称为 name=value 对的名字和值(就像javabean一样),name=value 装载了Annotation的信息
Annotation类型 : 类型定义了Annotation的名字、类型、成员默认值。一个Annotation类型可以说是一个特殊的 java接口,它的成员变量是受限制的,而声明 Annotation类型时需要使用新语法。当通过 java反射api访问Annotation时,返回值将是一个实现该 annotation类型接口的对象,通过访问这个对象能方便的访问到其Annotation成员
注解的分类
根据注解参数的个数,我们可以将注解分为三类 :
1> 标记注解 : 一个没有成员定义的Annotation类型被称为标记注解。这种Annotation类型仅使用自身的存在与否来提供信息。比如系统注解@Override
2> 单值注解
3> 完整注解
根据注解使用方法和用途,可以将Annotation分为三类 :
1> JDK内置系统注解
2> 元注解
3> 自定义注解
系统内置标准注解
注解的语法比较简单,除了@符号的使用外他基本与Java固有的语法一致,JavaSE中内置三个标准注解,定义在java.lang中 :
@Override :
用于修饰此方法覆盖了父类的方法
@
Override是一个标记注解类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果使用这种Annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。这个annotaton常常在试图覆盖父类方法而确又写错了方法名时发挥威力。使用方法极其简单:在使用此annotation时只要在被修饰的方法前面加上@Override即可
@Deprecated :
用于修饰已经过时的方法
@
Deprecated 也是一个标记注解。当一个类型或者类型成员使用 @Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果在代码中通过继承或者覆盖的方式使用这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。
值得注意,@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的 : 前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过 时、它应当如何被禁止或者替代的描述)。
在java5.0,java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag,并使用它们产生警告信息。但是这种状况将在后续版本中改变,应在现在就开始使用@Deprecated来修饰过时的方法而不是 @deprecated javadoc tag
@SuppressWarnnings : 用于通知java编译器禁止特定的编译警告
@SuppressWarnings 被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0,sun提供的javac编译器为我们提供 -Xlint 选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。例如当使用一个 generic collection类而又没有提供它的类型时,编译器将提示出"unchecked warning"的警告。通常当这种情况发生时,就需要查找引起警告的代码。如果它真的表示错误,就需要纠正它。例如如果警告信息表明代码中的switch语句没有覆盖所有可能的case,那么就应增加一个默认的case来避免这种警告。
有时无法避免这种警告,例如,使用必须和非generic的旧代码交互的generic collection类时,不能避免这个unchecked warning。此时@SuppressWarning就要派上用场了,在调用的方法前增加@SuppressWarnings修饰,告诉编译器停止对此方法的警告。
SuppressWarning不是一个标记注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告 名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名
SuppressWarnings注解的常见参数值的简单说明
1.deprecation : 使用了不赞成使用的类或方法时的警告
2.unchecked : 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型
3.fallthrough : 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
4.path : 在类路径、源文件路径等中有不存在的路径时的警告
5.serial : 当在可序列化的类上缺少 serialVersionUID 定义时的警告
6.finally : 任何 finally 子句不能正常完成时的警告
7.all : 关于以上所有情况的警告
元注解
用于注解的注解,包括四种 :
1>
@
Retention : 定义注解的保留策略
@
Retention(RetentionPolicy.SOURCE) : 注解仅存在于源码中,在class字节码文件中不包含
@
Retention(RetentionPolicy.CLASS) : 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME) : 注解会在class字节码文件中存在,在运行时可以通过反射获取到
注 :
使用注解功能时,如果需要用反射读取注解,就必须设置@Retention(RetentionPolicy.RUNTIME),因为默认情况下为CLASS,读取的时候会报异常
2> @Target : 定义注解的作用目标
@Target({ElementType.TYPE}) : 接口、类、枚举、注解
@Target({ElementType.FIELD}) : 字段、枚举的常量
@Target({ElementType.METHOD}) : 方法
@Target({ElementType.PARAMETER}) : 方法参数
@Target({ElementType.CONSTRUCTOR}) : 构造函数
@Target({ElementType.LOCAL_VARIABLE}) : 局部变量
@Target({ElementType.ANNOTATION_TYPE}) : 注解
@Target({ElementType.PACKAGE}) : 包
注 : @Target 可以包含一个或多个
3>
@Document :
说明该注解将被包含在javadoc中,
作用和
javadoc
差不多,其实它存在的好处是开发人员可以定制Javadoc不支持的文档属性,并在开发中应用
@Documented
@Retention
(RetentionPolicy.
RUNTIME
)
@Target
({ElementType.
METHOD
, ElementType.
CONSTRUCTOR
})
public
@
interface
TestAnntation
{
}
4> @Inherited : 说明子类可以继承父类中的该注解,可
控制注释是否会影响到子类
@Inherited
@Documented
@Retention
(RetentionPolicy.
RUNTIME
)
@Target
({ElementType.
METHOD
, ElementType.
CONSTRUCTOR
})
public
@
interface
TestAnntation
{
}
自定义 Annotation
定义新的 Annotation类型使用 @interface关键字定义一个新的 Annotation类型与定义一个接口非常像,定义该Annotation之后就可以使用该Annotation。Annotation可用于修饰任何程序元素,包括类、接口、方法等
注 : 使用自定注解需要使用 @Retention(RetentionPolicy.RUNTIME),这样才能获取注解中的值
定义成员变量
Annotation 的成员变量在 Annotation 的成员变量在 Annotation定义中以无参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型,如 :
public
@
interface
TestAnntation
{
/**
* 姓名
*
*
@return
*/
String name();
/**
* 年龄
*
*
@return
*/
int
age();
/**
* 0:未知 1:男 2:女
*
*
@return
*/
int
gender()
default
0
;
}
使用
public class
Test {
@TestAnntation
(name =
"abc"
, age =
100
)
public void
info() {
}
}
注 : 在定义Annotation成员变量时可以指定默认值使用 default关键字,此时如 name后面的 ="xx" 以及 age 后面的就都不需要,如果没有设置默认值在使用注解时则必须添加该属性
AnnotationElement 接口代表程序中可以接受注解的程序元素
为变量赋默认值
public
@
interface
TestAnntation
{
public enum
FontColor {
RED
,
GREEN
,
BLUE
}
FontColor fontColor()
default
FontColor.
BLUE
;
}
注 :
Java提供了一个ElementType枚举类型来控制每个注释的使用范围,如上面案例中注解中 fontColor 只能使用 FontColor 值
读取注释信息
当读取某个注释信息时,在运行时通过反射来实现的,如果对元注释还有点印象,应需要将保持性策略设为
@Retention(RetentionPolicy.RUNTIME)
并发注解
Java并发编程中,用到了一些专门为并发编程准备的 Annotation
主要包括三类 :
1> 类 Annotation(注解)
就像名字一样,这些注解是针对类的,主有要以下三个 :
@Immutable : 类是不可变的,包含 @ThreadSafe的意思
@ThreadSafe : 表示这个类是线程安全的,具体是否真安全,那要看实现者怎么实现,打上这个标签只是表示一下。不线程安全的类打上这个注解也没事
@NotThreadSafe : 表示这个类不是线程安全的。如果是线程安全的非要打上这个注解,那也不会报错
这三个注解,对用户和维护者是有益的,用户可以立即看出来这个类是否是线程安全的,维护者则是可以根据这个注解,重点检查线程安全方面。另外,代码分析工具可能会利用这个注解
2> 域 Annotation(注解)
域注解是对类里面成员变量加的注解
3> 方法 Annotation(注解)
方法注解是对类里面方法加的注解。域注解和方法注解都是用 @GuardedBy( lock )来标识。里面的Lock是告诉维护者 : 这个状态变量,这个方法被哪个锁保护着。这样可以强烈的提示类的维护者注意这里。
@GuardedBy( lock )有以下几种使用形式:
<1> @GuardedBy("this") : 受对象内部锁保护
<2> @GuardedBy("fieldName") : 受与fieldName引用相关联的锁保护
<3> @GuardedBy("ClassName.fieldName") : 受一个类的静态field的锁保存
<4> @GuardedBy("methodName()") : 锁对象是 methodName() 方法的返值,受这个锁保护
<5> @GuardedBy("ClassName.class") : 受 ClassName类的直接锁对象保护,而不是这个类的某个实例的锁对象
AnnotatedElement 接口是所有程序元素 (Class、Method和Constructor) 的父接口,所以程序通过反射获取某个类的AnnotatedElement 对象之后,程序就可以调用该对象的如下四个个方法来访问 Annotation信息 :
方法1 : <T extends Annotation> T getAnnotation(Class<T> annotationClass) : 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null
方法2 : Annotation[] getAnnotations() : 返回该程序元素上存在的所有注解
方法3 : boolean is AnnotationPresent(Class<?extends Annotation> annotationClass) : 判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false
方法4 : Annotation[] getDeclaredAnnotations() : 返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响
如下实例 :
@Target
(ElementType.
FIELD
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Documented
public
@
interface
FruitProvider
{
/** 供应商编号 */
public int
id()
default
-
1
;
/** 供应商名称 */
public
String name()
default
""
;
/** 供应商地址 */
public
String address()
default
""
;
}
@Target
(ElementType.
FIELD
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Documented
public
@
interface
FruitName
{
String value()
default
""
;
}
@Target
(ElementType.
FIELD
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Documented
public
@
interface
FruitColor
{
/**
颜色枚举
*/
public enum
Color {
BULE
,
RED
,
GREEN
}
/** 颜色属性 */
Color fruitColor()
default
Color.
GREEN
;
}
public class
Apple {
@FruitName
(
"Apple"
)
private
String
appleName
;
@FruitColor
(fruitColor =
FruitColor
.Color.
RED
)
private
String
appleColor
;
@FruitProvider
(id =
1
, name =
"陕西红富士集团"
, address =
"陕西省西安市延安路89号红富士大厦"
)
private
String
appleProvider
;
public void
setAppleColor(String appleColor) {
this
.
appleColor
= appleColor;
}
public
String getAppleColor() {
return
appleColor
;
}
public void
setAppleName(String appleName) {
this
.
appleName
= appleName;
}
public
String getAppleName() {
return
appleName
;
}
public void
setAppleProvider(String appleProvider) {
this
.
appleProvider
= appleProvider;
}
public
String getAppleProvider() {
return
appleProvider
;
}
public void
displayName() {
System.
out
.println(
"水果的名字是:苹果"
);
}
}
public class
FruitInfoUtil {
public static void
getFruitInfo(Class<?> clazz) {
String strFruitName =
" 水果名称:"
;
String strFruitColor =
" 水果颜色:"
;
String strFruitProvicer =
"供应商信息:"
;
Field[] fields = clazz.getDeclaredFields();
for
(Field field : fields) {
if
(field.isAnnotationPresent(
FruitName
.
class
)) {
FruitName
fruitName = (
FruitName
) field.getAnnotation(
FruitName
.
class
);
strFruitName = strFruitName + fruitName.value();
System.
out
.println(strFruitName);
}
else if
(field.isAnnotationPresent(
FruitColor
.
class
)) {
FruitColor
fruitColor = (
FruitColor
) field.getAnnotation(
FruitColor
.
class
);
strFruitColor = strFruitColor + fruitColor.fruitColor().toString();
System.
out
.println(strFruitColor);
}
else if
(field.isAnnotationPresent(
FruitProvider
.
class
)) {
FruitProvider
fruitProvider = (
FruitProvider
) field.getAnnotation(
FruitProvider
.
class
);
strFruitProvicer =
" 供应商编号:"
+ fruitProvider.id() +
" 供应商名称:"
+ fruitProvider.name() +
" 供应商地址:"
+ fruitProvider.address();
System.
out
.println(strFruitProvicer);
}
}
}
}
public class
FruitRun {
public static void
main(String[] args) {
FruitInfoUtil.
getFruitInfo
(Apple.
class
);
}
}
水果名称:Apple
水果颜色:RED
供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦