以前,『XML』是各大框架的青睞者,它以鬆散耦合的方式完成了框架中幾乎所有的配置,但是隨著專案越來越龐大,『XML』的內容也越來越複雜,維護成本變高。
於就有人提出來一種標記式高耦合的配置方式,『註解』。方法上可以進行註解,類別上也可以註解,欄位屬性上也可以註解,反正幾乎需要配置的地方都可以進行註解。
關於『註解』和『XML』兩種不同的配置模式,爭論了好多年了,各有各的優劣,註解可以提供更大的便捷性,易於維護修改,但耦合度高,而XML 相對於註解則是相反的。
追求低耦合就要拋棄高效率,追求效率必然會遇到耦合。本文意不再辨析兩者誰優誰劣,而在於以最簡單的語言介紹註解相關的基本內容。
註解的本質
「java.lang.annotation.Annotation」介面中有這麼一句話,用來描述『註解』。
The common interface extended by all annotation types 所有的注解类型都继承自这个普通的接口(Annotation)
這句話有點抽象,但卻說出了註解的本質。我們來看一個JDK 內建註解的定義:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
這是註解@Override 的定義,其實它本質上就是:
public interface Override extends Annotation{ }
沒錯,註解的本質就是一個繼承了Annotation 介面的接口。有關這一點,你可以去反編譯任一個註解類,你會得到結果的。
一個註解準確意義上來說,只不過是一種特殊的註解而已,如果沒有解析它的程式碼,它可能連註解都不如。
而解析一個類別或方法的註解往往有兩種形式,一種是編譯期直接的掃描,一種是運行期反射。反射的事情我們待會說,而編譯器的掃描指的是編譯器在對java 程式碼編譯字節碼的過程中會偵測到某個類別或方法被一些註解修飾,這時它就會對於這些註解進行某些處理。
典型的就是註解@Override,一旦編譯器偵測到某個方法被修飾了@Override 註解,編譯器就會檢查目前方法的方法簽章是否真正重寫了父類別的某個方法,也就是比較父類別中是否具有一個同樣的方法簽章。
這一種情況只適用於那些編譯器已經熟知的註解類,例如JDK 內建的幾個註解,而你自訂的註解,編譯器是不知道你這個註解的作用的,當然也不知道該如何處理,往往只是會根據該註解的作用範圍來選擇是否編譯進字節碼文件,僅此而已。
元註解
『元註解』是用來修飾註解的註解,通常用在註解的定義上,例如:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
這是我們@Override 註解的定義,你可以看到其中的@Target,@Retention 兩個註解就是我們所謂的『元註解’,『元註解』一般用於指定某個註解生命週期以及作用目標等資訊。 。我剛整理了一套2018最新的0基礎入門和進階教程,無私分享,加Java學習q-u-n :六七八,二四一,五六三即可獲取,內附:開發工具和安裝包,以及系統學習路線圖
JAVA 中有以下幾個『元註解』:
@Target:注解的作用目标 @Retention:注解的生命周期 @Documented:注解是否应当被包含在 JavaDoc 文档中 @Inherited:是否允许子类继承该注解
其中,@Target 用來指明被修飾的註解最終可以作用的目標是誰,也就是指明,你的註解到底是用來修飾方法的?修飾類的?還是用來修飾字段屬性的。
@Target 的定義如下:
我們可以透過以下的方式來為這個value 傳值:
@Target(value = {ElementType.FIELD})
被這個@ Target 註解修飾的註解將只能作用在成員欄位上,不能用於修飾方法或類別。其中,ElementType 是一個列舉類型,有以下一些值:
ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上 ElementType.FIELD:允许作用在属性字段上 ElementType.METHOD:允许作用在方法上 ElementType.PARAMETER:允许作用在方法参数上 ElementType.CONSTRUCTOR:允许作用在构造器上 ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上 ElementType.ANNOTATION_TYPE:允许作用在注解上 ElementType.PACKAGE:允许作用在包上
@Retention 用於指明目前註解的生命週期,它的基本定義如下:
同樣的,它也有一個value 屬性:
@Retention(value = RetentionPolicy.RUNTIME
這裡的RetentionPolicy 仍然是一個枚舉類型,它有以下幾個枚舉值可取:
RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件 RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件 RetentionPolicy.RUNTIME:永久保存,可以反射获取
@Retention 註解指定了被修飾的註解的生命週期,一種是只能在編譯期可見,編譯後會被丟棄,一種會被編譯器編譯進class 檔案中,無論是類別或是方法,乃至字段,他們都是有屬性表的,而JAVA 虛擬機也定義了幾種註解屬性表用於存儲註解信息,但是這種可見性不能帶到方法區,類加載時會予以丟棄,最後一種則是永久存在的可見性。
剩下两种类型的注解我们日常用的不多,也比较简单,这里不再详细的进行介绍了,你只需要知道他们各自的作用即可。@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。
JAVA 的内置三大注解
除了上述四种元注解外,JDK 还为我们预定义了另外三种注解,它们是:
@Override @Deprecated @SuppressWarnings @Override 注解想必是大家很熟悉的了,它的定义如下: @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
它没有任何的属性,所以并不能存储任何其他信息。它只能作用于方法之上,编译结束后将被丢弃。
所以你看,它就是一种典型的『标记式注解』,仅被编译器可知,编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,如果不是,自然不能通过编译。
@Deprecated 的基本定义如下:
依然是一种『标记式注解』,永久存在,可以修饰所有的类型,作用是,标记当前的类或者方法或者字段等已经不再被推荐使用了,可能下一次的 JDK 版本就会删除。
当然,编译器并不会强制要求你做什么,只是告诉你 JDK 已经不再推荐使用当前的方法或者类了,建议你使用某个替代者。
@SuppressWarnings 主要用来压制 java 的警告,它的基本定义如下:
它有一个 value 属性需要你主动的传值,这个 value 代表一个什么意思呢,这个 value 代表的就是需要被压制的警告类型。例如:
public static void main(String[] args) { Date date = new Date(2018, 7, 11); }
这么一段代码,程序启动时编译器会报一个警告。
Warning:(8, 21) java: java.util.Date 中的 Date(int,int,int) 已过时
而如果我们不希望程序启动时,编译器检查代码中过时的方法,就可以使用 @SuppressWarnings 注解并给它的 value 属性传入一个参数值来压制编译器的检查。
@SuppressWarning(value = "deprecated") public static void main(String[] args) { Date date = new Date(2018, 7, 11); }
这样你就会发现,编译器不再检查 main 方法下是否有过时的方法调用,也就压制了编译器对于这种警告的检查。
当然,JAVA 中还有很多的警告类型,他们都会对应一个字符串,通过设置 value 属性的值即可压制对于这一类警告类型的检查。
自定义注解的相关内容就不再赘述了,比较简单,通过类似以下的语法即可自定义一个注解。
public @interface InnotationName{ }
当然,自定义注解的时候也可以选择性的使用元注解进行修饰,这样你可以更加具体的指定你的注解的生命周期、作用范围等信息。
注解与反射
上述内容我们介绍了注解使用上的细节,也简单提到,「注解的本质就是一个继承了 Annotation 接口的接口」,现在我们就来从虚拟机的层面看看,注解的本质到底是什么。
首先,我们自定义一个注解类型:
这里我们指定了 Hello 这个注解只能修饰字段和方法,并且该注解永久存活,以便我们反射获取。
之前我们说过,虚拟机规范定义了一系列和注解相关的属性表,也就是说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:
RuntimeVisibleAnnotations:运行时可见的注解 RuntimeInVisibleAnnotations:运行时不可见的注解 RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解 RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解 AnnotationDefault:注解类元素的默认值
给大家看虚拟机的这几个注解相关的属性表的目的在于,让大家从整体上构建一个基本的印象,注解在字节码文件中是如何存储的。
所以,对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
getAnnotation:返回指定的注解 isAnnotationPresent:判定当前元素是否被指定注解修饰 getAnnotations:返回所有的注解 getDeclaredAnnotation:返回本元素的指定注解 getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
方法、字段中相关反射注解的方法基本是类似的,这里不再赘述,我们下面看一个完整的例子。
首先,设置一个虚拟机启动参数,用于捕获 JDK 动态代理类。
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
然后 main 函数。
我们说过,注解本质上是继承了 Annotation 接口的接口,而当你通过反射,也就是我们这里的 getAnnotation 方法去获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类。
我們執行程式後,會看到輸出目錄裡有這麼一個代理類,反編譯之後是這樣的:
代理類別實作介面Hello 並重寫其所有方法,包括value 方法以及介面Hello 從Annotation 介面繼承而來的方法。
而這個關鍵的 InvocationHandler 實例是誰?
AnnotationInvocationHandler 是 JAVA 中專門用於處理註解的 Handler, 這個類別的設計也非常有趣。
這裡有一個 memberValues,它是一個 Map 鍵值對,鍵是我們註解屬性名稱,值就是該屬性當初被賦上的值。
而這個invoke 方法就很有意思了,大家注意看,我們的代理類別代理了Hello 介面中所有的方法,所以對於代理類別中任何方法的呼叫都會被轉到這裡來。
var2 指向被呼叫的方法實例,而這裡首先用變數var4 取得該方法的簡明名稱,接著switch 結構判斷目前的呼叫方法是誰,如果是Annotation 中的四大方法,將var7 賦上特定的值。
如果目前呼叫的方法是 toString,equals,hashCode,annotationType 的話,AnnotationInvocationHandler 實例中已經預先定義了這些方法的實現,直接呼叫即可。
那麼假如 var7 沒有符合上這四種方法,說明目前的方法呼叫的是自訂註解位元組宣告的方法,例如我們 Hello 註解的 value 方法。 這種情況下,將從我們的註解 map 中取得這個註解屬性對應的值。
其實,JAVA 中的註解設計個人覺得有點反人類,明明是屬性的操作,非要用方法來實現。當然,如果你有不同的見解,歡迎留言探討。
最後我們再總結一下整個反射註解的工作原理:
#首先,我們透過鍵值對的形式可以為註解屬性賦值,像這樣:@Hello (value = "hello")。
接著,你用註解修飾某個元素,編譯器將在編譯期掃描每個類別或方法上的註解,會做一個基本的檢查,你的這個註解是否允許作用在目前位置,最後會將註解資訊寫入元素的屬性表。
然後,當你進行反射的時候,虛擬機器將所有生命週期在 RUNTIME 的註解取出來放到一個 map 中,並創建一個 AnnotationInvocationHandler 實例,把這個 map 傳遞給它。
最後,虛擬機器將採用 JDK 動態代理機制產生一個目標註解的代理類,並初始化好處理器。
那麼這樣,一個註解的實例就創建出來了,它本質上就是一個代理類,你應該去理解好 AnnotationInvocationHandler 中 invoke 方法的實現邏輯,這是核心。一句話概括就是,透過方法名稱傳回註解屬性值。
以上是深入淺析JAVA開發中註解的基本原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JVMmanagesgarbagecollectionacrossplatformseffectivelybyusingagenerationalapproachandadaptingtoOSandhardwaredifferences.ItemploysvariouscollectorslikeSerial,Parallel,CMS,andG1,eachsuitedfordifferentscenarios.Performancecanbetunedwithflagslike-XX:NewRa

Java代碼可以在不同操作系統上無需修改即可運行,這是因為Java的“一次編寫,到處運行”哲學,由Java虛擬機(JVM)實現。 JVM作為編譯後的Java字節碼與操作系統之間的中介,將字節碼翻譯成特定機器指令,確保程序在任何安裝了JVM的平台上都能獨立運行。

Java程序的編譯和執行通過字節碼和JVM實現平台獨立性。 1)編寫Java源碼並編譯成字節碼。 2)使用JVM在任何平台上執行字節碼,確保代碼的跨平台運行。

