1. 注解基本概念
注解,什么是注解?
打開百度搜索

好,看不懂
沒關系
一步一步慢慢來
先不管注解,注釋這個概念應該就很熟悉了,檔案注釋,單行注釋,多行注釋
注釋是對一段程式,一個方法,一個類進行描述,是給我們程式員看的,都知道,注解是不會被編譯的,會被忽略
注解,同樣的道理,其實就是用來說明代碼的,但是注解是 給計算機看的,是會被編譯的
因此:
注解概念:jdk1.5之后出現,是對程式進行說明,并且會被編輯,給計算機看的
來看看toString 方法

Override 這個單詞并不陌生java基礎—重寫、多載 ,沒錯 重寫,子類重寫父類的方法
如果我們 把這個方法改一下
可以看到注解 報錯了 錯誤資訊:

方法不是重寫父類的方法
就像我們之前說的函式式介面 java8 (jdk 1.8) 新特性——Lambda
@FunctionalInterface 注解一樣
所以注解的其中一個功能就出來了: 用來校驗,編譯檢查
注解的格式:@+名稱
2. JDK 中的內置注解
java提供了5個基本的注解:

這五個注解的介紹百度百科已經說的很清楚了,這邊直接參考
限定父類重寫方法:@Override
當子類重寫父類方法時,子類可以加上這個注解,那這有什么什么用?這可以確保子類確實重寫了父類的方法,避免出現低級錯誤
2. 標示已過時:@Deprecated
這個注解用于表示某個程式元素類,方法等已過時,當其他程式使用已過時的類,方法時編譯器會給出警告(洗掉線,這個見了不少了吧),
3.抑制編譯器警告:@SuppressWarnings
被該注解修飾的元素以及該元素的所有子元素取消顯示編譯器警告,例如修飾一個類,那他的欄位,方法都是顯示警告
4.“堆污染”警告與@SafeVarargs
想理解這個就要明白什么是堆污染,堆污染是什么?
其實很好理解,就是把不帶泛型的物件賦給一個帶泛型的物件,為什么不行?很簡單,因為不帶泛型的話,默認會給泛型設定為object,意思就是什么型別都可以往里面塞,那你一個不帶泛型的怎么可能給一個帶泛型塞呢,
例如運行如下代碼:
List list = new ArrayList(); list.add(20);
List<String> ls = list; System.out.println(ls.get(0));
則會拋出堆污染例外Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at Test.Test1.main(Test1.java:29)
注意:可變引數更容易引發堆污染例外,因為java不允許創建泛型陣列,可變引數恰恰是陣列,
抑制這個警告的方法有三個:
1.@SafeVarargs修飾引發該警告的方法或構造器
2.使用@suppressWarnings("unchecked")
3.編譯時使用-Xlint:varargs
5.函式式介面與@Functionallnterface
什么是函式式?如果介面中只有一個抽象方法(可以包含多個默認方法或多個static方法)
介面體內只能宣告常量欄位和抽象方法,并且被隱式宣告為public,static,final,
介面里面不能有私有的方法或變數,
這個注解有什么用?這個注解保證這個介面只有一個抽象方法,注意這個只能修飾介面
認識認識,有個印象
3.注解的功能
-
首先一個就是前邊的 用來編譯檢查功能
-
其次,注解還有撰寫檔案的功能
什么?撰寫檔案,一臉懵逼,別急,看看就知道了
jdk 檔案,應該程式員人手都有一份,沒有的伙伴 公眾號內回復【檔案】 獲取

其實這些檔案就是通過注解生成的
事實勝于雄辯
我們先建一個類

桌面新建一個檔案夾 api ,把這個類拷貝進去

cmd 然后進行javadoc 命令

可以看到生成了一堆檔案,

雙擊打開index.html

是不是發現新大陸!!
-
再者,就是代碼分析【這個就是最重要的了,Spring Aop 自定義注解都是這個用的這個功能,當然主要原理還是反射】 java進階—反射
4. 注解本質
前邊 說了,注解語法格式:@+名稱
那么,是不是只要我們 @ +隨便一個名字是不是就是注解呢?@OpLog
@UserLog
你當然可以這樣定義,但并不是按照這種格式寫了,編譯器就能認,它背后是有進行一些操作的,也就是說讓編譯器認你寫的這個是注解
照貓畫虎,我們來看官方是怎么處理的
點開注解原碼

格式:
元注解
public @interface 注解名稱 {
}
@interface 是什么意思
現在我隨便寫了一個類
public @interface MyAnnotation {
}

進行反編譯

