Java基础之枚举类与注解-注解(Annotation)
主要内容:
- 注解(Annotation)概述
- 常见的Annotation示例
- 自定义Annotation
- JDK中的元注解
- 利用反射获取注解信息
- JDK8中注解的新特性
- 注解(Annotation)的概述
-
从JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注解)
-
Annotation其实就是代码里的“特殊标记”,这些标记可以在编译时,类加载时,运行时被读取,并执行相应的处理。通常使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署
-
Annotation可以像修饰符一样被使用,可用于修饰包,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的"name=value"对中。
-
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用的任何切面,代替JavaEE旧版中锁遗留的繁冗代码和XML配置等。
-
未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的Struts2有一部分也是基于注解了,注解是一种趋势,一定程度上可以说:框架=注解+反射+设计模式。
-
使用Annotation时要在前面加@符号,并把该Annotation当初一个修饰符使用。用于修饰它支持的程序元素
-
示例一:生成文档相关的注解
-
注解名 用处 @author 表明开发该类模块的作者,多个作者之间使用,分割 @version 标明该类模块的版本 @see 参数转向,也就是相关主题 @since 从哪个版本开始增加的 @param 对方法中某参数的说明,如果没有参数就不能写 @return 对方法返回值的说明,如果方法的返回值类型是void就不能写 @exception 对方法可能抛出的异常进行寿命,如果方法没有用throws显示抛出的异常就不能写 -
其中
- @param @return和@exception 这三个标记都是只用于方法的。
- @param的格式要求:@param 形参名 形参类型 形参说明
- @return的格式要求:@return 返回值类型 返回值说明
- @exception的格式要求:@exception 异常类型 异常说明
- @param和@exception可以并列多个
-
/** * @author jiangl * @version 1.0 * @date 2021/4/21 8:44 */ public class EnumTest1 { /** * * @param name 名字 * @param age 年龄 * @return * @throws Exception 异常 * @throws IOException IO异常 */ public String testAnnotation(String name,String age)throws Exception, IOException{ return name; } }
-
示例二:在编译时进行格式检查(JDK内置的三个基本注解)
- @Override:限定重写父类方法,该注解只能用于方法
- @Deprecated:用于表示所修饰的元素(类、方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择(做一个提示,用处为了向下兼容)
- @SuppressWarnings:抑制编译器警告
package com.jl.java.base.myannotation; /** * @author jiangl * @version 1.0 * @date 2021/4/21 11:19 */ public class AnnotationTest1 { public static void main(String[] args) { Dog dog = new BigDog(); dog.run(); } } interface Animal{ @SuppressWarnings({}) void sleep(); } class Dog { private String name; private int age; public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } public void eat() { System.out.println("吃骨头"); } @Deprecated public void run(){ System.out.println("狗跑步"); } } class BigDog extends Dog implements Animal{ /** * 如果此处不写@Override注解,编译期可能不认为是重写,调用时会调用父类的方法 */ @Override public void run() { System.out.println("大狗跑步"); } @Override public void sleep() { System.out.println("睡觉"); } }
-
示例三:跟踪代码依赖性,实现替代配置文件功能
-
Servlet3.0提供了注解(Annotation),使得不再需要在web.xml文件中进行Servlet的部署
-
package com.jl.java.base.myannotation; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author jiangl * @version 1.0 * @date 2021/4/21 11:00 */ @WebServlet("/loging") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
-
Spring框架中关于“事务”的管理啊
-
另外,Junit单元测试中也有大量注解的使用
- @Test:标记在非静态的测试方法上,只有标记@Test的方法才能被作为一个测试方法单独测试。一个类中可以有多个@Test标记的方法。运行时如果只想运行一个@Test标记的方法,那么选择这个方法名,然后单独运行,否则整个类的所有标记@Test的方法都会被执行
- @Test(timeout =1000):设置超时时间,如果测试时间超过了你定义的timeout,测试失败
- @Test(excepted):申明出会发生的异常,比如@Test(excepted = Exception.class)
- 其他还有:
- @BeforeClass:标记在静态方法上。因为这个方法只执行一次。在类初始化时执行。
- @AfterClass:标记在静态方法上。因为这个方法只执行一次,在所有方法完成后执行。
- @Before:标记在非静态方法上,在@Test方法前面执行,而且是在每一个@Test方法后面都执行
- @After:标记在非静态方法上,在@Test方法后面执行,而且是在每一个@Test方法后面都执行
- @Ignore:标记在本次不参与测试的方法上,这个注解的含义就是“某些方法尚未完成,暂不参与测试”。
- @BeforeClass,@AfterClass,@Before,@After,@Ignore都是配合@Test使用的,单独使用没有意义
- @Test:标记在非静态的测试方法上,只有标记@Test的方法才能被作为一个测试方法单独测试。一个类中可以有多个@Test标记的方法。运行时如果只想运行一个@Test标记的方法,那么选择这个方法名,然后单独运行,否则整个类的所有标记@Test的方法都会被执行
-
package com.jl.java.base.myannotation; import org.junit.*; import java.util.Arrays; /** * @author jiangl * @version 1.0 * @date 2021/4/21 11:14 */ public class MyJunitTest { private static Object[] array; private static int total; @BeforeClass public static void beforeClass(){ System.out.println("初始化数组"); array = new Object[5]; } @Before public void before(){ System.out.println("调用之前"); } @Test public void test(){ //往数组中添加一个元素 System.out.println("add"); array[total++] = "hello"; } @After public void after(){ System.out.println("调用之后"); } @AfterClass public static void afterClass(){ System.out.println(Arrays.toString(array)); } }
-
-
- 使用自定义注解
- 定义新的Annotation类型使用@interface关键字
- 自定义注解自动继承了java.lang.Annotation接口
- Annotation的成员变量在Annotation定义中以无参方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称为配置参数。类型只能是“八种基本数据类型、String型、Class型、enum型、Annotation类型、以及以上所有类型的数组”(不能为包装类,Collection容器,例如Map,List)。
- 可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字
- 如果只有一个参数成员,建议使用参数名为value
- 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是“参数名 = 参数值”,如果只有一个参数成员,且名称为value,可以省略“value="
- 没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation
- 注意:自定义注解必须配上注解的信息处理流程(使用反射)才有意义
定义新的Annotation类型使用@interface关键字
public @interface MyOwnAnnotation {
}
自定义注解自动继承了java.lang.Annotation接口
package java.lang.annotation;
/**
* The common interface extended by all annotation types. Note that an
* interface that manually extends this one does <i>not</i> define
* an annotation type. Also note that this interface does not itself
* define an annotation type.
*
* @author Josh Bloch
* @since 1.5
*/
public interface Annotation {
/**
* Returns true if the specified object represents an annotation
* that is logically equivalent to this one. In other words,
* returns true if the specified object is an instance of the same
* annotation type as this instance, all of whose members are equal
* to the corresponding member of this annotation, as defined below:
* <ul>
* <li>Two corresponding primitive typed members whose values are
* <tt>x</tt> and <tt>y</tt> are considered equal if <tt>x == y</tt>,
* unless their type is <tt>float</tt> or <tt>double</tt>.
*
* <li>Two corresponding <tt>float</tt> members whose values
* are <tt>x</tt> and <tt>y</tt> are considered equal if
* <tt>Float.valueOf(x).equals(Float.valueOf(y))</tt>.
* (Unlike the <tt>==</tt> operator, NaN is considered equal
* to itself, and <tt>0.0f</tt> unequal to <tt>-0.0f</tt>.)
*
* <li>Two corresponding <tt>double</tt> members whose values
* are <tt>x</tt> and <tt>y</tt> are considered equal if
* <tt>Double.valueOf(x).equals(Double.valueOf(y))</tt>.
* (Unlike the <tt>==</tt> operator, NaN is considered equal
* to itself, and <tt>0.0</tt> unequal to <tt>-0.0</tt>.)
*
* <li>Two corresponding <tt>String</tt>, <tt>Class</tt>, enum, or
* annotation typed members whose values are <tt>x</tt> and <tt>y</tt>
* are considered equal if <tt>x.equals(y)</tt>. (Note that this
* definition is recursive for annotation typed members.)
*
* <li>Two corresponding array typed members <tt>x</tt> and <tt>y</tt>
* are considered equal if <tt>Arrays.equals(x, y)</tt>, for the
* appropriate overloading of {@link java.util.Arrays#equals}.
* </ul>
*
* @return true if the specified object represents an annotation
* that is logically equivalent to this one, otherwise false
*/
boolean equals(Object obj);
/**
* Returns the hash code of this annotation, as defined below:
*
* <p>The hash code of an annotation is the sum of the hash codes
* of its members (including those with default values), as defined
* below:
*
* The hash code of an annotation member is (127 times the hash code
* of the member-name as computed by {@link String#hashCode()}) XOR
* the hash code of the member-value, as defined below:
*
* <p>The hash code of a member-value depends on its type:
* <ul>
* <li>The hash code of a primitive value <tt><i>v</i></tt> is equal to
* <tt><i>WrapperType</i>.valueOf(<i>v</i>).hashCode()</tt>, where
* <tt><i>WrapperType</i></tt> is the wrapper type corresponding
* to the primitive type of <tt><i>v</i></tt> ({@link Byte},
* {@link Character}, {@link Double}, {@link Float}, {@link Integer},
* {@link Long}, {@link Short}, or {@link Boolean}).
*
* <li>The hash code of a string, enum, class, or annotation member-value
I <tt><i>v</i></tt> is computed as by calling
* <tt><i>v</i>.hashCode()</tt>. (In the case of annotation
* member values, this is a recursive definition.)
*
* <li>The hash code of an array member-value is computed by calling
* the appropriate overloading of
* {@link java.util.Arrays#hashCode(long[]) Arrays.hashCode}
* on the value. (There is one overloading for each primitive
* type, and one for object reference types.)
* </ul>
*
* @return the hash code of this annotation
*/
int hashCode();
/**
* Returns a string representation of this annotation. The details
* of the representation are implementation-dependent, but the following
* may be regarded as typical:
* <pre>
* @com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
* </pre>
*
* @return a string representation of this annotation
*/
String toString();
/**
* Returns the annotation type of this annotation.
*/
Class<? extends Annotation> annotationType();
}
Annotation的成员变量在Annotation定义中以无参方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称为配置参数。类型只能是“八种基本数据类型、String型、Class型、enum型、Annotation类型、以及以上所有类型的数组”(不能为包装类,Collection容器,例如Map,List)。
/**
* @author jiangl
* @version 1.0
* @date 2021/4/21 14:02
*/
public @interface MyOwnAnnotation {
/**
* 看着是方法,其实是注解的成员变量
* Annotation的成员变量在Annotation定义中以无参方法的形式来声明。
* 其方法名和返回值定义了该成员的名字和类型。
* @return
*/
public String value();
}
可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字
package com.jl.java.base.myannotation;
import java.util.List;
/**
* @author jiangl
* @version 1.0
* @date 2021/4/21 14:02
*/
public @interface MyOwnAnnotation {
/**
* 看着是方法,其实是注解的成员变量
* Annotation的成员变量在Annotation定义中以无参方法的形式来声明。
* 其方法名和返回值定义了该成员的名字和类型。
* @return
*/
public String value() default "1231";
public String[] value1() default {"dasfd","dfasdf"};
public int[] value2() default {1,2};
}
如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是“参数名 = 参数值”,如果只有一个参数成员,且名称为value,可以省略“value="
/**
* @author jiangl
* @version 1.0
* @date 2021/4/21 14:10
*/
@MyOwnAnnotation(value = "12121",value1 = {"ddd","da"})
public class UserMyAnnotation {
}
/**
* @author jiangl
* @version 1.0
* @date 2021/4/21 14:02
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
@Documented
@Inherited
public @interface MyOwnAnnotation {
/**
* 看着是方法,其实是注解的成员变量
* Annotation的成员变量在Annotation定义中以无参方法的形式来声明。
* 其方法名和返回值定义了该成员的名字和类型。
* @return
*/
public String value() default "1231";
public String[] value1() default {"dasfd","dfasdf"};
public int[] value2() default {1,2};
}
- JDK中的元注解
-
JDK的元Annotation用于修饰其他Annotation定义,对现有的注解进行解释说明的注解
-
JDK5.0提供了4中元注解
- Retention
- Target
- Documented
- Inherited
-
解释四个元注解
-
@Retention:只能用于修饰一个Annotation定义,用于指定该Annotation的生命周期,@Retention包含一个RetentionPolicy类型的成员变量,使用@Retention时必须为该value成员变量指定值:
-
RetentionPolicy.SOURCE:在源文件中有效(即修饰的注解仅在源文件中保留),编译器直接丢弃这种策略的注解
-
RetentionPolicy.CLASS:在class文件中有效(即修饰的注解仅在class中保留),当运行java程序时,jvm不会保留注解,这是默认策略
-
RetentionPolicy.RUNTIME:在运行时有效(即修饰的注解在运行时保留),当运行java程序时,jvm会保留注解。被RetentionPolicy.RUNTIME 策略修饰的注解,在程序中可以通过反射获取到该注解
-
RetentionPolicy 作用 SOURCE 在源文件中有效(即修饰的注解仅在源文件中保留),编译器直接丢弃这种策略的注解 CLAS 在class文件中有效(即修饰的注解仅在class中保留),当运行java程序时,jvm不会保留注解,这是默认策略 RUNTIME 在运行时有效(即修饰的注解在运行时保留),当运行java程序时,jvm会保留注解。被RetentionPolicy.RUNTIME 策略修饰的注解,在程序中可以通过反射获取到该注解 -
package java.lang.annotation; public enum RetentionPolicy { /** * 在源文件中有效(即修饰的注解仅在源文件中保留),编译器直接丢弃这种策略的注解 */ SOURCE, /** * 在class文件中有效(即修饰的注解仅在class中保留),当运行java程序时,jvm不会保留注解,这是默认策略 */ CLASS, /** * 在运行时有效(即修饰的注解在运行时保留),当运行java程序时,jvm会保留注解。被 RetentionPolicy.RUNTIME 策略修饰的注解,在程序中可以通过反射获取到该注解 * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MRb6pn7-1618993675344)(D:\personal\文档\文档\javaBase\枚举类与注解\注解\pic\RetentionPolicy作用视图.png)]
-
-
@Target:用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰哪些程序元素。@Target也包含一个名为value[] 的成员变量。value[]的类型为ElementType
-
ELEMENTYPE 作用 TYPE 作用于class类、interface接口、@interface注解、enum枚举类 CONSTRUCTOR 作用于描述构造器 FIELD 作用于描述域(属性、成员变量) LOCAL_VARIABLE 作用于描述局部变量 METHOD 作用于描述方法 PACKAGE 作用于描述包 PARAMETER 作用于描述参数 -
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE }
-
-
@Documented:用于指定该元Annotation修饰的Annotation类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的。
- 定义为@Documented的注解必须设置RetentionPolicy值为RUNTIME
-
@Inherited:被@Inherited修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。
-
比如:如果把标有@Inherited注解的自定义注解标注在类级别上,子类则可以继承父类类级别的注解
-
实际应用中,使用较少
-
/** * @author jiangl * @version 1.0 * @date 2021/4/21 14:10 */ @MyOwnAnnotation(value = "12121",value1 = {"ddd","da"}) public class UserMyAnnotation { public static void main(String[] args) { Class childClass = Child.class; Annotation[] annotations = childClass.getAnnotations(); for(int i=0;i<=annotations.length-1;i++){ System.out.println(annotations[i]); } } } class Child extends UserMyAnnotation{ }
-
结果:
- @com.jl.java.base.myannotation.MyOwnAnnotation(value=12121, value1=[ddd, da], value2=[1, 2])
-
-
-
通过反射来获取注解
package com.jl.java.base.myannotation; import java.lang.annotation.Annotation; /** * @author jiangl * @version 1.0 * @date 2021/4/21 14:10 */ @MyOwnAnnotation(value = "12121",value1 = {"ddd","da"}) public class UserMyAnnotation { public static void main(String[] args) { Class childClass = Child.class; Annotation[] annotations = childClass.getAnnotations(); for(int i=0;i<=annotations.length-1;i++){ System.out.println(annotations[i]); } } } class Child extends UserMyAnnotation{ }
-
jdk8中的注解新特性:可重复注解,类型注解
可重复注解:
1.在Father上声明@Repeatable,成员值为Fathers.class
2.Father的Target比Fathers的小 ,Retention 比Fathers的大(要包含),但是具体可重复的作用是以Fathers的为准的,@Inherited两者需要相同
场景:
在jdk8之前,如果有需求需要在一个类上加两个一样的注解,如果使用以下方式会编译报错Duplicate annotation
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; /** * @author jiangl * @version 1.0 * @date 2021/4/21 15:28 */ @Father @Father public class RepeatAnnotationTest { } @Retention(RetentionPolicy.RUNTIME) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Documented @Inherited @interface Father{ }
解决方法:
定义一个注解数组
package com.jl.java.base.myannotation; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; /** * @author jiangl * @version 1.0 * @date 2021/4/21 15:28 */ @Fathers({@Father,@Father}) public class RepeatAnnotationTest { } @Retention(RetentionPolicy.RUNTIME) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Documented @Inherited @interface Father{ } @interface Fathers{ Father[] value(); }
在jdk8及以后,新增了@Repeatable注解
1.先定义一个数组注解@interface Fathers
2.在需要使用重复注解的注解上加上@Repeatable()
3.将数组注解的类对象作为参数传给@Repeatable()的value属性,如@Repeatable(value = Fathers.class)
4.最后可以使用重复注解了
5.需要注意的是,数组注解的@Retention 策略 和 @Target 策略 需要大于 使用的可重复的注解的@Retention 策略 和 @Target 策略
-
package com.jl.java.base.myannotation; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; /** * @author jiangl * @version 1.0 * @date 2021/4/21 15:28 */ @Father @Father public class RepeatAnnotationTest { } @Retention(RetentionPolicy.RUNTIME) @Repeatable(Fathers.class) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Documented @Inherited @interface Father{ } @Retention(RetentionPolicy.RUNTIME) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @interface Fathers{ Father[] value(); }
类型的注解:
- 在JDK1.8之后,关于元注解@Target的参数类型ElementType枚举值多了两个:TYPE_PARAMETER,TYPE_USER
- 在java 8 之前,注解只能是在声明的地方所使用,Java8开始,注解可以应用在任何地方了。
- ElementType.TYPE_PARAMETER 表示该注解能卸载类型变量的声明语句中(如:泛型声明)
- ElementType.TYPE_USER 表示注解能写在使用类型的任何语句中
public enum ElementType {
...
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
package com.jl.java.base.myannotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
/**
* @author jiangl
* @version 1.0
* @date 2021/4/21 16:18
*/
public class TypeAnnotationTest<@Annotation1 T> {
public static void main(String[] args) throws @Annotation1 Exception{
@Annotation1 String a = "dfasfd";
List<@Annotation1 String> str = null;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
@interface Annotation1{
}