19 注解
19.1 注解概述
注解,或者叫注释
注释Annotation是一种引用数据类型,编译之后也是xxx.class文件
怎么自定义注解呢?语法格式?
修饰符列表@interface 注解类型名{}
注解怎么使用?用在哪里
注解使用:@注解类姓名,可以使用在类上,属性上,方法上,变量上等...,注解还可以出现在注解类型上
- 代码实现
新建Annotation定义注解
java
package com.bjpowernode._15Annotation;
/*
自定义注解
*/
public @interface MyAnnotation00 {
}
在类上,属性上,方法上,变量,接口,枚举上等...,
java
package com.bjpowernode._15Annotation;
/**
1.注解,或者叫注释
2.注释Annotation是一种引用数据类型,编译之后也是xxx.class文件
3.怎么自定义注解呢?语法格式?
[修饰符列表]@interface 注解类型名{}
*/
@MyAnnotation00
public class AnnotationTest01 {
@MyAnnotation00
private int no;
@MyAnnotation00
public AnnotationTest01(){}
@MyAnnotation00
public static void m1(){
int i =100;
}
@MyAnnotation00
public void m2(){
}
}
@MyAnnotation00
interface MyInterface{
}
@MyAnnotation00
enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
甚至注解用在注解上面
java
package com.bjpowernode._15Annotation;
@MyAnnotation00
public @interface OtherAnnotation {
}
19.2 JDK自带的注解
19.2.1 种类
java.lang包下
Deprecated:这个注解表示被标记的元素已"过时"。它的主要目的是在不破坏现有代码的前提下,为开发者提供一个过渡期,表明存在更好的替代方案,并警示新代码不要继续使用
Override:被注解的方法旨在重写(或实现)其父类(或接口)中声明的方法
SupressWarnings(了解):在注解的元素(及该元素包含的所有程序元素)中,应取消显示指定的编译器警告
元注解:见下
19.2.2 Override注解
java
package com.bjpowernode._15Annotation;
/*
关于JDK lang包下的Override注解:
public @interface Override{
}
*/
//@Override 编译报错
public class AnnotationTest02 {
//@Override 编译报错
private int no;
//@Override是标识性注解,只能出现在方法上,是给编译器参考的,和运行阶段没有关系
//凡是java中的方法有这个注释的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器会报错
//例如下面的toString()方法写错了,携程ToString,@Override就会检查此方法有没有错误,认为他没有重写报错
@Override
public String toString() {
return super.toString();
}
public static void main(String[] args) {
}
}
19.2.3 元注解
- 概念
就是注解的注解
@Override为什么不能出现在类或者其他上面
因为源码上面有一行注解@Target(ElementType.METHOD),表示只能出现在方法上,这个就是元注解
@Retention(RetentionPolicy.SOURCE)表示@Override保存在源文件当中,而不是在AnnotationTest02注解文件当中
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- 常见元注解:TargetRetention
Target:用来标注"注解类型"的"注解",这个Target注解用来标注"被标注的注解"可以出现在哪一些位置上
Retention:用来标注"注解类型"的"注解",用来标注"被标注的注解"可以出现在哪些位置上。例如@Retention(RetentionPolicy.SOURCE)表示该注解只被保留在java源文件中,编译后生成class就没了
@Retention(RetentionPolicy.CLASS)表示该注解被保存在class文件中
@Retention(RetentionPolicy.RUNTIME)表示该注解被保存在class当中,并且可以被反射机制所读取
19.2.4 Deprecated
- 注解源码
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@Retention(RetentionPolicy.RUNTIME)表示注解已经保存在字节码文件中了, 并且可以被程序所读到
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})既可以在构造器,属性,变量,方法,包上,参数上,类上都可以出现
- 代码实现
java
package com.bjpowernode._15Annotation;
@Deprecated//表示这个类已经过时了
public class AnnotationTest03 {
public static void main(String[] args) {
AnnotationTest03.doSome1();
AnnotationTest03 at = new AnnotationTest03();
at.doOther();
}
//Deprecated这个注解标注的元素已过时
//主要是向其他程序原传递一个信息,已过时,有更好的解决方案存在
@Deprecated
public static void doSome1(){
System.out.println("do something!");
}
@Deprecated
public void doSome2(){
System.out.println("do something!");
}
@Deprecated
public static void doOther(){
System.out.println("do other...");
}
}
class T {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
AnnotationTest03 at1 = new AnnotationTest03();
//这个会出现------,表示@Deprecated的信息是保留在注解已经保存在字节码文件中了, 并且可以被程序所读到
at1.doSome2();//在下面Problems会出现警告:'doSome2()' is deprecated
Class c = Class.forName("java.lang.Date");
Object obj = c.newInstance();
}
}
19.3 实现自定义注解
19.3.1 自定义注解指定属性以及使用自定义有属性的注解
自定义注解
java
package com.bjpowernode._15Annotation;
public @interface MyAnnotion02 {
/**
* 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性
* 看着像1个方法,但是是属性name
* @return
*/
String name();
String color();
int age() default 29;//默认值29
}
java类使用自定义注解
java
package com.bjpowernode._15Annotation;
@Deprecated()//可以不指定值,因为在新的jdk9中,使用deflaut制定了since的默认值
public class Annotation04 {
//@MyAnnotion02():编译报错的原因,如果一个注解当中有属性,那么必须给属性赋值
//指定name属性的值就好了
@MyAnnotion02(name="zhangsan",color= "红色")//age默认值设置好了,可以不用写
public void doSome(){
}
}
19.3.2 自定义注解运用在方法上
- 注解属性名称省略(仅限属性名是value的时候并且只有一个属性)
- 注解类
java
package com.bjpowernode._15Annotation;
public @interface OtherAnnotation04 {
String name();
}
- 类运用自定义注解
java
package com.bjpowernode._15Annotation;
public class AnnotationTest05 {
@MyAnnotation03(value="hehe")
public void doSome(){
}
@MyAnnotation03("haha")///注解属性名称省略(仅限属性名是value的时候并且只有一个属性)
public void doOther(){
}
}
- 注解属性名称不可省略
- 注解类
java
package com.bjpowernode._15Annotation;
public @interface OtherAnnotation04 {
String name();
}
- 类运用自定义注解
java
package com.bjpowernode._15Annotation;
public class OtherAnnotationTest06 {
@OtherAnnotation04("test")
public void doSome(){
}
}
19.3.3 属性的注解类型
- 可以是什么类型
JAVA
package com.bjpowernode._15Annotation;
public @interface MyAnnotation05 {
/*
注解当中的属性可以是什么类型?
可以是byte short int long float double boolean char String Class 枚举类
以及以上每一种数据形式
*/
int value1();
String value2();
int[] value3();
String[] value4();
Season value5();//自建的枚举类型
Season[] value6();//枚举数组
Class parameterType();//ClASS类
Class[] parameters();//CLASS数组
}
枚举类
java
package com.bjpowernode._15Annotation;
public enum Season07 {
SPRING,SUMMER,AUTUMN,WINTER
}
- 数组只有一个可以省略大括号的注解
- 注解
java
package com.bjpowernode._15Annotation;
public @interface OtherAnnotation05 {
int age();
/*
邮箱地址属性,支持多个
*/
String[] email();
/**
* 季节数组,Season是枚举类型
* @return
*/
Season[] seasonArray();
}
- 类使用自定义注解
java
package com.bjpowernode._15Annotation;
public class OtherAnnotationTest08 {
@OtherAnnotation05(age=25,email={"zhangsan@123.com","zhangsan@souhu.com"},
seasonArray={Season.SPRING,Season.SUMMER})
public void doSome(){
}
//数据只有一个,可以省略大括号
@OtherAnnotation05(age=25,email="zhangsan@123.com",seasonArray=Season.SPRING)
public void doOther(){
}
}
19.3.4 @Retention源码
因此@Retention(RetentionPolicy.RUNTIME)其实就是@Retention(value=RetentionPolicy.RUNTIME),并且RetentionPolicy是一个枚举类
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
java
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
19.3.5 @Target源码
因此@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})的意思是传入一个 ElementType\[\]枚举的数组
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
java
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
来个运用
- 注解
java
package com.bjpowernode._15Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//只允许该注解可以标注类,方法,value=可以省略
@Target(value = {ElementType.TYPE,ElementType.METHOD})
//希望注解可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation06 {
}
- 使用自定义注解的类
java
package com.bjpowernode._15Annotation;
public class MyAnnotationTest09 {
//@MyAnnotation06 报错:'@MyAnnotation06' not applicable to field,因为自定义的注解只能在类和方法上,
// 不能在属性上,如果想要加上ElementType.FIELD
int i;
//@MyAnnotation06 报错:'@MyAnnotation06' not applicable to constructor不能出现在构造方法上
public MyAnnotationTest09(){
}
@MyAnnotation06
public void doSome(){
//@MyAnnotation06 报错:'@MyAnnotation06' not applicable to local variable不能出现在局部变量上
int i;
}
}
19.4 反射注解
- 反射机制代码实现获取类的注解
- 注解
java
package com.bjpowernode._15Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//只允许该注解可以标注类,方法,value=可以省略
@Target(value = {ElementType.TYPE,ElementType.METHOD})
//希望注解可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation06 {
/*
value属性
*/
//String value() default "北京大兴区";
String value();
}
- 使用注解的类
java
package com.bjpowernode._15Annotation;
@MyAnnotation06("上海浦东区")
public class MyAnnotationTest09 {
//@MyAnnotation06 报错:'@MyAnnotation06' not applicable to field,因为自定义的注解只能在类和方法上,
// 不能在属性上,如果想要加上ElementType.FIELD
int i;
//@MyAnnotation06 报错:'@MyAnnotation06' not applicable to constructor不能出现在构造方法上
public MyAnnotationTest09(){
}
// @MyAnnotation06
public void doSome(){
//@MyAnnotation06 报错:'@MyAnnotation06' not applicable to local variable不能出现在局部变量上
int i;
}
}
- 程序入口类通过反射获取注解
java
package com.bjpowernode._15Annotation;
import java.lang.annotation.Annotation;
public class RefletAnnotationTest10 {
public static void main(String[] args) throws ClassNotFoundException {
//先获取类
Class c = Class.forName("com.bjpowernode._15Annotation.MyAnnotationTest09");
//判断类上面是否有Annotation注解类型,即是否有MyAnnotation06注解
System.out.println(c.isAnnotationPresent(MyAnnotation06.class));//true
//判断String类上是否存在这个注解,是没有我们自定义的这个注解的
Class stringClass = Class.forName("java.lang.String");
System.out.println(stringClass.isAnnotationPresent(MyAnnotation06.class));//false
//如果存在我们想要知道的那个自定义注解
if(c.isAnnotationPresent(MyAnnotation06.class)){
//获取该注解对象
//转型的原因是 getAnnotation 返回的是Annotation类型。如果不转型 不能调用MyA你notation的属性
MyAnnotation06 myAnnotation = (MyAnnotation06)c.getAnnotation(MyAnnotation06.class);
System.out.println("类上面的注解对象"+myAnnotation);//类上面的注解对象@com.bjpowernode._15Annotation.MyAnnotation06()
//获取对象的属性怎么办?跟调接口没区别
String value = myAnnotation.value();
System.out.println(value);//北京大兴区 不用默认值写在注解上就输出:上海浦东区
}
}
}
- 反射机制代码实现获取方法的注解
注解
java
package com.bjpowernode._15Annotation;
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)
public @interface MyAnnotation07 {
String username();
String password();
}
使用自定义注解的类
java
package com.bjpowernode._15Annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class MyAnnotationTest10 {
@MyAnnotation07(username = "admin",password = "123")
public void doSome(){
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//获取MyAnnotationTest的doSome方法上面的注解信息
Class c = Class.forName("com.bjpowernode._15Annotation.MyAnnotationTest10");
//获取方法doSome()方法
Method doSomeMethod = c.getDeclaredMethod("doSome");
//判断该方法上是否存在这个注解
if(doSomeMethod.isAnnotationPresent(MyAnnotation07.class)){
MyAnnotation07 myAnnotation = (MyAnnotation07)doSomeMethod.getAnnotation(MyAnnotation07.class);
System.out.println(myAnnotation.username());//admin
System.out.println(myAnnotation.password());//123
}
}
}
- 案例
需求:假设有这样一个注解,叫做@id,这个注解只能出现在类上面,当类上有这个注解的时候,要求这个类中必须要有一个int类型的id属性。如果没有这个属性就报异常,如果有这个属性则正常执行
思考注解什么作用?注解当中等同于一个标记,如果元素上有这个注解怎么办,没有这个注解怎么办
注解
java
package com.bjpowernode._15Annotation;
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.TYPE)
public @interface Id09 {
}
User类
java
package com.bjpowernode._15Annotation;
@Id09
public class User11 {
int id;
String name;
String password;
}
反射注解类
java
package com.bjpowernode._15Annotation;
import java.lang.reflect.Field;
public class AnnotationTest11 {
public static void main(String[] args) throws ClassNotFoundException {
Class userClass = Class.forName("com.bjpowernode._15Annotation.User11");
//判断类上是否含有Id注解
if(userClass.isAnnotationPresent(Id09.class)){
//当一个上面有一个Id注解的时候,要求类中必须存在int类型的id属性
Field[] fields = userClass.getDeclaredFields();
boolean isOk = false;
for (Field field : fields) {
if("id".equals(field.getName())&&"int".equals(field.getType().getSimpleName())){
//表示类是合法的
isOk = true;
break;
}
}
//判断是否合法
if(!isOk){
throw new HasnotIdPropertyException12("被@Id标注的类中必须要有一个int类型的id属性");
}
}
}
}
异常类
java
package com.bjpowernode._15Annotation;
public class HasnotIdPropertyException12 extends RuntimeException{
public HasnotIdPropertyException12(){
}
public HasnotIdPropertyException12(String s){
super(s);
}
}