一、註解
註解:
一、 標準註解:@Override、@Deprecated、@SuppressWarnings
二、 元註解:用於修飾註解的註解,通常用在註解的定義上
1. @Target : 註解的作用目標
- package、type(類、介面、列舉、Annotation型別)
- 型別成員(方法、構造方法、成員變數、列舉值)
- 方法引數和本地變數(如迴圈變數、catch引數)
檢視@Target註解原始碼:
package java.lang.annotation; @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(); }
其中定義了一個value()的陣列,檢視陣列是個列舉類:
package java.lang.annotation; 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 }
前面的幾個都好理解,註釋都有,後面在JDK1.8中新增了兩個(TYPE_PARAMETER和TYPE_USE)
其中TYPE_USE包含以上所有型別,即如果想讓註解標註在上面任何型別,就可以選用TYPE_USE了。而另一個TYPE_PARAMETER也是做引數標註的,和PARAMETER不同的是,TYPE_PARAMETER是作用在泛型引數上的。
例如自定義TYPE_PARAMETER型別的註解@TypeParameterAnnotation:
@Target(ElementType.TYPE_PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface TypeParameterAnnotation { } //將@TypeParameterAnnotation作用於泛型引數之上 public class TypeParameterDemo<@TypeParameterAnnotation T> { public <@TypeParameterAnnotation U> T foo(T t){ return t; } }
2. @Retention : 標註註解被保留時間的長短
- 定義註解的生命週期
檢視@Retention註解原始碼:
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
其中定義了一個型別為RetentionPolicy的value(),進一步檢視RetentionPolicy是個列舉類:
package java.lang.annotation;
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
}
- SOURCE:註解只保留在原始檔,當Java檔案編譯成class檔案的時候,註解被遺棄;
- CLASS:註解被保留到class檔案,但jvm載入class檔案時候被遺棄,這是預設的生命週期;
- RUNTIME:註解不僅被儲存到class檔案中,jvm載入class檔案之後,仍然存在;
這三個可以簡單的理解為Java原始檔(.java檔案) ---> .class檔案 ---> 記憶體中的位元組碼
3. @Documented : 註解是否應該被包含在JavaDoc文件中
4. @Inherited : 是否允許子類繼承該註解
關於@Inherited介紹看這篇文章比較好 https://www.jianshu.com/p/7f54e7250be3
三、 自定義註解
1. 格式
public @interface 註解名 {
修飾符 返回值 屬性名() 預設值;
修飾符 返回值 屬性名() 預設值;
}
2. 註解屬性支援的型別
- 所有基本型別(int float double boolean byte char short long)
- String型別
- Class型別
- Enum型別
- Annotation型別
- 以上所有型別的陣列
3. 關於類的基本資訊
-
Package
public class Package implements java.lang.reflect.AnnotatedElement { //省略... }
Package實現了AnnotatedElement
-
Class
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement { //省略... }
檢視Class發現其實現了AnnotatedElement
-
Constructor
public final class Constructor<T> extends Executable { //省略... } public abstract class Executable extends AccessibleObject implements Member, GenericDeclaration { //省略... } public class AccessibleObject implements AnnotatedElement { //省略... }
Constructor也是實現了AnnotatedElement,其間還繼承了是否可執行以及是否可達兩個類
-
Field
public final class Field extends AccessibleObject implements Member { //省略... } public class AccessibleObject implements AnnotatedElement { //省略... }
Field也實現了AnnotatedElement,其繼承了是否可達這個類
-
Method
public final class Method extends Executable { //省略... } public abstract class Executable extends AccessibleObject implements Member, GenericDeclaration { //省略... } public class AccessibleObject implements AnnotatedElement { //省略... }
Method與Constructor類似,都存在是否可執行以及是否可達,而Field只存在是否可達情況(Field並不被執行),但是四個類都實現了AnnotatedElement,來檢視一下其原始碼(註釋太多都被我幹掉了):
public interface AnnotatedElement {
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
/*
* Definition of associated: directly or indirectly present OR
* neither directly nor indirectly present AND the element is
* a Class, the annotation type is inheritable, and the
* annotation type is associated with the superclass of the
* element.
*/
T[] result = getDeclaredAnnotationsByType(annotationClass);
if (result.length == 0 && // Neither directly nor indirectly present
this instanceof Class && // the element is a class
AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
Class<?> superClass = ((Class<?>) this).getSuperclass();
if (superClass != null) {
// Determine if the annotation is associated with the
// superclass
result = superClass.getAnnotationsByType(annotationClass);
}
}
return result;
}
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.
getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
collect(Collectors.toMap(Annotation::annotationType,
Function.identity(),
((first,second) -> first),
LinkedHashMap::new)),
annotationClass);
}
Annotation[] getDeclaredAnnotations();
}
根據單一職責原則,介面中只定義方法,實現都在實現類中實現的,所以Package、Class、Constructor、Field、Method都是有其對應的實現方法的,這裡我們不關心他們是怎麼實現的,而檢視這幾個方法都是做什麼的,這裡有getDeclaredAnnotation和getAnnotation兩個方法,Declared是自獲取自身的註解,不包括繼承過來的,而getAnnotation是或者所有註解,包括繼承過來的。
//判斷指定型別的註解是否存在於此元素上
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
//獲取物件上單個指定註解
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
//獲取物件上面所有的註解
Annotation[] getAnnotations();
//獲取物件上面指定型別的註解陣列
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
T[] result = getDeclaredAnnotationsByType(annotationClass);
if (result.length == 0 && // Neither directly nor indirectly present
this instanceof Class && // the element is a class
AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
Class<?> superClass = ((Class<?>) this).getSuperclass();
if (superClass != null) {
// Determine if the annotation is associated with the
// superclass
result = superClass.getAnnotationsByType(annotationClass);
}
}
return result;
}
前幾個都沒問題,後面getAnnotationsByType是JDK1.8新增的屬性,我們來舉例說明:
首先定義兩個註解
@Repeatable(MultipleAnnotation.class)
public @interface TypeTestAnnotation {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultipleAnnotation {
TypeTestAnnotation[] value();
}
public class ZhangSan {
@TypeTestAnnotation("第一個")
@TypeTestAnnotation("第二個")
@TypeTestAnnotation("第三個")
String language;
public static void main(String[] args) throws NoSuchFieldException {
TypeTestAnnotation[] languages = ZhangSan.class.getDeclaredField("language").getDeclaredAnnotationsByType(TypeTestAnnotation.class);
for (TypeTestAnnotation language : languages) {
System.out.println(language);
}
}
}
out:
@com.demo.annotation.TypeTestAnnotation(value=第一個)
@com.demo.annotation.TypeTestAnnotation(value=第二個)
@com.demo.annotation.TypeTestAnnotation(value=第三個)
其中@Repeatable註解也是在JDK1.8中新增的,旨在擴充套件重複註解,這裡的TypeTestAnnotation指向儲存註解MultipleAnnotation,1.8之前如果想使用重複註解得這樣定義:
public @interface TypeTestAnnotation {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultipleAnnotation {
TypeTestAnnotation[] value();
}
public class ZhangSan {
@MultipleAnnotation({@TypeTestAnnotation("第一個"),@TypeTestAnnotation("第二個"),@TypeTestAnnotation("第三個")})
String language;
public static void main(String[] args) throws NoSuchFieldException {
MultipleAnnotation language = ZhangSan.class.getDeclaredField("language").getDeclaredAnnotation(MultipleAnnotation.class);
System.out.println(language);
}
}
out:
@com.demo.annotation.MultipleAnnotation(value=[@com.demo.annotation.TypeTestAnnotation(value=第一個), @com.demo.annotation.TypeTestAnnotation(value=第二個), @com.demo.annotation.TypeTestAnnotation(value=第三個)])
這樣做也是可以,但是很明顯結合了@Repeatable的getDeclaredAnnotationsByType更加的人性化。
講完AnnotatedElement之後,進入以下實戰測試:
四、 自定義註解實戰
1. 先定義兩個註解:
//類和方法
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CourseInfoAnnotation {
//課程名稱
String courseName();
//課程標籤
String courseTag();
//課程簡介
String courseProfile();
//課程式號
int courseIndex() default 1503;
}
//欄位
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfoAnnotation {
//名字
String name();
//年齡
int age() default 18;
//性別
String gender() default "男";
//開發語言
String[] language();
}
2. 在定義一個用於標註的類English:
@CourseInfoAnnotation(
courseName = "類名字",
courseTag = "類標籤",
courseProfile = "類簡介")
public class English {
@PersonInfoAnnotation(name = "Joker", language = {"Java", "C++", "Python"})
private String author;
@CourseInfoAnnotation(
courseName = "方法名字",
courseTag = "方法標籤",
courseProfile = "方法簡介",
courseIndex = 200)
public void getCourseInfo(){
}
}
3. 編寫測試類AnnotationParser:
public class AnnotationParser {
//解析類的註解
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.annotation.English");
//獲取class的註解,而非成員或方法的註解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//獲取單個註解
CourseInfoAnnotation annotation2 = clazz.getAnnotation(CourseInfoAnnotation.class);
System.out.println(annotation2);
}
//解析成員變數的註解
public static void parseFieldAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.annotation.English");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
//是否被PersonInfoAnnotation註解修飾
boolean hasAnnotation = declaredField.isAnnotationPresent(PersonInfoAnnotation.class);
if(hasAnnotation){
PersonInfoAnnotation annotation = declaredField.getAnnotation(PersonInfoAnnotation.class);
System.out.println(annotation);
}
}
}
//解析成員方法的註解
public static void parseMethodAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.annotation.English");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
boolean hasAnnotation = declaredMethod.isAnnotationPresent(CourseInfoAnnotation.class);
if(hasAnnotation){
CourseInfoAnnotation annotation = declaredMethod.getAnnotation(CourseInfoAnnotation.class);
System.out.println(annotation);
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
System.out.println("-----------------------------------------------------");
parseFieldAnnotation();
System.out.println("-----------------------------------------------------");
parseMethodAnnotation();
}
}
out:
@com.demo.annotation.CourseInfoAnnotation(courseIndex=1503, courseName=類名字, courseTag=類標籤, courseProfile=類簡介)
@com.demo.annotation.CourseInfoAnnotation(courseIndex=1503, courseName=類名字, courseTag=類標籤, courseProfile=類簡介)
-----------------------------------------------------
@com.demo.annotation.PersonInfoAnnotation(gender=男, age=18, name=Joker, language=[Java, C++, Python])
-----------------------------------------------------
@com.demo.annotation.CourseInfoAnnotation(courseIndex=200, courseName=方法名字, courseTag=方法標籤, courseProfile=方法簡介)
結果無誤,此篇結。