注解(Annotation)
一、认识注解
注解(Annotation)很重要,未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的Struts2有一部分也是基于注解的了,注解是一种趋势,现在已经有不少的人开始用注解了,注解是JDK1.5之后才有的新特性
JDK1.5之后内部提供的三个注解
@Deprecated 意思是“废弃的,过时的”
@Override 意思是“重写、覆盖”
@SuppressWarnings 意思是“压缩警告”
范例:注解的应用:
package demo.annotation;
public class AnnotationTest {
/**
* @param args
*/
@SuppressWarnings(":deprecation")
//一个注解就是一个类,相当于创建了SuppressWarnings类的一个实例对象
public static void main(String[] args) {
System.runFinalizersOnExit(true);
//The method runFinalizersOnExit(boolean) from the type System is deprecated(过时的,废弃的)
@Deprecated //表明该方法过时,不建议使用
public static void sayHello(){
System.out.println("hi,annotation");
}
@Override //表示要覆盖该方法
public String toString(){
return "覆盖";
}
}
总结:注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。
注解就相当于一个你的源程序要调用一个类,在源程序中应用某个注解,得事先准备好这个注解类。就像你要调用某个类,得事先开发好这个类。
二、自定义注解及其应用
自定义一个最简单的注解:
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解(Annotation)类,在该注解类上使用另一个注解类(Retention),
* 被使用的注解类称为元注解
*/
@Retention(RetentionPolicy.RUNTIME)
//Retention注解决定MyAnnotation注解的生命周期,RetentionPolicy.RUNTIME指生命周期为程序运行时
@Target( { ElementType.METHOD, ElementType.TYPE })
//Target注解决定MyAnnotation注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分
/*
* @Retention(RetentionPolicy.SOURCE)
* 让MyAnnotation注解只在java源文件中存在,编译成.class文件后注解就不存在了
* @Retention(RetentionPolicy.CLASS)
* 让MyAnnotation注解在java源文件(.java文件)中存在,编译成.class文件后注解也还存在,
* 被MyAnnotation注解类标识的类被类加载器加载到内存中后MyAnnotation注解就不存在了
*/
public @interface MyAnnotation {
}
把自定义的注解加到某个类上:
@ MyAnnotation
public class AnnotationTest2{ }
用反射测试进行测试AnnotationUse的定义上是否有@MyAnnotation
package demo.annotation;
@MyAnnotation
public class AnnotationTest2 {
public static void main(String[] args) {
// 使用反射检查是否有注解
if (AnnotationTest2.class.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = (MyAnnotation) AnnotationTest2.class
.getAnnotation(MyAnnotation.class);
System.out.println(annotation);// 打印MyAnnotation对象
}
}
}
三、@Retention元注解
根据反射的测试的问题,引出@Retention元注解的讲解:其三种取值:RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME分别对应:Java源文件(.java文件)---->.class文件---->内存中的字节码
四、 Retention注解说明
当在Java源程序上加了一个注解,这个Java源程序要由javac去编译,javac把java源文件编译成.class文件,在编译成class时可能会把Java源程序上的一些注解给去掉,java编译器(javac)在处理java源程序时,可能会认为这个注解没有用了,于是就把这个注解去掉了,那么此时在编译好的class中就找不到注解了, 这是编译器编译java源程序时对注解进行处理的第一种可能情况,假设java编译器在把java源程序编译成class时,没有把java源程序中的注解去掉,那么此时在编译好的class中就可以找到注解,当程序使用编译好的class文件时,需要用类加载器把class文件加载到内存中,class文件中的东西不是字节码,class文件里面的东西由类加载器加载到内存中去,类加载器在加载class文件时,会对class文件里面的东西进行处理,如安全检查,处理完以后得到的最终在内存中的二进制的东西才是字节码,类加载器在把class文件加载到内存中时也有转换,转换时是否把class文件中的注解保留下来,这也有说法,所以说一个注解的生命周期有三个阶段:java源文件是一个阶段,class文件是一个阶段,内存中的字节码是一个阶段,javac把java源文件编译成.class文件时,有可能去掉里面的注解,类加载器把.class文件加载到内存时也有可能去掉里面的注解,因此在自定义注解时就可以使用Retention注解指明自定义注解的生命周期,自定义注解的生命周期是在RetentionPolicy.SOURCE阶段(java源文件阶段),还是在RetentionPolicy.CLASS阶段(class文件阶段),或者是在RetentionPolicy.RUNTIME阶段(内存中的字节码运行时阶段),根据JDK提供的API可以知道默认是在RetentionPolicy.CLASS阶段 (JDK的API写到:the retention policy defaults to RetentionPolicy.CLASS.)
下面看看@Deprecated、@Override、@SuppressWarnings这三个注解的@Retention注解的属性值分别是什么吧
4.1、@Deprecated
@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
4.2、@Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
@Override是给javac(java编译器)看的,所以生命周期是RetentionPolicy.SOURCE
4.3、@SuppressWarnings
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
@SuppressWarnings是给javac(java编译器)看的,所以生命周期是RetentionPolicy.SOURCE
五、@Target元注解
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
六、为注解增加属性
注解可以看成是一种特殊的类,可以为其添加属性
6.1.添加属性
语法:类型 属性名();
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
String color();
}
6.2.应用属性
package demo.annotation;
@MyAnnotation(color="red")//color属性
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 反射获得注解的实例,通过实例调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出red
}
}
6.3.为属性指定默认值
语法:类型 属性名() default 默认值;
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
String color() default "blue";//为属性指定默认值
}
package demo.annotation;
@MyAnnotation
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 用反射获得实例,通过该实例调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出color属性的默认值:blue
}
}
6.4.value属性
如果一个注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉“value=”部分。
例如:
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
String color() default "blue";
String value();//定义一个value属性
}
package cn.gacl.annotation;
@MyAnnotation("value")//等价于@MyAnnotation(value="value")
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 用反射获得实例,通过该实例调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出color属性的默认值:blue
System.out.println(annotation.value());
}
}
七、为注解增加高级属性
7.1、数组类型的属性
- 增加数组类型的属性:int[] arrayAttr() default {1,2,4};
- 应用数组类型的属性:@MyAnnotation(arrayAttr={2,4,5})
- 如果数组属性只有一个值,这时候属性值部分可以省略大括号,如:@MyAnnotation(arrayAttr=2),这就表示数组属性只有一个值,值为2
7.2.、枚举类型的属性
- 增加枚举类型的属性:EumTrafficLamp lamp() default EumTrafficLamp.RED;
- 应用枚举类型的属性:@MyAnnotation(lamp=EumTrafficLamp.GREEN)
7.3、注解类型的属性
为注解添加一个注解类型的属性,并指定注解属性的缺省值:MetaAnnotation annotationAttr() default @MetaAnnotation("xdp");
八、注解综合测试
TrafficLamp.java
package demo.annotation;
/**
* 信号灯颜色枚举
*/
public enum TrafficLamp {
RED,//红
YELLOW,//黄
GREEN//绿
}
MetaAnnotation.java
/**
* 元注解
*/
public @interface MetaAnnotation {
String value();//元注解MetaAnnotation设置有一个唯一的属性value
}
MyAnnotation.java
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
String color() default "blue";//为属性指定缺省值
/**
* value属性很特殊,如果一个注解中只有一个value属性要设置,可以省略属性名和等号不写, 直接写属性值,如@SuppressWarnings("deprecation"),
* 这里的设置了两个String类型的属性,color和value,
* 因为color属性指定有缺省值,value属性又是属于特殊的属性,因此使用MyAnnotation注解时
* 可以这样使用MyAnnotation注解:"@MyAnnotation(color="red",value="xdp")"
* 也可以这样使用:"@MyAnnotation("xdp")",这样写就表示MyAnnotation注解只有一个value属性要设置,color属性采用缺省值
*/
String value();//定义一个名称为value的属性
//添加一个int类型数组的属性
int[] arrayAttr() default {1,2,4};
//添加一个枚举类型的属性,并指定枚举属性的缺省值,缺省值只能从枚举类EumTrafficLamp中定义的枚举对象中取出任意一个作为缺省值
EumTrafficLamp lamp() default EumTrafficLamp.RED;
//为注解添加一个注解类型的属性,并指定注解属性的缺省值
MetaAnnotation annotationAttr() default @MetaAnnotation("xdp");
}
MyAnnotationTest.java
package demo.annotation;
/**
* 将MyAnnotation标记到AnnotaionTest类上,
* 并应用了注解类MyAnnotation中定义各种不同类型的属性
*/
@MyAnnotation(
color="red",
value="xdp",
arrayAttr={3,5,6},
lamp=EumTrafficLamp.GREEN,
annotationAttr=@MetaAnnotation("gacl")
)
public class MyAnnotationTest {
@MyAnnotation("将MyAnnotation注解标注到main方法上")
public static void main(String[] args) {
/**
* 这里是检查Annotation类是否有注解,这里需要使用反射才能完成对Annotation类的检查
*/
if(MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
/**
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
* MyAnnotation是一个类,这个类的实例对象annotation是通过反射得到的,这个实例对象是如何创建的呢?
* 一旦在某个类上使用了@MyAnnotation,那么这个MyAnnotation类的实例对象annotation就会被创建出来了
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());//输出color属性的默认值:red
System.out.println(annotation.value());//输出value属性的默认值:xdp
System.out.println(annotation.arrayAttr().length);//这里输出的数组属性的长度的结果为:3,数组属性有三个元素,因此数组的长度为3
System.out.println(annotation.lamp());//这里输出的枚举属性值为:GREEN
System.out.println(annotation.annotationAttr().value());//这里输出的注解属性值:gacl
MetaAnnotation ma = annotation.annotationAttr();//annotation是MyAnnotation类的一个实例对象
System.out.println(ma.value());//输出的结果为:gacl
}
}
}
Hello1.java
一.源代码及解析
/**
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
*
* You may not modify, use, reproduce, or distribute this software except in
* compliance with the terms of the License at:
* https://github.com/javaee/tutorial-examples/LICENSE.txt
*/
package javaeetutorial.hello1;
//导入下面两个注解需要的包
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named//用String//指定bean名称
@RequestScoped//表明该bean的生命周期是本次请求
public class Hello {//公有类Hello
private String name;//私有String域name
public Hello() {//公有构造器
}
public String getName() {//公有访问器方法,返回name
return name;
}
public void setName(String user_name) {//公有更改器方法,设置name的值
this.name = user_name;
}
}