Java基礎(chǔ)知識之注解、元注解
注解
Java注解也稱Java標(biāo)注,是jdk1.5(5.0)后的新特征。Java語言中的類、方法、變量、參數(shù)和包等都可以被標(biāo)注。和Javadoc不同,Java注解可以通過反射獲取標(biāo)注內(nèi)容,在編譯器生成類文件時,標(biāo)注可以被嵌入到字節(jié)碼中,Java虛擬機(jī)可以保留標(biāo)注內(nèi)容,在運行時可以獲取到標(biāo)注內(nèi)容,當(dāng)然它也支持自定義Java標(biāo)注
功能:用于說明程序
用途:一般用在框架中使用
格式:@AnnotationName
文檔注釋:
? @param @return @Exception從根本上是一個注釋,不存在代碼編譯,不會生成對應(yīng)的文檔
注解:
? @Override并不是沒有編譯就有效果,是因為不管是Eclipse還是IDEA都可以預(yù)編譯Java代碼生成對應(yīng)的.class文件的
注解作用
生成文檔
代碼中生成對應(yīng)的JavaDoc API文檔
@param @return
【IDEA JavaDoc工具使用參數(shù)】
? Other Command Line Arguments:-encoding utf-8 -charset utf-8
? 解決中文亂碼,因為IDEA默認(rèn)編碼集為UTF-8 windows默認(rèn)編碼集為 GBK
代碼檢查
繼承重寫,或者說接口遵從之后的實現(xiàn)中,存在@Override
代碼數(shù)據(jù)獲?。骸拘⌒涂蚣堋?/strong>
通過反射獲取指定注解中的一些內(nèi)容,例如:配置,數(shù)據(jù),操作,驗證等
Java預(yù)定義的注解
@Override
? 重寫/實現(xiàn)方法的情況下,檢查方法聲明是否和父類或者接口中的方法聲明一致,強制格式檢查
@Deprecated
標(biāo)記當(dāng)前方法已過時
@SuppressWarnings(“all”)
壓制警告,可以用于一些代碼中存在明確無異常的情況下,壓制一些警告
Annotation注解屬性【難點】
屬性
? 開發(fā)實際使用注解的方式中,數(shù)據(jù)使用方式更加偏向于屬性概念
? 使用
- 書寫代碼中使用
@MyAnnotation(id=1, name=“ocean”, age=16) - 使用反射時,會涉及到getXXX方法
通過屬性名獲取對應(yīng)值的概念來完成的
實際上是利用abstract方法來完成屬性概念的
屬性使用的格式【實際按照方法格式操作】
- 屬性的值數(shù)據(jù)類型和對應(yīng)的具體數(shù)據(jù) => 返回值類型和返回的數(shù)據(jù)屬性類型支持:
- 基本數(shù)據(jù)類型
- String類型
- 其他數(shù)據(jù)類型
- enmu枚舉類型,一個帶有名字的常量,為了更好的閱讀性和操作
- 以上類型對應(yīng)的數(shù)組
- 屬性值要求
- 定義屬性時可以使用default關(guān)鍵字,加上默認(rèn)值,該屬性在使用的過程中是沒有強制要求屬性值,如果沒有賦予屬性值,采用對應(yīng)的默認(rèn)值操作,如果賦值,使用對應(yīng)值
- 如果注解中有且只有一個value屬性,或者說注解中除value屬性之外,都有默認(rèn)值,不管是類,方法,成員變量,包使用當(dāng)前注解是可以直接在括號內(nèi)加入
對應(yīng)數(shù)據(jù)類型數(shù)值- 如果屬性是數(shù)組類型, {}大括號保存,并且不同的內(nèi)容,使用,隔開屬性的鍵名字 ==> 方法的名字