Java性能与硬件架构密切相关,理解这种关系可以显著提升编程能力。1)JVM通过JIT编译将Java字节码转换为机器指令,受CPU架构影响。2)内存管理和垃圾回收受RAM和内存总线速度影响。3)缓存和分支预测优化Java代码执行。4)多线程和并行处理在多核系统上提升性能。

使用原生庫會破壞Java的平台獨立性,因為這些庫需要為每個操作系統單獨編譯。 1)原生庫通過JNI與Java交互,提供Java無法直接實現的功能。 2)使用原生庫增加了項目複雜性,需要為不同平台管理庫文件。 3)雖然原生庫能提高性能,但應謹慎使用並進行跨平台測試。

JVM通過JavaNativeInterface(JNI)和Java標準庫處理操作系統API差異:1.JNI允許Java代碼調用本地代碼,直接與操作系統API交互。 2.Java標準庫提供統一API,內部映射到不同操作系統API,確保代碼跨平台運行。

modularitydoesnotdirectlyaffectJava'splatformindependence.Java'splatformindependenceismaintainedbytheJVM,butmodularityinfluencesapplicationstructureandmanagement,indirectlyimpactingplatformindependence.1)Deploymentanddistributionbecomemoreefficientwi

BytecodeinJavaistheintermediaterepresentationthatenablesplatformindependence.1)Javacodeiscompiledintobytecodestoredin.classfiles.2)TheJVMinterpretsorcompilesthisbytecodeintomachinecodeatruntime,allowingthesamebytecodetorunonanydevicewithaJVM,thusfulf


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SublimeText3漢化版
中文版,非常好用

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中