标签(空格分隔):java 注解
什么是java注解?
Annotation
(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata
)的途径和方法。Annotion
(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion
对象,然后通过Annotio
n对象来获取注解里面的元数据。
元数据从metadata
一词译来,就是“关于数据的数据”的意思。
元数据的功能作用有很多,比如:你可能用过Javadoc
的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
- 1.编写文档:通过代码里标识的元数据生成文档
- 2.代码分析:通过代码里标识的元数据对代码进行分析
- 3.编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查
在Java中元数据以标签的形式存在于Java
代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或针在运行时知道被运行代码的描述信息。综上所述:
- 1.元数据以标签的形式存在于
Java
代码中。 - 2.元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
- 3.元数据需要编译器之外的工具额外的处理用来生成其它的程序部件。
- 4.元数据可以只存在于
Java
源代码级别,也可以存在于编译之后的Class
文件内部。
Annotation
其实是一种接口。通过Java
的反射机制相关的API
来访问annotation
信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。annotation
是不会影响程序代码的执行,无论annotation
怎么变化,代码都始终如一地执行。Java
语言解释器在工作时会忽略这些annotation
,因此在JVM
中这些annotation
是“不起作用”的,只能通过配套的工具才能对这些annontaion
类型的信息进行访问和处理。- 包
java.lang.annotation
中包含所有定义自定义注解所需用到的原注解和接口。如接口java.lang.annotation.Annotation
是所有注解继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object
。该包同时定义了四个元注解,Documented
,Inherited
,Target
(作用范围,方法,属性,构造方法等),Retention
(生命范围,源代码,class
,runtime
)。Annotation
与interface
的异同: - 1.Annotation类型使用关键字
@interface
而不是interface
。这个关键字声明隐含了一个信息:它是继承了java.lang.annotation.Annotation
接口,并非声明了一个interface
; - 2.
Annotation
类型、方法定义是独特的、受限制的。
Annotation
类型的方法必须声明为无参数、无异常抛出的。这些方法定义了annotation
的成员:方法名成为了成员名,而方法返回值成为了成员的类型。而方法返回值类型必须为primitive
类型、Class
类型、枚举类型、annotation
类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用default
和一个默认数值来声明成员的默认值,null
不能作为成员默认值,这与我们在非annotation
类型中定义方法有很大不同。
Annotation
类型和它的方法不能使用annotation
类型的参数、成员不能是generic
。只有返回值类型是Class
的方法可以在annotation
类型中使用generic
,因为此方法能够用类转换将各种类型转换为Class
。 - 3.
Annotation
类型又与接口有着近似之处。
它们可以定义常量、静态成员类型(比如枚举类型定义)。Annotation
类型也可以如接口一般被实现或者继承。
- 生成文档。这是最常见的,也是
java
最早提供的注解。常用的有@see
、@param
、@return
等; - 跟踪代码依赖性,实现替代配置文件功能。比较常见的
spring2.5
开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量; - 在编译时进行格式检查。如
@override
放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
根据注解参数的个数,我们可以将注解分为三类:
- 1.标记注解:一个没有成员定义的
Annotation
类型被称为标记注解。这种Annotation
类型仅使用自身的存在与否来为我们提供信息。比如系统注解@Override
; - 2.单值注解
- 3.完整注解
根据注解使用方法和用途,我们可以将
Annotation
分为三类:
- 1.JDK内置系统注解
- 2.元注解
- 3.自定义注解(第三方提供和自己定义)
JDK内置注解
JavaSE
中内置三个标准注解,定义在java.lang
中:
@Override
:用于修饰此方法覆盖了父类的方法;@Deprecated
:用于修饰已经过时的方法;@SuppressWarnnings
:用于通知java编译器禁止特定的编译警告。
@Override
是一个标记注解类型,它被用作标注方法,不能用于其他程序。
它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种Annotation
在一个没有覆盖父类方法的方法时,java
编译器将以一个编译错误来警示。这个annotaton
常常在我们试图覆盖父类方法而确又写错了方法名时发挥威力。使用方法极其简单:在使用此annotation
时只要在被修饰的方法前面加上@Override
即可。下面的代码是一个使用@Override
修饰一个企图重载父类的displayName()
方法,而又存在拼写错误的实例:
public class Fruit {
public void displayName(){
System.out.println("水果的名字是:*****");
}
}
//编译没有问题
class Orange extends Fruit {
@Override
public void displayName(){
System.out.println("水果的名字是:桔子");
}
}
//编译出错
class Apple extends Fruit {
@Override
public void displayname(){
System.out.println("水果的名字是:苹果");
}
}
@Deprecated
,标记已过时:
同样Deprecated
也是一个标记注解。当一个类型或者类型成员使用@Deprecated
修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated
,但编译器仍然要报警。
值得注意,@Deprecated
这个annotation
类型和javadoc
中的 @deprecated
这个tag
是有区别的:前者是java
编译器识别的,而后者是被javadoc
工具所识别用来生成文档(包含程序成员为什么已经过 时、它应当如何被禁止或者替代的描述)。
在java5.0
,java
编译器仍然象其从前版本那样寻找@deprecated
这个javadoc
tag
,并使用它们产生警告信息。但是这种状况将在后续版本中改变,我们应在现在就开始使用@Deprecated
来修饰过时的方法而不是 @deprecated
javadoc
tag
。
下面一段程序中使用了@Deprecated
注解标示方法过期,同时在方法注释中用@deprecated
tag
标示该方法已经过时,代码如下:
class AppleService {
public void displayName(){
System.out.println("水果的名字是:苹果");
}
/**
* @deprecated 该方法已经过期,不推荐使用
*/
@Deprecated
public void showTaste(){
System.out.println("水果的苹果的口感是:脆甜");
}
public void showTaste(int typeId){
if(typeId==1){
System.out.println("水果的苹果的口感是:酸涩");
}
else if(typeId==2){
System.out.println("水果的苹果的口感是:绵甜");
}
else{
System.out.println("水果的苹果的口感是:脆甜");
}
}
}
public class FruitRun {
/**
* @param args
*/
public static void main(String[] args) {
Apple apple=new Apple();
apple.displayName();
/*在FruitRun类中使用的时候,编译器会给出该方法已过期,不推荐使用的提示 */
AppleService appleService=new AppleService();
appleService.showTaste();
appleService.showTaste(0);
appleService.showTaste(2);
}
}
SuppressWarnnings
,抑制编译器警告:
@SuppressWarnings
被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0
,sun
提供的javac
编译器为我们提供了-Xlint
选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。例如当我们使用一个generic
、collection
类而又没有提供它的类型时,编译器将提示出"unchecked warning
"的警告。通常当这种情况发生时,我们就需要查找引起警告的代码。如果它真的表示错误,我们就需要纠正它。例如如果警告信息表明我们代码中的switch
语句没有覆盖所有可能的case
,那么我们就应增加一个默认的case
来避免这种警告。
有时我们无法避免这种警告,例如,我们使用必须和非generic
的旧代码交互的generic collection
类时,我们不能避免这个unchecked warning
。此时@SuppressWarning
就要派上用场了,在调用的方法前增加@SuppressWarnings
修饰,告诉编译器停止对此方法的警告。
SuppressWarning
不是一个标记注解。它有一个类型为String[]
的成员,这个成员的值为被禁止的警告名。对于javac
编译器来讲,被-Xlin
t选项有效的警告 名也同样对@SuppressWarings
有效,同时编译器忽略掉无法识别的警告名。
annotation
语法允许在annotation
名后跟括号,括号中是使用逗号分割的name=value
对用于为annotation
的成员赋值。实例如下:
public class FruitService {
@SuppressWarnings(value={ "rawtypes", "unchecked" })
public static List<Fruit> getFruitList(){
List<Fruit> fruitList=new ArrayList();
return fruitList;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List<Fruit> getFruit(){
List<Fruit> fruitList=new ArrayList();
return fruitList;
}
@SuppressWarnings("unused")
public static void main(String[] args){
List<String> strList=new ArrayList<String>();
}
}
在这个例子中
SuppressWarnings
annotation
类型只定义了一个单一的成员,所以只有一个简单的value={...}
作为name=value
对。又由于成员值是一个数组,故使用大括号来声明数组值。注意:我们可以在下面的情况中缩写annotation
:
- 当
annotation
只有单一成员,并成员命名为"value=
"。这时可以省去"value=
"。比如将上面方法getFruit()
的SuppressWarnings
annotation
就是缩写的。
SuppressWarnings
注解的常见参数值的简单说明:
- 1.
deprecation
:使用了不赞成使用的类或方法时的警告; - 2.
unchecked
:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics
) 来指定集合保存的类型; - 3.
fallthrough
:当Switch
程序块直接通往下一种情况而没有Break
时的警告; - 4.
path
:在类路径、源文件路径等中有不存在的路径时的警告; - 5.
serial
:当在可序列化的类上缺少serialVersionUID
定义时的警告; - 6.
finally
:任何finally
子句不能正常完成时的警告; - 7.
all
:关于以上所有情况的警告。