可以看到 @interface 注解的本質就是 interface ,只不過繼承了 lang 包的一個類 java.lang.annotation.Annotation
看看api ,Api 公眾號內 回復 【檔案】 獲取

5. 注解屬性
我們都知道,介面中可以定義抽象方法,這邊叫做注解的屬性
屬性的回傳值型別是有要求的
-
八大基本型別
-
列舉
-
String 型別
-
注解
-
Class
-
以上型別的陣列

注意: 回傳值型別不能是 void 跟 類 型別

可以看到報錯了
使用注解,并且對屬性賦值就很簡單了,前邊說了,注解可以在一個類,一個方法上進行標記

可以看到這是我們剛剛定義的屬性,屬性名 = 值 就行了 , 值要跟型別對應上,并且有幾個屬性,就要寫幾個,不然會報錯

6. 值獲取
注解值獲取得通過反射獲取,前提是注解上有保留策略,也就是必須要有元注解,這邊先不管是什么東西,為了演示獲取值我們先加上


運行結果

7.元注解
最后來看元注解
我們知道注解是用來描述程式的
@Retention 這個注解 ,現在把它看做一個程式【只不過這個程式是注解程式】, 那么在它上面的注解是用來描述這個 @Retention 注解程式的
因此:元注解的概念就出來了

元注解:描述注解的注解
按住alt點擊target,可以看到target 注解上面又有元注解,套娃
那么元注解有哪些,JDK給我們定義好了
@Target : 表示注解能夠作用在什么位置【類,方法 等等】
@Retention: 描述注解被保留的階段【java代碼的三個階段】
@Document 描述注解是否抽取到Api檔案中
@Inherited: 注解是否被子類繼承 【加上這個標記,子類會自動繼承父類中的注解】
@Repeatable:java8新增的注解,用于開發重復注解
型別注解;這個也是java8新增的注解,可以用在任何用到型別的地方【其實也就是target 的列舉增加的列舉類值】
-
@Target :
點開原碼,我們可以看到,target 只有一個屬性,這個屬性是列舉陣列,
點開ElementType 就可以看到這就是一個列舉
因此,元注解 @Target 就可以這樣寫

可以看到列舉類中不只有一個 TYPE 屬性,有這個多,我們先注重以下三種就行了,java8 新加的型別注解后面再提

-
ElementType.TYPE: 表示這個注解只能作用在類上
可以看到成員變數跟方法上都報錯
-
ElementType.METHOD :可以作用于方法上
-
ElementType.FIELD:可以作用于成員變數
這兩個就不分別單獨演示了,跟在類上單獨一個意思
我們全都加上看看

可以看到成員變數跟方法上都可以使用這個注解

2. @Retention
同樣有一個列舉屬性


只有三個,這就是java代碼三個階段了 ,從上到下分別表示,源代碼(.java), .class ,以及運行時階段 ,我們自定義 一般都是 采用 RUNTIME
source: 位元組碼檔案都不存在被描述的注解
class: 被描述的注解,會保留到class位元組碼檔案,不會被JVM讀取
runtime: 被描述的注解,會保留class位元組碼檔案, 會被JVM讀取

3. @Document :就使用前邊的javadoc 命令,被注解的注解可以保存在java API檔案中
4. @Inherited :就是子類也能獲取父類 定義的 注解 屬性的值

父類:
/**
* @author java資訊
* @since 2011-11-24
*/
@MyAnnotation(name = "父類的zhangsan")
public class TestApi {
}
子類:
package com.test1.api;
public class Test2 extends TestApi{
public static void main(String[] args) {
MyAnnotation annotation = Test2.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.name());
}
}

5. @Repeatable:可以重復使用一個注解
這個剛好也是前面沒提到的 class 型別

沒加之前

現在我們先定義一個容器注解,也就是注解 屬性 是 注解陣列
@Target(ElementType.TYPE)
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
MyAnnotation[] value();
}
接著 加上

可以看到可以使用重復注解了
@MyAnnotation(name = "zhangsan 1 ")
@MyAnnotation(name = "zhangsan 2")
public class TestApi {
public static void main(String[] args) {
MyAnnotation[] annotationsByType = TestApi.class.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation an : annotationsByType) {
System.out.println(an.name());
}
}
}

6. 型別注解
java8 新加的,在target 列舉屬性中新加的列舉值,這兩個

一般都是使用 TYPE_USER 就夠了,表示注解可以使用在任何地方
比如:lombok 中的@ NonNull注解

我們就可以使用在引數前面

以上就是注解全部內容了,最后最后最后!!!別忘了,公眾號回復【檔案】 獲取api ,感謝閱讀
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/540435.html
標籤:Java