自定義注解
格式:
public @interface AnnotationName {
屬性列表;
}
Annotation注解是可以編譯得到對應(yīng)的.class字節(jié)碼文件,驗證了注解是可以參與編譯過程的
通過反編譯工具可以得到一下內(nèi)容
【Annotation本質(zhì)】
public interface MyAnnotation1 extends java.lang.annotation.Annotation {
}
MyAnnotation1
本質(zhì)是一個interface,同時java.lang.annotation.Annotation 子接口
package cn.ocean888.a_annotation.MyAnnotation;
/**
* 自定義注解!??!
* public interface MyAnnotation1 extends java.lang.annotation.Annotation {
* }
*
* @author Anonymous
* @date 2020/3/10 11:01
*/
public @interface MyAnnotation1 {
// 屬性 ==> 方法形式
}
元注解
基于注解的解釋,用來約束注解的的一些操作問題
@Retention
表示這個注解的保存方式,是只在代碼中,還是編入class文件中,或者是運行時可以通過反射訪問
RetentionPolicy.RUNTIME:當(dāng)前注解會編譯生成對應(yīng)的.class字節(jié)碼文件,并且可以加載到JVM中,參與代碼執(zhí)行
RetentionPolicy.CLASS
RetentionPolicy.SOURCE:注解將被編譯器丟棄(該類型的注解信息只會保留在源碼里,源碼經(jīng)過編譯后,注解信息會被丟棄,不會保留在編譯好的class文件里)
@Document
標(biāo)記這些注解是否包含在用戶文檔中
是否可以通過JavaDoc工具,生成對應(yīng)的API文檔
@Target
標(biāo)記這個注解應(yīng)該是那種Java成員
屬性:ElementType
? TYPE:當(dāng)前注解可以用于類聲明
? METHOD:當(dāng)前注解可以用于方法聲明位置
? FIELD:當(dāng)前注解可以用于成員變量聲明位置
@Inherited
標(biāo)記這個注解是繼承于那個注解類(默認(rèn) 注解不繼承于任何子類)
獲取類上的注解
Java獲取類上的注解有下面3個方法:
- Class.getAnnotations() 獲取所有的注解,包括自己聲明的以及繼承的
- Class.getAnnotation(Class< A > annotationClass) 獲取指定的注解,該注解可以是自己聲明的,也可以是繼承的
- Class.getDeclaredAnnotations() 獲取自己聲明的注解、
java.lang.Class類的isAnnotation()方法用于檢查此Class是否為Annotation類型
java.lang.Class類的isAnnotationPresent()方法用于檢查此類中是否存在指定注釋類型的注釋
實例:
實例1:兩種屬性文件的加載的方式

