有目錄, 不迷路
- 什么是注解?
- 注解的作用
- 內置注解
- @Override
- @Deprecated
- @SuppressWarnings
- 元注解
- @Target
- @Retention
- 自定義注解
- 尾聲
什么是注解?
注解(Annotation),又稱作元資料,翻開之前我寫過關于Java語言發展史的博客:那些年Java走過的路,找到其中的<Java語言發展狀況>章節便可知,它是屬于JDK5.0引入的技術,距今已有17年歷史了,可見他還是個少年,或許他也曾夢想仗劍走天涯,可惜bug多沒去,


說起注解,大家可能會想到詞匯表注解、古文注解或者注釋,

我們第一次接觸注解,可能是使用子類繼承父類的方法(任何物件的父類都是Object),看到重寫的注解@Override:
package annotation;
/**
* @ClassName Son
* @Description 注解演示
* @Author 古闕月
* @Date 2021/4/8 19:00
* @Version 1.0
*/
public class Son {
@Override // 重寫的注解
public String toString() {
return super.toString();
}
}
到后來Spring等框架中普遍使用了各種各樣的注解,注解的語法格式非常簡單:@ + 注解名,但是偏偏一個小小的注解,卻能在框架中實作各種各樣的功能,讓人不禁覺得灰常奇妙,而且簡單優雅!!!
注解的作用
注解(Annotation)主要有兩個作用:
- 注解(Annotation)就像注釋(Comment)一樣,它不屬于程式本身,但是可以對程式作出解釋,就比如我們一看到
@Override注解,就知道該注解下的方法為重寫父類的方法, - 我們可以通過其他程式,如編譯器讀取注解,然后再對注釋下的程式進行檢查和約束,或者我們可以利用反射技術來讀取注解,從而實作各種各樣的功能, 如重寫后的方法必須和父類的方法同名,我們試著改變方法名,就會出現報錯提醒:


內置注解
JDK內置了三個注解,下面來簡單介紹一下,
@Override
@Override注解的簡單使用在之前的章節已經介紹過了,它位于java.lang包下,官方解釋,看下面的檔案截圖:表示一個方法宣告打算重寫超類中的另一個方法宣告,如果方法利用此注釋型別進行注解但沒有重寫超類方法,則編譯器會生成一條錯誤訊息, 是不是跟我之前介紹的一樣?

我們再點進去@Override來看一下它的源代碼:
package java.lang;
import java.lang.annotation.*;
/**
* @author Peter von der Ahé
* @author Joshua Bloch
* @jls 9.6.1.4 @Override
* @since 1.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
感覺似乎很懵,沒有關系,之后給你細細講解,
@Deprecated
@Deprecated注解 同樣位于java.lang包下,官方解釋為:用@Deprecated 注釋的程式元素,不鼓勵程式員使用這樣的元素,通常是因為它很危隙訓存在更好的選擇,在使用不被贊成的程式元素或在不被贊成的代碼中執行重寫時,編譯器會發出警告,

此注解用于修飾構造方法(CONSTRUCTOR)、屬性(FIELD)、區域變數(LOCAL_VARIABLE)、方法(METHOD)等,如我們用一個方法舉例子:
/**
* 廢棄的方法
*/
@Deprecated
public static void deprecatedMethod() {
System.out.println("我是一個被廢棄的方法...");
}
當我們試圖使用deprecatedMethod()方法時,便會出現下劃線廢棄警告,表示不建議使用,危隙訓者有更好的選擇,

當然,你要是想強行使用,也是可以的:
package annotation;
import lombok.ToString;
/**
* @ClassName Son
* @Description 注解演示
* @Author 古闕月
* @Date 2021/4/8 19:00
* @Version 1.0
*/
public class Son {
/**
* 廢棄的方法
*/
@Deprecated
public static void deprecatedMethod() {
System.out.println("我是一個被廢棄的方法...");
}
@Override // 重寫的注解
public String toString() {
return super.toString();
}
public static void main(String[] args) {
// 不建議使用的方法,不安全或者有更好的選擇
deprecatedMethod();
}
}
運行,得:

