注解(Annotation)是Java 1.5提供的新特性, 注解相当于一个特殊的类,像写接口一样的创建,要在前面写上"@"符号。 注解相当于一种标记,在程序中加了注解就等于给程序打上了某种标记,没加,则等于没有某种标记。以后,javac编译器开发工具和其他程序可以用注解来了解操作者的类及各种元素上有无标记,什么标记,就去做相应的事,标记可以加在包、类、字段、方法、方法的参数以及局部变量上,注解就是告诉编译器或者开发工具某种信息。未来的开发模式基本上都是基于注解的,这也是一种趋势。
在java.lang包中提供了以上三个注解,@SuppressWarnings("deprecation")、@Deprecated和@Override,其中@SuppressWarnings("deprecation")是制止警告信息,以示所使用的方法已过时;@Deprecated,若要升级某个类,其中的方法过时,可以用其进行注解; @Override ,表示对父类方法的覆盖(重写)。
注解的应用结构图:
<1>.
注解类:
@interface A{ }
<2>.
应用了"注解类"的类:
(B类使用了注解A)
@A Class B{ }
<3>.
对"应用了注解类的类"进行反射操作的类:
(当C类使用B这个类时,它会检查是否使用了注解)
Class C{
B.class.isAnnotationPresent(
A.class);
A a = B.class.getAnnotation(
A.class);
}
自定义注解及其应用
(1). 定义一个最简单的注解:public@interface AnnotationTest{}
(2). 把它加到某个类上:@ItcastAnnotation public class AnnotationTest{}
(3). 用反射进行测试AnnotationTest的定义是否有@ItcastAnnotation
package cn.itcast.day02;
public class AnnotationTest {
//这就是过时的方法,制止警告信息的注解
@SuppressWarnings("deprecation")
//value属性的特殊应用,即在给其他属性指定默认值的时候,即只给value属性设值的时候,可以不写等号
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
//检查这个类是否有注解,就要使用反射,首先要获取其字节码(就可知道其中的信息),检查ItcastAnnotation
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
//若该注解存在,if返回true,就得到注解,要进行类型转换
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation);
//用对象调用属性值
System.out.println(annotation.color());
System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
//调用注解类属性
//System.out.println(annotation.annotationAttr().value());
}
}
//若此方法已经过期,用注解告诉新的调用者此方法已经过时,或许对旧调用者还有用,注解以后就会给方法画上横线
//使用注解如下:
@Deprecated
public static void sayHello(){
System.out.println("Hellp world!");
}
}
为注解增加各种属性
注解的功能之所以那么强大,就是因为它有属性
<1>. 什么是注解的属性?
一个注解相当于一个胸牌,如果某所学校的学生胸前都佩戴有胸牌,就知道其属于这个学习学校的学生,否则不是。 若还要区分他为该学校哪个班级的学生,这时,就可以在其胸牌上增加相应的属性,就可以区分班级,注解也是同样,加了属性的注解如下:
@ItcastAnnotation(color="red")
<2>. 定义基本类型的属性和应用属性
a.在注解中增加String color();
b.@ItcastAnnotation(color="red")
<3>. 用反射方式获得注释对应的实例对象后,再通过该对象调用属性对应的方法
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color());
可以认为上面这个@ItcastAnnotation是ItcastAnnotation的一个实例对象
<4>. 为属性指定缺省值
String color()default "yellow";
<5>. value属性
String value() default
<6>.
注解很像接口, 它的属性很像方法:
a. 在创建注解的时候,就将属性值赋给该注解的对象:
@ItcastAnnotation(color="red")
public class AnnotationTest {}
用对象调用属性值:
annotation.color()
b.
@SupressWarnings("deprecation"),这也是一个属性,只是这个属性的名字非常的特殊(相当于省略了名称和等号)
c. value的特殊应用
如果只给value属性进行设置,就不需要写value的等号!
问题:
但是出现了这样的情况:@ItcastAnnotation(color="red",value="abc"),即有两个属性值,AnnotationTest类中
调用属性的时候,就会报错,如何解决这个问题呢?
解决办法:
可以在ItcastAnnotation类中设置属性时,给color属性指定default缺省属性
String color() default "blue";
Stringvalue();
<7>.为注解增加高级属性
(1).数组类型的属性
int[] arrayAttr() default{1,2,3};
@ItcastAnnotation(arrayAttr={1,2,3})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括号
(2).枚举类型的属性
EnumTest.TrafficLamp lamp();
@ItcastAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
(3).注解类型的属性
MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
@ItcastAnnotation(annotationAttr=@MetaAnnotation("yyy"))
可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotation ma = ItcastAnnotation.annotationAttr();
package cn.itcast.day02;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cn.itcast.day01.EnumTest;
import cn.itcast.day01.EnumTest.TrafficLamp;
//给注解类加上注解,注解的注解就叫做元注解,该注解的作用就是将其保留到运行期间
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
//给注解增加属性,注解的属性很像方法
String color() default "blue";
String value();
//增加数组属性
int[] arrayAttr() default {3,4,5} ;
//增加枚举的属性,此时要导入之前定义的枚举类
TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
//增加注解类属性,创建一个MetaAnnotation类,即注解的属性又是一个注解
MetaAnnotation annotationAttr() default @MetaAnnotation("1hm")
}
根据反射测试的问题,引出@Retention元注解,它的取值有三种:
RetentionPolicy.SOURCE -----> java源文件
RetentionPolicy.CLASS -----> class文件
RetentionPolicy.RUNTIME-----> 内存中的字节码
这三种取值,也被成为注解的三个生命周期,默认是属于 RetentionPolicy.CLASS阶段
大概过程:
java源文件是最开始的文件,有注解,当javac(编译器)在编译时有可能会去掉源文件中的注解,然后产生class文件 类加 载 器将class文件加载到内存中也存在一个转换 的过程,转换时是否把class文件中的注解保留下来,这也存在不同的说法,所以一个注解的生命周期有三个阶段
由此,可得知:当我们在设计一个注解的时候,就可以加上@Retention这个注解,以此说明该注解在哪个阶段,书写格式如下:
@Retention(RetentionPolicy.RUNTIME)
纠正一个概念:
class文件不是字节码,只有当类加载器将class文件加载到内存中,变成二进制的代码(文件)才是字节码,所以字节码都是二进制的
给注解类加上注解,注解的注解就叫做元注解,还有元信息,元数据
该注解的 作用就是保留注解的作用,(加此注解可以保留注解)
@Retention(RetentionPolicy.RUNTIME)
public @interface ItcastAnnotation {}
代码分析:
给 ItcastAnnotation类没有加注解 @Retention(RetentionPolicy.RUNTIME),导致运行没有结果的原因:
就是AnnotationTest.class得到的是字节码文件,即已经被加载到内存中,即该文件是二进制的文件,但是ItcastAnnotation.class,
是class文件,是@Retention注解的默认生命周期阶段,所以这是源程序在设计时没有加字节码阶段的注解,所以运行就没有结果
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
//若该注解存在,if返回true,就得到注解,要进行类型转换
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation);
//运行的结果为:@cn.itcast.day02.ItcastAnnotation()
}
思考:
@Override、@SuppressWarnings和@Deprecated,这三个注解的@Retention的属性值分别是什么?
@Override的属性值是: RetentionPolicy.SOURCE,java源文件阶段
@SuppressWarnings,(给编译器看的)编译器用完了就没用了,所以它的属性值是: RetentionPolicy.SOURCE,java源文件 阶段 @Deprecated,查看的加载到内存中的二进制代码,它的属性值是: RetentionPolicy.RUNTIME,是内存中的字节码阶段,编译器是在读java文件的字节码
这些属性也可以从API文档中查找
了解:
@Retention的值(SOURCE,CLASS和RUNTIME)都是枚举类型的值 ,叫作:Enum
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE}),该注解的作用是用来说明注解用在java程序的那个成分上(类,方法,变量等)以及哪个生命周期,其中的ElementType也是枚举类
@Target为元注解,它的默认值是任何元素,设置Targetd等于ElementType.METHOD,原来加在类上的注解就报错了,改为用数组的方式设置 {ElementType.Method,ElementType.TYPE}就可以了
TYPE类型是Java1.5中财出现的一种对类似于类的java中的类型的一种定义,例如:接口(interface),枚举(Enum)
Java中的以下成分可以加载注解:
注释类型声明、构造方法声明、字段声明、局部变量声明、方法声明、包声明、参数声明和类、接口或枚举声明