注解
JDK5.0以上的版本引入了一项新特性:Annotation,中文翻译成注解,是用来为程序元素(类、方法、成员变量等)设置说明和解释的一种元数据,Java开发和部署工具可以读取这些注解,并以某种形式处理这些注解。
注解(annotation)是JDK 5.0以上版本新增加的功能。它可以添加到程序的任何元素(包声明、类型声明、构造方法、方法、成员变量等)上,用来设置一些说明和解释,Java开发和部署工具可以读取这些注释,并以某种形式处理这些注释。
注解是在代码里做的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的描述源代码的信息(这些信息被存储在注解的“name=value”键-值对中)。代码分析工具、开发和部署工具可以通过这些补充的、描述源代码的信息进行验证或者进行部署。
注解类似于修饰符一样被使用,可以用于包、类、构造方法、方法、成员变量、参数、局部变量的声明。
需要注意的是,注解被用来为程序元素设置元数据,它不影响程序代码的执行,即无论增加、删除注解,程序的执行都不受任何影响。如果希望让程序中的注解起一定作用,只有通过配套的工具对注解中的元数据信息进行提取、访问,根据这些元数据增加额外功能和处理等。
JDK内置的基本注解类型:
Java的注解采用“@”标记形式,后面跟上注解类型名称,如果注解需要数据,通过“name=value”向注解提供数据。
注解类型和注解的区别:注解类型是某一类型注解的定义,类似于类。注解是某一注解类型的一个具体实例,类似于该类的实例。
预定义了三个注解:
重写Override :是一个限定重写方法的注解类型,用来指明被注解的方法必须是重写超类方法的方法,只能用于方法上。编译器在编译源代码时会检查用@Override标注的方法是否有重写父类的方法。
警告Deprecated :用来标记已过时成员的注解类型,用来指明被注解的方法是一个过时的方法,不建议使用了。当编译调用到被标注为Deprecated的方法的类时,编译器就会产生警告。
抑制警告SuppressWarnings :是抑制编译器警告的注解类型,用来指明被注解的方法、变量或类在编译时如果有警告信息,就阻止警告。
@SuppressWarnings(value={"unchecked", "deprecation"}),
自定义注解类型 :
Java中允许程序员自定义注解类型。注解类型的定义和接口类型的定义差不多,只是在interface前面多了一个“@”。
/** 自定义注解类型 */
public @interface MyAnnotation {
}
或者
/**带属性的注解类型*/
public @interface MyAnnotation{
String value(); //定义一个属性
}
使用注解
/** 使用自定义注解类型:MyAnnotation */
class UserMyAnnotation{
@MyAnnotation("abc")//“abc”传给了属性value。
public void myMethod(){
System.out.println("使用自定义的注解");
}
}
约定,如果使用注解时没有显式指定属性名,却指定了属性值,而这个注解类型又有名为value的属性,就将这个值赋给value属性。如果没有在注解类型中定义名为value的属性,就会出现编译错误。
例16.1 在定义注解类型时,还可以给它的属性指定默认值。
//定义自己的一个枚举类型
enum Status {ACTIVE, INACTIVE};
//定义注解类型
public @interface MyAnnotation{
String value();
Status status() default Status.ACTIVE;
//给status属性指定默认值
}
那么,在使用时,就可以不需要给status属性显式指定值了,它就会使用默认值。
/** 使用自定义注解类型:MyAnnotation */
class UserMyAnnotation{
//value属性的值为“abc”;status属性使用默认值Status.ACTIVE
@MyAnnotation(value="abc")
public void myMethod(){
System.out.println("使用自定义的注解");
}
}
当然,你还是可以给有默认的属性显式指定值的。
class UserMyAnnotation{
@MyAnnotation(value="xxx", status=Status.INACTIVE)
public void myMethod2(){
System.out.println("使用自定义的注解");
}
}
这里使用这个自定义注解类型时,给它的多个属性赋了值,多个属性之间用逗号“,”分隔。
对注解进行注解 :
JDK 5.0中提供了四种专门用在注解上的注解类型,分别是Target、Retention、Documented和Inherited。
目标Target
枚举ElementType:定义了注解类型可应用于Java程序的哪些元素。public enum ElementType{
TYPE, //适用于类、接口、枚举
FIELD,//适用于成员字段
METHOD, //适用于方法
PARAMETER, //适用于方法的参数
CONSTRUCTOR, //适用于构造方法
LOCAL_VARIABLE,//适用于局部变
ANNOTATION_TYPE, //适用于注解类型
PACKAGE//适用于包 }
例16.3 目标Target注解类型的使用。
package com.qiujy.corejava16;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@interface MethodAnnotation{
}
@MethodAnnotation //作用在类上----编译出错
public class TestTarget {
@MethodAnnotation //作用在方法上----正确
public void myMethod(){
}
}
类型Retention
既然可以自定义注解类型,当然也可以读取程序中的注解信息。但是注解只有被保存在class文件中才可以被读出来。Java编译器中处理类中出现的注解时,有以下3种方式。
编译器处理完后,不保留注解到编译后的类文件中。
将注解保留在编译后的类文件中,但是在运行时忽略它。
将注解保留在编译后的类文件中,并在第一次加载类时读取它。
这3种方式对应于java.lang.annotation.RetentionPolicy枚举的3个值。该枚举的源代码如下所示:
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, //编译器处理完后,并不将它保留到编译后的类文件中
CLASS, //编译器将注解保留在编译后的类文件中,但是在运行时忽略它
RUNTIME //编译器将注解保留在编译后的类文件中,并在第一次加载类时读取它
}
而Retention就是用来设置注解是否保存在class文件中的。
这3种方式对应于java.lang.annotation.RetentionPolicy枚举的3个值。该枚举的源代码如下所示:
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, //编译器处理完后,并不将它保留到编译后的类文件中
CLASS, //编译器将注解保留在编译后的类文件中,但是在运行时忽略它
RUNTIME //编译器将注解保留在编译后的类文件中,并在第一次加载类时读取它
}
而Retention就是用来设置注解是否保存在class文件中的。
例16.4 注解Retention的使用。
@Retention(RetentionPolicy.SOURCE)
@interface MyAnnotation1 { }
@interface MyAnnotation2 { }
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3 {}
文档Documented
这个注解类型和文档有关。默认的情况下,在使用javadoc自动生成文档时,注解将被忽略掉。如果想在文档中也包含注解,必须使用Documented定义。另外需要注意的是:定义为Documented的注解必须设置Retention的值为RetentionPolicy.RUNTIME。
例16.5 文档Documented注解的使用。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface DocAnnotation {
}
继承Inherited
在默认的情况下,父类上的注解并不会被子类继承。如果要让这个注解可以被子类上继承,就必须给这个注解类型定义上添加Inherited注解。
利用反射获取注解信息:
JDK 5.0 API中的java.lang.reflect.AnnotatedElement接口中定义了四种反射性读取注解信息的方法。
public Annotation getAnnotation(Class annotationType):如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
public Annotation[] getAnnotations():返回此元素上存在的所有注解。
public Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。
public boolean isAnnotationPresent(Class annotationType):如果指定类型的注解存在于此元素上,则返回true,否则返回false。
java.lang.Class类和java.lang.reflect包中的Constructor、Field、Method、Package类都实现了AnnotationElement接口,可以从这些类的实例上分别取得标注于其上的注解及相关信息。
package com.hbsi.annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*定义一个功能,用于去除ArrayList中的重复元素。
* 1.存入的是字符串
* 2.存入一个自定义对象。
*
* 思路:
* 1.创建一个新集合,存放非重复元素
* 2.对原来的集合进行遍历
* 3.在遍历的过程中进行判断元素是否在新集合中存在
* 4.如果不存在,添加。
* 5.返回新集合。
*
* */
public class Test1 {
/**
* @param args
*/
public static void main(String[] args) {
/*List list=new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc2");
list.add("abc3");
System.out.println(list);
list=getSingList(list);
System.out.println(list);*/
List list=new ArrayList();
list.add(new Student("aa",23));
list.add(new Student("bb",24));
list.add(new Student("cc",25));
list.add(new Student("bb",24));
System.out.println(list);
list=getSingList(list);
System.out.println(list);
}
public static List getSingList(List list){
//创建一个新集合
List newList=new ArrayList();
/*Iterator it=list.iterator();
while(it.hasNext()){
Object obj=it.next();
if(!(newList.contains(obj))){
newList.add(obj);
}*/
for(Iterator it=list.iterator();it.hasNext();){
Object obj=it.next();
if(!(newList.contains(obj))){
newList.add(obj);
}
}
return newList;
}
}
package com.hbsi.annotation;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/*每个学生有自己的姓名和年龄
每个学生都有自己的籍贯,籍贯是String类型
学生和籍贯之间存在映射关系
映射关系用Map表示
键:Student
值:籍贯
*
* */
public class Test2 {
/**
* @param args
*/
public static void main(String[] args) {
HashMap<Student,String> hm=new LinkedHashMap<Student,String>();
hm.put(new Student("aa",23),"保定");
hm.put(new Student("bb",24),"廊坊");
hm.put(new Student("cc",32),"石家庄");
hm.put(new Student("dd",26),"邢台");
hm.put(new Student("ee",21),"邯郸");
//keySet
/*Set<Student> set=hm.keySet();
Iterator<Student> it=set.iterator();
while(it.hasNext()){
Student s=it.next();
String address=hm.get(s);
System.out.println(s.getName()+"..."+s.getAge()+".."+address);
}*/
//entrySet
Set<Map.Entry<Student,String>> entrys=hm.entrySet();
Iterator<Map.Entry<Student,String>> it1=entrys.iterator();
while(it1.hasNext()){
Map.Entry<Student,String> entry=it1.next();
System.out.println(entry.getKey().getName()+"..."+entry.getValue());
}
}
}