你說啥什麼?註解你還不會?
註解
文末有彩蛋。
一、什麼是註解?
Annotaion
-
註解(Annotaion)
是從JDK5.0開始引入的一種新技術稱之為註解機制。 -
註解(Annotaion)
的格式:- 註解是以"
@註釋名
"在程式碼中使用的,可以新增一些引數值,例如:@GetMapping("/get")
- 註解是以"
-
註解(Annotaion)
可以使用的範圍:-
可以在package、class、method、field等上面使用。例如:
-
@Controller public class RequestController { @DeleteMapping("/delete") @ResponseBody public String delete(String name,Integer id){ JSONObject json = new JSONObject(); json.put("requestType","deleteType"); json.put("name",name); json.put("id",id); return json.toString(); } }
-
-
我們可以通過反射機制程式設計對這些後設資料的訪問。
-
註解有一些特定的功能,例如:
-
當你如果要重寫toString()方法的時候,不是按照規定的名字來寫的話,就會報錯:
-
正常的話,是不會報錯的:
-
說明
@Override
註解帶有檢查的作用。
-
-
二、內建註解
Java內部定義了一套註解,共有7個:
註解名稱 | 作用 |
---|---|
@Override | 檢查該方法是否是重寫方法。如果發現其父類,或者是引用的介面中並沒有該方法時,會報編譯錯誤。 |
@Deprecated | 標記過時方法。如果使用該方法,會報編譯警告。 |
@SuppressWarnings | 指示編譯器去忽略註解中宣告的警告。 |
作用在其他註解的註解(元註解
):在java.lang.annotaion
包中
註解名稱 | 作用 |
---|---|
@Retention | 標識這個註解怎麼儲存,是隻在程式碼中,還是編入class檔案中,或者是在執行時可以通過反射訪問。 |
@Documented | 標記這些註解是否包含在使用者文件中。 |
@Target | 標記這個註解應該是哪種 Java 成員。 |
@Inherited | 標記這個註解是繼承於哪個註解類(預設 註解並沒有繼承於任何子類) |
從 Java 7 開始,額外新增了 3 個註解:
註解名稱 | 作用 |
---|---|
@SafeVarargs | Java 7 開始支援,忽略任何使用引數為泛型變數的方法或建構函式呼叫產生的警告。 |
@FunctionalInterface | Java 8 開始支援,標識一個匿名函式或函式式介面。 |
@Repeatable | Java 8 開始支援,標識某註解可以在同一個宣告上使用多次。 |
三、元註解的使用
(一)前期準備
如果
你想自定義註解
,那麼元註解是必知必會必懂
的。
元註解就是註解自定義註解的註解
。可能有點饒,一會看例子就明白了,直白點就是給你自定義的註解上一定要加的註解
。
作用在其他註解的註解(元註解
):在java.lang.annotaion
包中
註解名稱 | 作用 |
---|---|
@Retention | 標識這個註解怎麼儲存,是隻在程式碼中,還是編入class檔案中,或者是在執行時可以通過反射訪問。 |
@Documented | 標記這些註解是否包含在使用者文件中。 |
@Target | 標記這個註解應該是哪種 Java 成員。 |
@Inherited | 標記這個註解是繼承於哪個註解類(預設 註解並沒有繼承於任何子類) |
我們定義一個類,類的作用就是用來測試我們定義的註解:
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 18:11
* @Description: 自定義註解測試
*/
public class Test {
}
然後建立一個類,把class識別符號改成@interface
:這就是自定義好的註解了。
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 19:00
* @Description: 自定義註解
*/
public @interface MyAnnotaion {
}
現在可以看到就可以使用了,只不過沒有任何的功能:
(二)@Target的用法詳解
我們來賦予一定的功能,來標識這個註解的作用:
加上元註解:@Target,就是用來指出對什麼生效,作用的目標是什麼,可以在什麼地方使用。
可以看到傳遞的是一個E
可以點看@Target的原始碼看一下:
可以看到下面這種情況。
需要提前知道的是,
value()
是接收的引數,並不是一個方法。那麼Target就需要接收一個
ElementType[]
的陣列。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
沒加之前,先改造一下:
可以看到這個註解什麼都沒加,既可以加在類上,也可以加在方法上,也可以加在變數上。
如果我們想做限制呢?只允許我們的這個註解對類生效
我們先來看一下Controller註解
這個應該不陌生,就不解釋了。
那麼我們也可以加上這個:
可以看到效果了,只要類上的生效了,其餘的都報錯了。
那麼這就是元註解Target的作用。
看一下ElementType.java
中列舉的引數,這些都可以使用,瞭解下就好。
package java.lang.annotation;
public enum ElementType {
TYPE, /* 類、介面(包括註釋型別)或列舉宣告 */
FIELD, /* 欄位宣告(包括列舉常量) */
METHOD, /* 方法宣告 */
PARAMETER, /* 引數宣告 */
CONSTRUCTOR, /* 構造方法宣告 */
LOCAL_VARIABLE, /* 區域性變數宣告 */
ANNOTATION_TYPE, /* 註釋型別宣告 */
PACKAGE /* 包宣告 */
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
如果你想讓你的註解對什麼生效,就指定好就OK:
(三)@Retention的用法詳解
Retention中需要傳遞RetentionPolicy。
RetentionPolicy.java
有三個列舉引數,如下:
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation資訊僅存在於編譯器處理期間,編譯器處理完之後就沒有該Annotation資訊了 */
CLASS, /* 編譯器將Annotation儲存於類對應的.class檔案中。預設行為 */
RUNTIME /* 編譯器將Annotation儲存於class檔案中,並且可由JVM讀入 */
}
一般都是呼叫**RUNTIME
**,呼叫RUNTIME
我們可以通過反射拿到相關的資料,來進行處理等。
由於這個不太好驗證,就不一一驗證了,瞭解就好
。
(四)@Documented的用法詳解
如果使用 @Documented 修飾該 Annotation,則表示它可以出現在 javadoc 中。
定義 Annotation 時,@Documented 可有可無;
若沒有定義,則 Annotation 不會出現在 javadoc 中。
(五)@Inherited的用法詳解
子類可以繼承父類的註解。
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 19:00
* @Description: 自定義註解
*/
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotaion {
}
四、自定義引數詳解
註解的引數:引數的型別 + 引數名 ();
如果想有預設值還需要加上default 值。
例如:
//自定義引數1
String name();
//自定義引數2 帶預設值的
String type() default "";
我們給事先定義好的註解類加上一個引數,隨便寫名字即可:
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotaion {
//引數
String name();
}
可以看到我們在使用的時候,就會報錯了,原因是必須要給這個定義好的引數傳遞一個值。
我們傳遞過來值:
@MyAnnotaion(name = "鄭暉")
public void test(){
}
這個時候就有同學要問了:我定義好引數之後,可以不傳遞引數嗎,用到的時候再傳遞。
答案是可以的,如下:設定一個預設值就好了:
String type() default "";
當然了,也可以傳遞很多型別的引數例如:
import java.lang.annotation.*;
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 19:00
* @Description: 自定義註解
*/
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
//String型別
String name();
//int型別
int age() default 0;
//boolean型別
boolean bool() default false;
//char 型別
char cha() default ' ';
//各種陣列型別
String[] strs() default {};
//列舉型別
MyEnum myEnum() default MyEnum.A;
}
列舉型別類定義:
public enum MyEnum {
A,B,C,D;
}
我們在使用的時候,就可以隨心所欲的使用:
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 18:11
* @Description: 自定義註解測試
*/
public class Test {
String data;
@MyAnnotaion(name = "鄭暉",age = 85,cha = 'A',strs = {"aasd","xsw","你好"},myEnum = MyEnum.C)
public void test(){
}
}
五、利用反射操作註解
MyA.java:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 對於我們的作用:標識著加了這個註解的我們才允許掃描
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyA {
}
MyAnnotation.java:
import java.lang.annotation.*;
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 19:00
* @Description: 自定義註解 ---->給特定的方法用的
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//String型別
String name();
}
Test.java:
/**
* @Auther: truedei
* @Date: 2020 /2020/9/6 18:11
* @Description: 自定義註解測試
*/
@MyA
public class Test {
String data;
@MyAnnotation(name = "鄭暉")
public void test(String name){
System.out.println("我的名字:"+name);
}
}
測試類:
package cn.annotaion;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
import java.lang.reflect.Method;
/**
*測試類
*/
public class AnnotationTest {
public static void main(String[] args) throws Exception {
//拿到指定的類的Test
Class cl = Class.forName("cn.annotaion.Test");
//判斷是否是我們特定自定義的註解 如果是就掃描
if (cl.isAnnotationPresent(MyA.class)) {
Method[] methods = cl.getMethods();
for (Method method : methods) {
// 判斷 somebody() 方法是否包含MyAnnotation註解
if(method.isAnnotationPresent(MyAnnotation.class)){
// 獲取該方法的MyAnnotation註解例項
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
// 獲取 myAnnotation的值,並列印出來
String name = myAnnotation.name();
System.out.println(name);
//執行這個方法
method.invoke(new Test(),name);
}
}
}
}
}
執行:
六、彩蛋
看完了之後,發現有什麼問題嗎?
細心的你一定注意到:最初我使用的是MyAnnotaion.java
後來MyAnnotation.java
沒錯,少寫了個t
。還好不影響大局。
相關文章
- 為什麼你還是學不會程式設計程式設計
- 看完動畫你還敢說不會 快速排序動畫排序
- 什麼?你還不會用位運算來操作狀態?
- 【Spring註解驅動開發】你還不會使用@Resource和@Inject註解?那你就out了!!Spring
- 看完這個你還不會 插入排序 麼排序
- 你瞭解什麼是微前端嗎?說說你對它的理解前端
- 這就是為什麼你學不會DDD
- 你還不會ES的CUD嗎?
- 你說你學不動啦,看 Redux 作者怎麼說?Redux
- 為什麼說會不會SQL,決定著你的工資?方向不對,努力也白費!SQL
- 為啥你用@JsonFormat註解時,LocalDateTime會反序列化失敗?JSONORMLDA
- 9012年,鐵汁你為什麼還不上手Kotlin?Kotlin
- SAP:什麼熱 什麼不熱 為什麼你會關心?(轉)
- 你知道什麼是CSS-in-JS嗎?說說你對它的瞭解CSSJS
- 我去,你竟然還不會用 synchronizedsynchronized
- 『學概念找員外』你說什麼?交易還能撤回?
- 還在用Synchronized?Atomic你瞭解不?synchronized
- 做了這麼多年前端,為什麼你還是不會寫業務程式碼?前端
- 我覺得你可能真的還不會JavaJava
- 你竟然還不會用正規表示式?
- 當面試官說 “你還有什麼問題想問的”,你該如何回答?面試
- 當面試官說“你還有什麼問題想問的”,你該如何回答?面試
- 當面試官說 “你還有什麼問題想問的” ,你該如何回答?面試
- 說說你遇到過最大的bug是什麼?怎麼解決的?
- 2020最新全棧必備 Redis,你還不瞭解麼全棧Redis
- 老闆問你什麼是SASE,你該怎麼說?
- 你不是說你會Aop嗎?
- Spring MVC常用註解,你會幾個?SpringMVC
- 聽說你還不會用Dagger2?Dagger2 For Android最佳實踐教程Android
- 30歲後,你還剩下什麼?
- Hive(總)看完這篇,別說你不會Hive!Hive
- 2020 還不會泡 Github 你就落伍了Github
- 說說你做過讓你覺得最滿意的專案是什麼?為什麼?
- 面試官:說說你對NoSQL的瞭解,為什麼要有NoSQL面試SQL
- 1、說說你瞭解過、使用過什麼程式語言?比較小他們的優點 2、說說你瞭解過、使用過什麼程式設計工具?程式設計
- 聽說你會 Python ?Python
- 醒醒,Android開發居然只有cv最順手,你還會什麼?Android
- 文科妹子都會用 GitHub,你這個工科生還等什麼Github