概述
注解Annotation是一种引用数据类型,编译之后也是生成xxx.class文件,其定义的语法格式是:
[修饰符列表] @interface 注解类型名{ }
注解使用在类、属性、方法、变量、别的注解等上面,其实可以出现在几乎所有的标识符上,示例如下:
package annotation;
public @interface MyAnnotation {
}
package annotation;
@MyAnnotation
public class AnnotationTest01 {
@MyAnnotation
private int no;
@MyAnnotation
public String name;
protected String password;
@MyAnnotation
public static final float PI = 3.14f;
@MyAnnotation
public void test(){
@MyAnnotation
int num;
}
@MyAnnotation
public void run(@MyAnnotation String name){
}
}
package annotation;
@MyAnnotation
public @interface OneAnnotatione {
}
JDK内置的注解
java.lang包下的注释类型:
Deprecated是用@Deprecated注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择;
Override表示一个方法声明打算重写超类中的另一个方法声明;
SuppressWarning显示应该在注释元素中取消显示指定的编译器警告。
Override注解
package annotation;
/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
①只能注解方法
②这个注解是给编译器参考的,和运行阶段没有关系
③凡是java中的方法带有这个注解的,编译器都会进行编译检查,若没有重写父类编译器就会报错
*/
public class AnnotationTest02 {
@Override
public String toString() {
return super.toString();
}
}
注解最后编译生成.class文件。
以上,注解的注解称为元注解,@Target、@Retention就是元注解。
关于@Target注解
这是一个元注解,用来标注“注解类型”的“注解”,这个注解可以用来标注“被标注的注解”应该出现在哪些位置上。比如@Target(ElementType.METHOD)就表示被标注的注解可以出现在方法上。
关于@Retention注解
这是一个元注解,用来标记“被标记的注解”应该最终保存在哪里。
比如,@Retention(RetentionPolicy.SOURCE)就表示该注解最终只能被保留在java源文件中;
@Retention(RetentionPolicy.CLASS)表示该注解最终保留到class文件中;
@Retention(RetentionPolicy.RUNTIME)表示该注解最终保留到class文件中,并且可以被反射机制读取到。
Deprecated注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
package annotation;
/*
已过时,有更好的替代,尽量不要调用
*/
public class AnnotationTest03 {
public static void main(String[] args) {
//表示这个方法已过时
AnnotationTest03 at = new AnnotationTest03();
at.doSome(); //do some...
AnnotationTest03.doSome(); //do some...
AnnotationTest03.doOther(); //do other...
}
@Deprecated
public static void doSome(){
System.out.println("do some...");
}
public static void doOther(){
System.out.println("do other...");
}
}
自定义注解
package annotation;
public @interface OneAnnotatione {
String value();
}
package annotation;
public @interface MyAnnotation {
/**
* 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性
* 看着像一个方法,实际上他就是一个属性name
* @return
*/
String name();
String color();
/**
* 年龄属性默认为25
* @return
*/
int age() default 25;
String value();
}
package annotation;
public class AnnotationTest04 {
//(属性名=属性值)
@MyAnnotation(name = "zhangsan",color = "黑色",value = "xixixi")
public void doSome(){
System.out.println("do some...");
}
//注解属性只有value时,在使用时该属性名可以省略
@OneAnnotatione("圣诞老人")
public void doOther(){
System.out.println("do other...");
}
}
自定义注解的使用
package annotation;
public @interface OneAnnotatione {
String value();
}
package annotation;
public @interface MyAnnotation {
/**
* 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性
* 看着像一个方法,实际上他就是一个属性name
* @return
*/
String name();
String color();
/**
* 年龄属性默认为25
* @return
*/
int age() default 25;
String value();
}
package annotation.annotation4;
public @interface OtherAnnotation {
int age();
String[] email();
Season[] seasonArray();
}
package annotation.annotation4;
public enum Season {
SPRING,SUMMER,AUTNMN,WINTER
}
package annotation.annotation4;
public class OtherAnnotationTest {
@OtherAnnotation(age = 25,email = {"zs@qq.com","ls@sohu.com"},seasonArray = Season.AUTNMN)
public void doSome(){
}
//数组只有一个则大括号可省略
@OtherAnnotation(age = 26,email = "zs@163.com",seasonArray = {Season.SPRING,Season.WINTER})
public void doOther(){
}
}
通过反射获取注解属性值
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "杭州西湖区";
}
package annotation;
@MyAnnotation
public class MyAnnotationTest {
@MyAnnotation
int i;
@MyAnnotation
public void doSome(){
}
@MyAnnotation
public MyAnnotationTest(){
}
}
package annotation;
import java.lang.annotation.Annotation;
public class ReflectAnnotationTest {
public static void main(String[] args) throws Exception{
//获取这个类的对象
Class c = Class.forName("annotation.MyAnnotationTest");
//判断这个类是否有注解@MyAnnotation
// boolean annotationPresent = c.isAnnotationPresent(MyAnnotation.class);
// System.out.println(annotationPresent); //true
//判断类上是否有这个注解,如果有,就拿到
if (c.isAnnotationPresent(MyAnnotation.class)){
//这一切都是因为@Retention(RetentionPolicy.RUNTIME)
MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation); //@annotation.MyAnnotation(value=杭州西湖区)
//获取注解对象的属性怎么办?和调接口没区别
String value = myAnnotation.value();
System.out.println(value); //杭州西湖区
}
//判断String这个类上是否有@MyAnnotation这个注解
Class c2 = Class.forName("java.lang.String");
System.out.println(c2.isAnnotationPresent(MyAnnotation.class));//false
}
}
通过反射获取方法上的注解信息
package annotation.annotation6;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String username();
String password();
}
package annotation.annotation6;
import java.lang.reflect.Method;
public class MyAnnotationTest {
@MyAnnotation(username = "admin",password = "123")
public void doSome(){
}
//获取MyAnnotationTest的doSome方法上面的注解信息
public static void main(String[] args) throws Exception{
Class c = Class.forName("annotation.annotation6.MyAnnotationTest");
Method method = c.getDeclaredMethod("doSome");
//判断该方法上是否有这个注解
if (method.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println(annotation.username()); //admin
System.out.println(annotation.password()); //123
}
}
}
注解灵魂拷问 : 有什么用?
现在提一个需求:
假设有这样一个注解:@Id,这个注解只能出现在类上面.当这个类上有这个注解的时候,要求这个类中必须有一个int类型的属性,如果没有这个属性就报异常,如果有就正常执行.
package annotation.annotation7;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
public @interface Id {
}
package annotation.annotation7;
@Id
public class User {
// int id;
String name;
String password;
}
package annotation.annotation7;
public class HasNotIdPropertyException extends RuntimeException {
public HasNotIdPropertyException(String message) {
super(message);
}
public HasNotIdPropertyException() {
}
}
package annotation.annotation7;
import java.lang.reflect.Field;
public class HasIdAnnotationTest {
public static void main(String[] args) throws Exception{
Class userClass = Class.forName("annotation.annotation7.User");
Field[] dfs = userClass.getDeclaredFields();
boolean isOk = false;
for (Field df : dfs) {
if ("id".equals(df.getName())&&"int".equals(df.getType().getSimpleName())){
isOk = true;
break;
}
}
while (!isOk){
throw new HasNotIdPropertyException("被@Id标注的注解必须含有一个int类型的属性!");
}
}
}
这里运行是成功的,就是说,当我们故意注释掉User类的id属性时,就会报这个异常;放开注释时,回父正常,代码正确。达到预期效果。
事实证明这个例子没啥用,即不写该自定义注解并标注在User类上,测试也能有相同效果。
记住获取反射注解的方法是:
Class c = Class.forName("annotation.annotation6.MyAnnotationTest");
Method method = c.getDeclaredMethod("doSome");
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);