1.properties
className = cn.ocean888.a_annotation.Person id = 1 name = "ocean"
Person類
package cn.ocean888.a_annotation;
public class Person {
Integer id;
String name;
public Person() {
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
第一種使用反射來完成
package cn.ocean888.a_annotation;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException {
Properties properties = new Properties();
properties.load(new FileInputStream("./src/1.properties"));
String className = properties.getProperty("className");
String id = properties.getProperty("id");
String name = properties.getProperty("name");
System.out.println(className);
/*
使用反射的方法
*/
Class<?> aClass = Class.forName(className);
Person person = (Person) aClass.getConstructor().newInstance();
System.out.println(person);
Field declaredField = aClass.getDeclaredField("id");
declaredField.setAccessible(true);
declaredField.set(person, Integer.parseInt(id));
Field declaredField2 = aClass.getDeclaredField("name");
declaredField2.setAccessible(true);
declaredField2.set(person, name);
System.out.println(person);
}
}

第二種使用反射加注解的方式來完成
自定義注解
package cn.ocean888.a_annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 聲明當(dāng)前注解有且只能用于類名之上
@Target(ElementType.TYPE)
// 當(dāng)前注解參與代碼運行
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotaion1 {
// 屬性
String className();
int id();
String name();
}
ReflectAnnotation.java
package cn.ocean888.a_annotation;
import java.lang.annotation.Annotation;
@MyAnnotaion1(className = "cn.ocean888.a_annotation.Person",
id = 2,
name = "ocean")
public class ReflectAnnotation {
public static void main(String[] args) {
// 加載ReflectAnnotation
Class<ReflectAnnotation> cls = ReflectAnnotation.class;
// 因為注解再類名之上,通過Class獲取對應(yīng)的Annotation
MyAnnotaion1 annotation = cls.getAnnotation(MyAnnotaion1.class);
String s = annotation.className();
int id = annotation.id();
String name = annotation.name();
System.out.println(s);
System.out.println(id);
System.out.println(name);
}
}

實例2使用注解測試代碼運行
對Tools方法中帶有@Check注解標(biāo)記的方法進(jìn)行檢查

Tools.java
package cn.ocean888.a_annotation_checkMethod;
import java.util.ArrayList;
/**
* 需要測試的方法
*/
public class Tools {
@Check
public void test1() {
String str = null;
System.out.println(str.toString());
}
@Check
public void test2() {
int[] arr = null;
System.out.println(arr[5]);
}
@Check
public void test3() {
int[] arr = {1,2,3,4,5};
System.out.println(arr[3]);
}
@Check
public void test4() {
ArrayList<Integer> integers = new ArrayList<>();
System.out.println(integers.get(20).toString());
}
@Check
public void test5() {
throw new NullPointerException("NullPointException");
}
}
Utils.java
package cn.ocean888.a_annotation_checkMethod;
public class Utils {
@Check
public void test() {
throw new IndexOutOfBoundsException("下標(biāo)越界");
}
}
Check.java
package cn.ocean888.a_annotation_checkMethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 該注解沒有任何屬性,只是作為是否需要測試的標(biāo)記
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
ClassAnnotation.java
package cn.ocean888.a_annotation_checkMethod;
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 ClassAnnotation {
String className();
}
TestProject.java
package cn.ocean888.a_annotation_checkMethod;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 測試Tools類內(nèi)的方法,如果方法帶有Check注解,執(zhí)行測試并記錄異常
*/
@ClassAnnotation(className = "cn.ocean888.a_annotation_checkMethod.Tools")
public class TestProject {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
// 從注解中獲取對應(yīng)的屬性
Class<TestProject> testProjectClass = TestProject.class;
// 獲取所有指定的注解
ClassAnnotation annotation = testProjectClass.getAnnotation(ClassAnnotation.class);
String s = annotation.className();
// s = cn.ocean888.a_annotation_checkMethod.Utils
Class<?> aClass = Class.forName(s);
Object tools = aClass.getConstructor().newInstance();
// 獲取所有Tools類內(nèi)的方法,不包括父類方法
Method[] declaredMethods = aClass.getDeclaredMethods();
// 記錄錯誤出現(xiàn)次數(shù)
int count = 0;
long l = System.currentTimeMillis();
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("./src/log.txt"));
// 遍歷方法數(shù)組
for (Method declaredMethod : declaredMethods) {
declaredMethod.setAccessible(true);
// 判斷當(dāng)前方法是否帶有注解@Check標(biāo)記
if (declaredMethod.isAnnotationPresent(Check.class)) {
try {
declaredMethod.invoke(tools);
} catch (Exception e) {
count += 1;
// 1.哪一個方法出現(xiàn)異常
bufferedWriter.write("方法:" + declaredMethod.getName());
bufferedWriter.newLine();
// 2.發(fā)生異常原因,獲取對應(yīng)的類型
bufferedWriter.write("異常類型:" + e.getCause().getClass().getSimpleName());
bufferedWriter.newLine();
// 3.異常信息
bufferedWriter.write("異常信息:" + e.getCause().getMessage());
bufferedWriter.newLine();
}
}
}
long l1 = System.currentTimeMillis();
bufferedWriter.write("出現(xiàn)錯誤的次數(shù)" + count);
bufferedWriter.newLine();
bufferedWriter.write("總耗時" + (l1 - l));
bufferedWriter.close();
}
}
代碼的靈活性在于可以對className直接進(jìn)行替換