同樣,這里貼出@Deprecated的源代碼:
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
/**
* @author Neal Gafter
* @since 1.5
* @jls 9.6.3.6 @Deprecated
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
如果看不懂,沒關系,之后講解,
@SuppressWarnings
@SuppressWarnings看字面意思很好理解:鎮壓警告 的意思,和之前的兩兄弟@Override以及@Deprecated一樣,位于java.lang包下,官方意思是:用于取消注釋元素中指定顯示的編譯器警告,詳情可見下圖檔案截圖:

這樣看來似乎有些小抽象,沒關系,我們來舉一個例子:
public void suppressWarningsMethod() {
double num = 3.14;
}
上文是一個方法,如果suppressWarningsMethod()方法以及num數值沒有被使用過的話,是會被暗化顯示的:

我們完全可以用@SuppressWarnings注解來取消這個編譯器警告:
@SuppressWarnings("unused") // 取消未使用便做暗化處理的編譯器警告
public void suppressWarningsMethod() {
double num = 3.14;
}
效果如下:

這個時候有細心的小伙伴可能就會發現了:之前使用重寫注解@Override以及廢棄注解@Deprecated時是直接使用的,可使用這個抑制警告注解@SuppressWarnings時,怎么是這么用的:@SuppressWarnings("unused") ,這個括號里面雙引號里面的 unused 是啥意思?
不要急,我們先來看一下
@SuppressWarnings注解的原始碼
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
/**
* @author Josh Bloch
* @since 1.5
* @jls 4.8 Raw Types
* @jls 4.12.2 Variables of Reference Type
* @jls 5.1.9 Unchecked Conversion
* @jls 5.5.2 Checked Casts and Unchecked Casts
* @jls 9.6.3.5 @SuppressWarnings
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
這個時候,我們就會發現@SuppressWarnings注解跟之前兩個注解一個很大的不同,多了一個:
String[] value();
這個value()像方法又沒有方法體,型別還是個String陣列,好像讓人有些看不懂!
這里提前透露一下,這個
value()就是這個@SuppressWarnings的配置引數,value為這個配置引數的引數名,而value()是注解中配置引數的宣告形式,而String[]則是這個名為value的配置引數的型別!!!
所以,當我們使用@SuppressWarnings注解時,后面需要加上引數,由勺ò干知,這個引數還可以為多個:
@SuppressWarnings({"unused", "null"}) // 取消編譯器警告
public void suppressWarningsMethod() {
double num = 3.14;
}
那么,這個引數值是不是隨便加的呢?那倒也不是,不同的引數有不同的含義,負責鎮壓不同的警告,具體含義如下表所示:
| 引數值 | 含義 |
|---|---|
| all | to suppress all warnings(抑制所有警告) |
| boxing | to suppress warnings relative to boxing/unboxing operations(要抑制與箱/非裝箱操作相關的警告) |
| cast | to suppress warnings relative to cast operations(為了抑制與強制轉換操作相關的警告)) |
| dep-ann | to suppress warnings relative to deprecated annotation(要抑制相對于棄用注釋的警告) |
| deprecation | to suppress warnings relative to deprecation(要抑制相對于棄用的警告) |
| fallthrough | to suppress warnings relative to missing breaks in switch statements(在switch陳述句中,抑制與缺失中斷相關的警告) |
| finally | to suppress warnings relative to finally block that don’t return(為了抑制警告,相對于最終阻止不回傳的警告) |
| hiding | to suppress warnings relative to locals that hide variable(為了抑制本地隱藏變數的警告) |
| incomplete-switch | to suppress warnings relative to missing entries in a switch statement (enum case)(為了在switch陳述句(enum案例)中抑制相對于缺失條目的警告) |
| nls | to suppress warnings relative to non-nls string literals(要抑制相對于非nls字串字面量的警告) |
| null | to suppress warnings relative to null analysis(為了抑制與null分析相關的警告) |
| rawtypes | to suppress warnings relative to un-specific types when using generics on class params(在類params上使用泛型時,要抑制相對于非特異性型別的警告) |
| restriction | to suppress warnings relative to usage of discouraged or forbidden references(禁止使用警告或禁止參考的警告) |
| rawtypes | to suppress warnings relative to un-specific types when using generics on class params(在類params上使用泛型時,要抑制相對于非特異性型別的警告) |
| serial | to suppress warnings relative to missing serialVersionUID field for a serializable class(為了一個可串行化的類,為了抑制相對于缺失的serialVersionUID欄位的警告) |
| static-access | o suppress warnings relative to incorrect static access(o抑制與不正確的靜態訪問相關的警告) |
| synthetic-access | to suppress warnings relative to unoptimized access from inner classes(相對于內部類的未優化訪問,來抑制警告) |
| unchecked | to suppress warnings relative to unchecked operations(相對于不受約束的操作,抑制警告) |
| unqualified-field-access | to suppress warnings relative to field access unqualified(為了抑制與現場訪問相關的警告) |
| unused | oto suppress warnings relative to unused code(抑制沒有使用過代碼的警告) |
當大家看到最后一個unused時,是不是瞬間就知道了為什么本博主之前用的是@SuppressWarnings("unused")了呢?當然了,抑制警告注解@SuppressWarnings雖然是強迫癥的福利,但是平時作業中使用較少,大家了解下就好,也不必深究,
元注解
在之前我們已經了解了JDK內置的三個注解重寫注解@Override、廢棄注解@Deprecated以及抑制警告注解@SuppressWarnings,想必大家已經比較清楚它們的作用了,那么注解的作用僅限于此嗎?
當然不是,看看框架里面那些注解,比如
@RestController、@Autowired等,就知道注解沒那么簡單了!!!
當然了,我們也可以自定義屬于自己的注解,這么一說,是不是有意思多了呢?
至于怎么自定義屬于自己的注解呢?這就不得不說到元注解了,
那么,什么是元注解呢?我們先來看之前重寫注解@Override的原始碼:
package java.lang;
import java.lang.annotation.*;
/**
* @author Peter von der Ahé
* @author Joshua Bloch
* @jls 9.6.1.4 @Override
* @since 1.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
我們會發現在重寫注解@Override的上方有這樣兩行代碼:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
這個@Target以及@Retention就是所謂的元注解了!
元注解就是注解的注解,用于給其他注解作出說明!
在jdk1.5中總共定義了四個元注解供我們使用,他們分別是:
@Target:用于描述注解的使用范圍,@Retention:用于描述注解的生命周期,指示注解要保留多久,@Documented:說明該注解被包含在javadoc中,@Inherited:表示子類可以繼承父類的該注解,
然后,在jdk1.8中又新定義le兩個元注解:
@Native: 表示被注解的內容是原生(本機)相關的,(說白了就是去呼叫非Java語言實作的代碼,比如C)@Repeatable:表示該注解可以重復使用,
大家看到這里,想必也有些懵了:6個元注解,該怎么用呀?
不必擔心,簡化思維,我們重點關注前面兩個元注解就好了:@Target以及@Retention,而且這兩個元注解里面的值也是規定好了的列舉值,為什么說這兩個注解很重要呢?因為其他注解都可以少,唯獨這兩個注解必不可少!!!
@Target
含義之前已經講過了:用于描述注解的使用范圍,
我們來看一下@Target的源代碼:
package java.lang.annotation;
/**
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 9.7.4 Where Annotations May Appear
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
這樣我們就會發現注解@Target里面的引數值是個ElementType陣列:
ElementType[] value();
點開ElementType的源代碼一看,這是個列舉型別:
package java.lang.annotation;
/**
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 4.1 The Kinds of Types and Values
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, // 類
/** Field declaration (includes enum constants) */
FIELD, // 屬性
/** Method declaration */
METHOD, // 方法
/** Formal parameter declaration */
PARAMETER, // 正式引數
/** Constructor declaration */
CONSTRUCTOR, // 建構式
/** Local variable declaration */
LOCAL_VARIABLE, // 區域變數
/** Annotation type declaration */
ANNOTATION_TYPE, // 注解型別
/** Package declaration */
PACKAGE, // 包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER, // 鍵入引數
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE // 使用型別
}
ElementType內各個值的含義已經寫在注釋上了,比如如果我們想某個注解能夠使用的類(TYPE)、方法(METHOD)以及屬性(FIELD)上,則應該在該注解上加上:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
當然了,如果不使用@Target注解,該注解可以作用于任何元素上!!!
@Retention
同樣,該注解的含義之前也已經講過了:用于描述注解的生命周期,指示注解要保留多久,
我們也來看一下它的原始碼:
package java.lang.annotation;
/**
* Indicates how long annotations with the annotated type are to
* be retained. If no Retention annotation is present on
* an annotation type declaration, the retention policy defaults to
* {@code RetentionPolicy.CLASS}.
*
* <p>A Retention meta-annotation has effect only if the
* meta-annotated type is used directly for annotation. It has no
* effect if the meta-annotated type is used as a member type in
* another annotation type.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
發現它的引數值也是一個列舉值RetentionPolicy:
package java.lang.annotation;
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 原始碼
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* 編譯
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* 運行時
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
里面有三個值,它們的含義以及范圍大小關系為:
(SOURCE(原始碼) < CLASS(編譯) < RUNTIME(運行時)
當然了,我們實際使用程序中 一般用runtime,而如果當我們不使用@Retention注解時,默認為CLASS,也就是注解保留到編譯階段,
小小的總結一下:
@Target以及@Retention注解是我們最常用到的注解,但是你不加上也沒關系,因為有默認值,但是我們一般會加上以便加以某種限制,
自定義注解
好了,在上一個章節,我們介紹了元注解,這里我們可以來學著自定義注解了,首先來介紹一下自定義注解的格式:
元注解
修飾符 @interface 注解名 {
配置引數型別 配置引數名(); // 配置引數可以有多個,也可以沒有
}
比如@SuppressWarnings注解:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
當我們宣告了一個自定義注解,則自動繼承了java.lang.annotation.Annotation介面,如我們按照之前的語法規則自定義一個名為myAnnotation的注解:
// 該注解保留在運行時
@Retention(RetentionPolicy.RUNTIME)
// 該注解可作用在類、屬性、方法上
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface myAnnotation {
String[] value();
}
不過需要注意的是:配置引數的型別只能是基本資料型別、String、enum、注解型別,而不能是其他,比如Object、Integer等,
我們來簡單使用一下這個注解吧:
package annotation;
import java.lang.annotation.*;
/**
* @ClassName MyAnnotationTest
* @Description 自定義注解測驗類
* @Author 古闕月
* @Date 2021/4/11 16:23
* @Version 1.0
*/
@myAnnotation("MyAnnotationTest")
public class MyAnnotationTest {
@myAnnotation("main")
public static void main(String[] args) {
}
}
// 該注解保留在運行時
@Retention(RetentionPolicy.RUNTIME)
// 該注解可作用在類、屬性、方法上
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface myAnnotation {
String[] value();
}
此處,還有一點細節,如果自定義注解內配置引數僅有一個時并且配置引數名為value,配置引數名可省略:
@myAnnotation("MyAnnotationTest")
public class MyAnnotationTest {
@myAnnotation("main")
public static void main(String[] args) {
}
}
但是如果有多個,或者配置引數名為一個且不為value,則需要加上引數名,如:
// 該注解保留在運行時
@Retention(RetentionPolicy.RUNTIME)
// 該注解可作用在類、屬性、方法上
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface myAnnotation {
String[] value();
// 修飾符
String modifier();
}
應當加上引數名:
@myAnnotation(value = "MyAnnotationTest", modifier = "public")
public class MyAnnotationTest {
@myAnnotation(value = "main", modifier = "public")
public static void main(String[] args) {
}
}
但是,在配置引數后面可以加上默認值,語法為:
配置引數型別 配置引數名() default 默認值;
如果有默認值的話,我們在使用注解時,可以不手動加入配置引數值,如:
// 該注解保留在運行時
@Retention(RetentionPolicy.RUNTIME)
// 該注解可作用在類、屬性、方法上
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface myAnnotation {
String[] value() default "methodName";
// 修飾符
String modifier() default "public";
}
可以不加配置引數值,后面的括號也可有可無:
@myAnnotation()
public class MyAnnotationTest {
@myAnnotation
public static void main(String[] args) {
}
}
總之,得保證一定有值就對了!!!
尾聲
好了,到這里這篇介紹注解的博客就結束了,如果有一定基礎的小伙伴,估計會說:就這?那框架里面怎么使用的注解呀?你這啥功能都沒實作,都沒有呀!那要這注解有何用?

其實我真的挺冤枉的:博客陸陸續續寫了一個多禮拜,也肝了近一萬五千字了,只來得及寫一些簡單的介紹以及基本語法,實在是肝不動了,至于注解如何結合Spring框架的IOC以及AOP思想,實作種種神奇的功能?比如我們常見的事務,
能看到這里,并且看完我的碎碎念的 估計是神人了,我猜是沒有多少人看到這里的,創作不易,如果覺得不錯的話,順手給個點贊、評論以及收藏加關注唄!如果有哪里講的不對的地方,也非常歡迎評論指正哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/275452.html
標籤:java
上一篇:Java-----多執行緒【執行緒通信問題 與 兩大解決方式】
下一篇:day2 javaee的入門知識
