说到注解,我们一定都不陌生,在我们编码过程中可以说是无处不在,非常的常见。比如:@Override
,@Deprecated
,@Controller
等等。
那么我们今天来说一下如何自定义一个注解。
一、注解的基本结构
我们在代码里面点开一个注解看一下:
package java.lang;
import java.lang.annotation.*;
/**
* Indicates that a method declaration is intended to override a
* method declaration in a supertype. If a method is annotated with
* this annotation type compilers are required to generate an error
* message unless at least one of the following conditions hold:
* * <ul><li>
* The method does override or implement a method declared in a
* supertype.
* </li><li>
* The method has a signature that is override-equivalent to that of
* any public method declared in {@linkplain Object}.
* </li></ul>
* * @author Peter von der Ahé
* @author Joshua Bloch
* @jls 9.6.1.4 @Override
* @since 1.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这个就是注解的名称:
public @interface Override {
}
这个是注解的元注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
除了上面这两个以外还有两个元注解。
- 元注解的作用就是负责注解其他注解,java定义了4个标准的meta-annotation元注解类型,他们被用来对其他annotation类型做说明。
<1>:@Target
:用来描述注解的使用范围,ElementType
枚举选择,可以看到,很多类型有,class
,method
等作用域。
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
}
<2>:@Retention
表示需要在什么级别保存到该注解信息,用于描述注解的生命周期,通过RetentionPolicy
枚举进行选择,(SOURCE<CLASS<RUNTIME
),默认为RUNTIME
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
}
<3>:@Document
说明该注解被说明在javadoc中
/**
* Indicates that annotations with a type are to be documented by javadoc
* and similar tools by default. This type should be used to annotate the
* declarations of types whose annotations affect the use of annotated
* elements by their clients. If a type declaration is annotated with
* Documented, its annotations become part of the public API
* of the annotated elements.
*
* @author Joshua Bloch
* @since 1.5
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
<4>:@Inherited
:说明子类可以继承父类中的该注解。
主要需要学习一下前两个元注解
二、自定义一个注解
- 使用
@interface
来申明一个自定义注解时,他会自动继承ava.lang.annotation.Annotation
接口。 - 格式public @interface xxx(注解名称){定义注解参数内容}。
package annotest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
*/
public class Test {
@myAnnotation
public void test(){
};
}
//@Target表示自定义注解的作用范围,METHOD:方法;TYPE:类 ;等等。
@Target({ElementType.METHOD,ElementType.TYPE})
//@Retention自定义注解的 运行作用域
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
//暂时没有参数
}
上面就定义了一个无参数的自定义注解。如果是注解有参数,需要在注解里面添加参数类型,如下:
参数格式: 类型 +名称();
可以看到加了参数后,使用注解如果不添加参数的话会报错,除非添加默认值:
或者
如果只有一个参数可以直接使用value命名,这样在使用注解的时候可以省略’名称=“xx”'直接赋值
三、注解的使用
自定义注解的使用需要用到反射的原理,直接上代码:
package annotest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Test03 {
}
//作用于类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnno{
String value();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoName{
String name();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoAge{
int age();
}
//使用类注解
@ClassAnno("className")
class Student{
//成员注解
@FiledAnnoName(name = "张三")
private String name;
//成员注解
@FiledAnnoAge(age = 18)
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
我们在Test03里面定义了三个注解:
//作用于类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnno{
String value();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoName{
String name();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoAge{
int age();
}
在Student类里面分别使用了注解。那么我们要如何取到注解里面设置的属性值呢?
public static void main(String[] args) throws ClassNotFoundException {
//使用反射获取对量class
Class aClass = Class.forName("annotest.Student");
//使用class对象获取 所有类注解
Annotation[] annotations = aClass.getAnnotations();
//便利注解信息
for (Annotation annotation : annotations) {
System.out.println("类注解:"+annotation);
//强转为ClassAnno注解对象
ClassAnno classAnno = (ClassAnno)annotation;
System.out.println("注解值:"+classAnno.value());
}
}
使用反射获取Student
注解信息annotations
可以获取到注解里面的值是多少。不清楚的可以去了解一下反射:java进阶之反射
看下结果:
类注解:@annotest.ClassAnno(value=className)
注解值:className
如果想要获取属性变量的注解需要先获取到Filed对象:
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//使用反射获取对量class
Class aClass = Class.forName("annotest.Student");
//使用class对象获取 所有类注解
Annotation[] annotations = aClass.getAnnotations();
//便利注解信息
for (Annotation annotation : annotations) {
System.out.println("类注解:"+annotation);
//强转为ClassAnno注解对象
ClassAnno classAnno = (ClassAnno)annotation;
System.out.println("注解值:"+classAnno.value());
}
//这里变量是私有的private ,不可以使用getField ,使用getDeclaredField获取Filed
Field name = aClass.getDeclaredField("name");
//这里变量是私有的private ,不可以使用getAnnotation ,使用getDeclaredAnnotation获取注解
FiledAnnoName annotation = name.getDeclaredAnnotation(FiledAnnoName.class);
String name1 = annotation.name();
System.out.println("成员注解name值:"+name1);
System.out.println("------------分割线------------");
//这里变量是私有的private ,不可以使用getField ,使用getDeclaredField获取Filed
Field age = aClass.getDeclaredField("age");
//这里变量是私有的private ,不可以使用getAnnotation ,使用getDeclaredAnnotation获取注解
FiledAnnoAge annoAge = age.getDeclaredAnnotation(FiledAnnoAge.class);
int age1 = annoAge.age();
System.out.println("成员注解age值:"+age1);
}
需要注意的是,类是默认缺省的,是有访问权限的。而name和age是私有private
的,无法直接获取到Filed对象,需要使用getDeclaredField
以及getDeclaredAnnotation
获取星湖街相关信息,才能够生效。
我们看下结果:
类注解:@annotest.ClassAnno(value=className)
注解值:className
成员注解name值:张三
------------分割线------------
成员注解age值:18
没问题的,好了,自定义注解就说道这里。
加油每一天!奥利给!