注解使用總結(jié)
- 注解在大多數(shù)情況下,都是使用過程,而不是自定義,會使用到框架中預(yù)處理好的注解
- 注解使用對象
- 編譯器
- 解析代碼
- JVM運行代碼使用
- 注解是一個標(biāo)簽,有時候是做標(biāo)記,有時候標(biāo)記有屬性
注解和python裝飾器的區(qū)別
先說java的注解(Annotation),實際上是給語法元素打一個標(biāo)記。比如你可以給一個函數(shù)打一個標(biāo)記,給一個類打一個標(biāo)記等等。Java只保證記錄這個標(biāo)記,但是不會主動根據(jù)這給標(biāo)記做任何事。
比如,你在Spring里,給一個私有成員打 @Autowired 這個標(biāo)記。
public class XXXService {
@Autowired
private XXXXRepository xxxxRepository;
// ...
}
如果你不用Spring框架的話,不會有任何事情發(fā)生,直接訪問這個字段就是空。當(dāng)如果你配置了合適的處理流程,而這個流程就會根據(jù)有沒有這個標(biāo)記干活。比如你要求Spring “Auto Scan” 并且注入依賴,這個處理過程會用反射去讀哪些元素被做了某個特定標(biāo)記。沒有標(biāo)記就不理,有標(biāo)記就注入。
python里的decorator是一個語法糖,是希望把“decorator”這個形式寫得更漂亮。比如,你想記錄一個函數(shù)開始執(zhí)行之前和之后的log:
def foo():
print("Hello")
def logit(fn):
def inner():
print("before execute")
fn()
printf("after execute")
return inner
這時,你可以魔改以下foo的實現(xiàn),用logit這個“裝飾器”來部分修改foo的行為,然后執(zhí)行:
foo = logit(foo) foo()
但python里的語法可以讓這個東西寫成:
@logit
def foo():
print("Hello")
foo()
也就是說,python這里的裝飾器是一個有邏輯的,可以執(zhí)行的函數(shù),只不過其寫法有些特殊要求;而Java里面的Annotation只是個標(biāo)記,需要其他代碼來“根據(jù)標(biāo)記執(zhí)行“。
當(dāng)然,裝飾器模式是個很通用的東西,無論是python,java還是其他語言都可以寫。只是python提供了特殊的語法糖而已。但java世界里做類似decorator的事情,希望動態(tài)魔改一個函數(shù)的行為,可以用動態(tài)代理或者AOP。
Java的Annotation因為相當(dāng)于多加了一層(標(biāo)記 + 處理邏輯),是一把雙刃劍。好處是,在不動代碼的情況下你可以通過外部配置來修改程序的行為。比如給一個函數(shù)打上@Test標(biāo)。如果通過UT框架運行,這些打標(biāo)的函數(shù)會被當(dāng)作是測試用例;但如果外部直接用普通的main啟動,這些@Test就會沒有一樣,不會影響代碼本身的邏輯。但反過來,也容易引來一些問題。比如有的時候,你很難知道那個根據(jù)標(biāo)記執(zhí)行的邏輯是不是真的跑了。也許你哪里配置拼錯一個字,或者classpath少依賴一個包,就造成那個邏輯并沒有真的執(zhí)行。這時從表面上也許很難看出來出錯了。
總結(jié)
到此這篇關(guān)于Java基礎(chǔ)知識之注解、元注解的文章就介紹到這了,更多相關(guān)Java注解、元注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼
本篇文章主要介紹了Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01
Java8中l(wèi)ambda表達(dá)式的應(yīng)用及一些泛型相關(guān)知識
這篇文章主要介紹了Java8中l(wèi)ambda表達(dá)式的應(yīng)用及一些泛型相關(guān)知識的相關(guān)資料2017-01-01
在Java的Spring框架的程序中使用JDBC API操作數(shù)據(jù)庫
這篇文章主要介紹了在Java的Spring框架的程序中使用JDBC API操作數(shù)據(jù)庫的方法,并通過示例展示了其存儲過程以及基本SQL語句的應(yīng)用,需要的朋友可以參考下2015-12-12
spring boot高并發(fā)下耗時操作的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于spring boot高并發(fā)下耗時操作的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

