注解
文末有彩蛋,
一、什么是注解?
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,還好不影響大局,
CSDN認證博客專家
Linux
分布式
Java
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/11711.html
標籤:其他
上一篇:關羽glfw resize的問題
下一篇:萬字圖解Java多執行緒

