天天看點

注解和反射

什麼是注解

​ 不是程式本身,可以對程式作出解釋.(這一點和注釋(comment)沒什麼差別)

​ 可以被其他程式(比如:編譯器等)讀取。

​ 注解是以“@注釋名”在代碼中存在的,還可以添加一些參數值,例如:@SuppressWarnings(value="unchecked").

​ 可以附加在package,class,method,field等上面,相當于給他們添加了額外的輔助資訊,我們可以通過反射機制程式設計實作對這些中繼資料的通路.

​ @Override:定義在java.lang.Overrride中,此注釋隻适用于修辭方法,表示一個方法聲明打算重寫超類中的另一個方法聲明.

​ @Deprecated:定義在java.lang.Deprecated中,此注釋可以用于修辭方法、屬性、類,表示不鼓勵程式員使用這樣的元素,通常是因為它很危險或者存在更好的選擇.

​ @SuppressWarnings:定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告資訊.

​ 與前兩個注釋有所不同,你需要添加一個參數才能正确使用,這些參數都是已經定義好了的,我們選擇性的使用就好了.

​ 例:

​ @SuppressWarnings("all")

​ @SuppressWarnings("unchecked")

​ @SuppressWarnings(value={"unchecked","deprecation"})

​ 元注解的作用是負責注解其他注解,Java定義了4個标準的meta-annotation類型,他們被用來提供對其他annotation類型做說明.

​ 這些類型和它們所支援的類在java.lang.annotation包中可以找到(@Tatget、@Retention、@Documented、@Inherlted)

​ @Target:用于描述直接的使用範圍(即:被描述的注解可以用在什麼地方)

​ @Retention:表示需要在什麼級别儲存該注解資訊,用于描述注解的生命周期

​ SOURCE<CLASS<RUNTIME

​ @Document:說明該注解将被包含在javadoc中

​ @Inherited:說明子類可以繼承父類的該注解

​ 使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口

​ 分析:

​ @interface用來聲明一個注解,格式:public @interface 注解名 {定義内容}

​ 其中的每一個方法實際上是聲明了一個配置參數.

​ 方法的名稱就是參數的名稱.

​ 傳回值類型就是參數的類型(傳回值隻能是基本類型,Calss、String、enum).

​ 可以通過default來聲明參數的預設值.

​ 如果隻有一個參數成員,一般參數名為value.

​ 注解元素必須要有值,我們定義注解元素時,經常使用空字元串,0作為預設值.

反射機制

​ 動态語言

​ 是一類在運作時可以改變其結構的語言:例如新的函數、對象、甚至代碼可以被引進,已有的函數可以被删除或是其他結構上的變化。通俗點說就是在運作時代碼可以根據某些條件改變自身結構。

​ 主要動态語言:Object-C、C#、JavaScript、PHP、Python等。

​ 靜态語言

​ 與動态語言相對應的,運作時結構不可變的語言就是靜态語言。如:Java、C、C++。

​ Java不是動态語言,但Java可以稱為“準動态語言”。即Java有一定的動态性,我們可以利用反射機制獲得類似動态語言的特性。Java的動态性讓程式設計的時候更加靈活。

​ Reflection(反射)是Java被視為動态語言的關鍵,反射機制允許程式在執行期借助與Reflection API取得任何類的内部資訊,并能直接操作任意對象的内部屬性及方法。

​ 加載完類之後,在堆記憶體的方法區中就産生了一個Class類型的對象(一個類隻有一個Class對象),這個對象就包含了完整的類的結構資訊。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,是以,我們形象的稱之為:反射

注解和反射

​ 在運作時判斷任意一個對象所屬的類

​ 在運作時構造任意一個類的對象

​ 在運作時判斷任意一個類所具有的成員變量和方法

​ 在運作時擷取泛型資訊

​ 在運作時調用任意一個對象的成員變量和方法

​ 在運作時處理注解

​ 生成動态代理

​ 等等

​ 優點:可以實作動态建立對象和編譯、展現出很大的靈活性

​ 缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼并且它滿足我們的需求。這類操作總是慢于直接執行相同的操作。

​ 在Object類中定義了以下方法,此方法将被所有子類繼承

​ 以上的方法傳回值類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程式運作的結果來看也很好了解,即:可以通過對象反射求出類的名稱。

​ 對象照鏡子後可以得到的資訊:某個類的屬性、方法和構造器、某個類到底實作了哪些接口。對于每個類而言,JRE都為其保留一個不變的Class類型的對象。一個Class對象包含了特定某個結構(class/interface/enum/annotation/primitive type/void/[])的有關資訊。

​ Class本身也是一個類

​ Class 對象隻能由系統建立對象

​ 一個加載的類在JVM中隻會有一個Class執行個體

​ 一個Class對象對應的是一個加載到JVM中的一個.class檔案

​ 每個類的執行個體都會記得自己是由哪個Class 執行個體所生成

​ 通過Class可以完整地得到一個類中的所有被加載的結構

​ Class類是Reflection的根源,針對任何你想動态加載、運作的類,唯有先獲得相應的Class對象

注解和反射

​ 若已知具體的類,通過類的class屬性擷取,該方法最為安全可靠,程式性能最高。

​ 已知某個類的執行個體,調用該執行個體的getClass()方法擷取Class對象

​ 已知一個類的全類名,且該類在類路徑下,可通過Class類的靜态方法forName()擷取,可能抛出ClassNotFoundException

