概述
Java語言中的類、方法、變量、參數和包等都可以被標註。和Javadoc不同,Java標註可以通過反射獲取標註內容。在編譯生成類文件時,標註可以被嵌入到字節碼中。Java虛擬機可以保留標註內容,在運行時可以獲取到標註內容。 當然它也支持自定義Java標註
JDK1.5之後的特徵
用於說明程序
一般在框架中使用
格式:
@AnnotationName
文檔註釋:
@param @return @Exeception 從根本上是一個註釋,不存在代碼編譯,不會生成對應的.class字節碼問題,只是提供給JavaDoc API文件生成工具。作爲標記生成對應的文檔。
註解是有一部分參與編譯
@Override並不是沒編譯就有效果了,是因爲不管是Eclipse還是IDEA都可以預編譯Java代碼生成對應的.class文件的
JDK註解
@Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
這個註解當添加在方法之上時,表示這個方法是一個繼承父類重寫方法或者是遵從接口實現的方法必須符合方法重寫和實現的標準,與原來的方法一致,當與原來的方法不一致時會有編譯錯誤。
@Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
這個註解被添加時該對象會被標註爲過時,在方法上會出現刪除線
@SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
在我們方法中有時候會出現黃色的警告,但是我們已經明確該過程不會出現異常,我們可以通過該註解進行錯誤壓制,強制提醒編譯器不要拋出警告。
自定義註解
自定義註解格式
public @interface AnnotationName() {
// 屬性列表
}
我們可以通過反編譯工具可以得到,其實是一個接口繼承了Annotation
得到的
【Annotation本質】
public interface MyAnnotation1 extends java.lang.annotation.Annotation {
}
註解屬性列表允許的類型
- 八種基本數據類型
- String類
- 枚舉類
- 數組類型
- 註解類
註解的註解元註解
@Retention()
加在註解之上,用來表明該註解被用於代碼的那個階段,屬於註解的生命週期
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
該註解屬性值有三個取值
SOURCE
只在源碼時生效,在編譯之後這個註解會被編譯器給丟棄,我們@Override()
註解就是這樣一個形式。CLASS
該註解在編譯時會將其一起編譯進字節碼文件,但是在虛擬機運行時,不會產生作用。RUNTIME
該註解在編譯時一同編譯進字節碼文件,同時會參與虛擬機的運行。
@Target()
該註解用於表明,被修飾的註解只用作用於哪些單位,如果被標註在不允許的單位上,將會出現編譯錯誤,編譯不通過。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
ElementType[]
有以下取值(ElementType是一個枚舉類型):
TYPE
表明被修飾的註解可以作用於類上。FIELD
表明被修飾的註解可以作用於成員變量上,包括枚舉類型成員。METHOD
表明被修飾的註解可以作用於方法上。PARAMETER
表明被修飾的註解可以作用於形式參數上CONSTRUCTOR
表明被修飾的註解可以作用於構造方法上LOCAL_VARIABLE
表明被修飾的註解可以作用於局部變量中ANNOTATION_TYPE
表明被修飾的註解可以作用於註解之上PACKAGE
表明被修飾的註解可以作用於包名之上TYPE_PARAMETER
表明被修飾的註解可以作用於類型聲明中,jdk1.8之後特點
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_PARAMETER)
public @interface PersonAnnotation {
}
public <@PersonAnnotation() T> T test2(T t) {
return t;
}
TYPE_USE
表明被修飾的註解可以作用於使用類型的任何地方,jdk1.8之後特點
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface PersonAnnotation {
}
public <T> T test2(T t) {
short num = (@PersonAnnotation short) 1;
return t;
}
@Documented()
表明被該註解修飾的註解,被使用的地方在生成API文檔的時候會保留下來
@Inherited()
被該註解修飾的註解,在使用的地方,子類的這個屬性同樣會有這樣的註解
@Repeatable
可重複註解,jdk1.8之後的新註解
public class RepeatingAnnotations {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filters {
Filter[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Filters.class)
public @interface Filter {
String value();
}
@Filter("filter1")
@Filter("filter2")
public interface Filterable {
}
public static void main(String[] args) {
for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) {
System.out.println(filter.value());
}
}
註解的使用
註解使用需要通過反射的機制來實現的,這裏我們通過一個自制的junit
單元測試註解和一個通過註解實例化對象的例子說明,註解的使用方式。
自制單元測試註解,記錄出錯的方法,並將日誌輸出文件
package com.justLym.demo.annotation.inter;
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 Check {
}
package com.justLym.demo.annotation;
import com.justLym.demo.annotation.inter.Check;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author justLym
* @version 1.0.0
* @date 2020/3/10 20:31
**/
public class MainTest {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
// 獲取被執行對象的字節碼
Class<AnnotationTest> annotationTestClass = AnnotationTest.class;
// 創建被執行對象的實例對象
AnnotationTest annotationTest = annotationTestClass.newInstance();
// 獲取字節碼中所有方法
Method[] declaredMethods = annotationTestClass.getDeclaredMethods();
// 創建文件輸出流,填好輸出位置
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("./src/log.txt")));
// 遍歷方法
for (Method declaredMethod : declaredMethods) {
declaredMethod.setAccessible(true);
// 判斷方法上面是否有自定義註解
if (declaredMethod.isAnnotationPresent(Check.class)) {
try {
// 執行方法
declaredMethod.invoke(annotationTest);
} catch (Exception e) {
writer.write("出錯的方法:" + declaredMethod.getName());
writer.newLine();
writer.write("出錯的原因:" + e.getCause().getClass().getName());
writer.newLine();
writer.write("出錯的詳細信息:" + e.getCause().getMessage());
writer.newLine();
writer.write("-------------------------------------------");
writer.newLine();
}
}
}
// 關閉輸出流
writer.close();
}
}
通過註解配置對象,然後實例化
package com.justLym.demo.annotation.inter;
import com.justLym.demo.annotation.myEnum.PersonEnum;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@Documented
@Inherited
public @interface PersonAnnotation {
int id() default 1;
/**
* 使用註解時,該屬性可不寫,默認未justLym
*/
String name() default "justLym";
/**
* 設置屬性爲枚舉類型
*/
PersonEnum kind() default PersonEnum.STUDENT;
}
package com.justLym.demo.annotation.myEnum;
public enum PersonEnum {
STUDENT, TEACHER
}
package com.justLym.demo.annotation;
import com.justLym.demo.annotation.entity.Person;
import com.justLym.demo.annotation.inter.Check;
import com.justLym.demo.annotation.inter.PersonAnnotation;
import com.justLym.demo.annotation.myEnum.PersonEnum;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author justLym
* @version 1.0.0
* @date 2020/3/10 19:12
**/
public class AnnotationTest {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
// 獲取添加註解方法所在類的字節碼文件
Class<AnnotationTest> annotationTestClass = AnnotationTest.class;
// 獲取需要實例化對象的字節碼
Class<Person> personClass = Person.class;
// 調用無參構造方法,實例化對象
Person instance = personClass.newInstance();
// 獲取註解所在類所有方法
Method[] declaredMethods = annotationTestClass.getDeclaredMethods();
// 賦予權限
AccessibleObject.setAccessible(declaredMethods, true);
// 遍歷方法
for (Method declaredMethod : declaredMethods) {
// 判斷方法是否被這個註解修飾
if (declaredMethod.isAnnotationPresent(PersonAnnotation.class)) {
// 獲取該方法上的註解
PersonAnnotation annotation = declaredMethod.getAnnotation(PersonAnnotation.class);
// 獲取註解對應的屬性,並賦值
Field id = personClass.getDeclaredField("id");
id.setAccessible(true);
id.set(instance, annotation.id());
// 獲取註解對應的屬性,並賦值
Field name = personClass.getDeclaredField("name");
name.setAccessible(true);
name.set(instance, annotation.name());
// 獲取註解對應的屬性,並賦值
Field personEnum = personClass.getDeclaredField("personEnum");
personEnum.setAccessible(true);
personEnum.set(instance, annotation.kind());
}
}
System.out.println(instance);
}
@PersonAnnotation(id = 2, name = "justLym2", kind = PersonEnum.TEACHER)
public static void test3() {
}
}