​ 内置基本資料類型可以直接用類名.Type

​ 還可以利用ClassLoader

​ class:外部類、成員(成員内部類、靜态内部類)、局部内部類、匿名内部類。

​ interface:接口

​ []:數組

​ enum:枚舉

​ annotation:注解@interface

​ primitive type:基本資料類型

​ void

注解和反射

​ 當程式主動使用某個類時,如果該類還未被加載到記憶體中,則系統會通過如下三個步驟來對該類進行初始化。

注解和反射

​ 加載:将class檔案位元組碼内容加載到記憶體中,并将這些靜态資料轉換成方法區的運作時資料結構,然後生成一個代表這個類的java.lang.Class對象.

​ 連結:将Java類的二進制代碼合并到JVM的運作狀态之中的過程。

​ 驗證:確定加載的類資訊符合JVM規範,沒有安全方面的問題

​ 準備:正式為類變量(static)配置設定記憶體并設定類變量預設初始值的階段,這些記憶體都将在方法區中進行配置設定。

​ 解析:虛拟機常量池内的符号引用(常量名)替換為直接引用(位址)的過程。

初始化:

執行類構造器()方法的過程。類構造器()方法是由編譯器自動收集類中所有類變量的指派動作和靜态代碼塊中的語句合并産生的。(類構造器是構造類資訊的,不是構造該類對象的構造器)。

當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。

虛拟機會保證一個類的()方法在多線程環境中被正确加鎖和同步。

​ 類的主動引用(一定會發生類的初始化)

​ 當虛拟機啟動,先初始化main方法所在的類

​ new一個類的對象

​ 調用類的靜态成員(除了final常量)和靜态方法

​ 使用java.lang.reflect包的方法對類進行反射調用

​ 當初始化一個類,如果其父類沒有被初始化,則會先初始化它的父類

​ 類的被動引用(不會發生類的初始化)

​ 當通路一個靜态域時,隻有真正聲明這個域的類才會被初始化。如:當通過子類引用父類的靜态變量,不會導緻子類初始化

​ 通過數組定義引用,不會觸發此類的初始化

​ 引用常量不會觸發此類的初始化(常量在連結階段就存入調用類的常量池中了)

​ 類加載的作用:将class檔案位元組碼内容加載到記憶體中,并将這些靜态資料轉換成方法區的運作時資料結構,然後再堆中生成一個代表這個類的java.lang.Class對象,作為方法區中類資料的通路入口。

​ 類緩存:标準的JavaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器中,它将維持加載(緩存)一段時間。不過JVM垃圾回收機制可以回收這些Class對象

注解和反射

類加載器的作用是用來把類裝載進記憶體的。JVM規範定義了如下類型的類的加載器。

注解和反射

​ 運作結果:

​ 通過反射擷取運作時類的完整結構

​ Field、Method、Constructor、Superclass、Interface、Annotation

​ 實作的全部接口

​ 所繼承的父類

​ 全部的構造器

​ 全部的方法

​ 全部的Field

​ 注解

​ ...

​ 建立類的對象:調用Class對象的newInstance()方法

​ 類必須有一個無參數的構造器.

​ 類的構造器的通路權限需要足夠.

​ 隻要在操作的時候明确的調用類中的構造器,并将參數傳遞進去之後,才可以執行個體化操作。

​ 通過Class類的getDeclaredConstructor(Class ... parameterTypes)取得本類的指定形參類型的構造器

​ 向構造器的形參中傳遞一個對象數組進去,裡面包含了構造器中所需的各個參數

​ 通過Constructor執行個體化對象

調用指定的方法

​ 通過反射、調用類中的方法,通過Method類完成

​ 通過Class類中的getMethod(String name,Class...parameterTypes)方法取得一個Method對象,并設定此方法操作時所需要的參數類型

​ 之後使用Object invoke(Object obj,Object[] args)進行調用,并向方法中傳遞要設定的obj對象的參數資訊

​ Object對應原方法的傳回值,若原方法無傳回值,此時傳回null

​ 若原方法若為靜态方法,此時形參Object obj可為null

​ 若原方法形參清單為空,則Object[] args為null

​ 若原方法聲明為private,則需要在調用此invoke()方法前,顯式調用方法對象的setAccessible(true)方法,将可通路private的方法。

注解和反射

setAccessible

​ Method和Field、Constructor對象都有setAccessible()方法

​ setAccessible作用是啟動和禁用通路安全檢查的開關

​ 參數值為true則訓示反射的對象在使用時應該取消Java語言通路檢查。

​ 提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁的被調用,那麼請設定為true。

​ 使得原本無法通路的私有成員也可以通路

​ 參數值為false則訓示反射的對象應該實施Java語言通路檢查

​ Java采用泛型擦除的機制來引入泛型, Java中的泛型僅僅是給編譯器javac使用的,確定資料的安全性和免去強制類型轉換問題,但是,一旦編譯完成,所有和泛型有關的類型全部擦除

​ 為了通過反射操作這些類型,Jave新增了ParameterizedType , GenericArrayType ,TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型.

​ ParameterizedType:表示一種參數化類型,比如Collection

​ GenericArrayType:表示一種元素類型是參數化類型或者類型變量的數組類型

​ TypeVariable:是各種類型變量的公共父接口

​ WildcardType:代表一種通配符類型表達式

​ ORM

​ Object relationship Mapping -> 對象關系映射

注解和反射

​ 類和表結構對應

​ 屬性和字段對應

​ 對象和記錄對應