註解與反射基礎知識梳理
註解
1.什麼是註解
-
不是必須的,但是可以對程式做出解釋,可以被其他程式(比如:編譯器)讀取。
-
格式:@註釋名 ,可以對其新增一些引數。
例如 :
@SuppressWarnings(value="unchecked")
-
註釋可以使用在package,class,method,field等上面,給其新增輔助資訊。
2.內建註解
@Override
@Deprecated
@SuppressWarnings
3.元註解
@Target
:用於描述註解的使用範圍
//自定義註解的方法 @Target(value=ElementType.MEYHOD) public @interface MyAnnotation{ } //Target原始碼 @Documented //註解被包含於javadoc @Retention(RetentionPolicy.RUNTIME)//執行時作用 @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value();//一個列舉型別的陣列 } //可以傳入的引數 package java.lang.annotation; public enum ElementType { TYPE, /* 類、介面(包括註釋型別)或列舉宣告 */ FIELD, /* 欄位宣告(包括列舉常量) */ METHOD, /* 方法宣告 */ PARAMETER, /* 引數宣告 */ CONSTRUCTOR, /* 構造方法宣告 */ LOCAL_VARIABLE, /* 區域性變數宣告 */ ANNOTATION_TYPE, /* 註釋型別宣告 */ PACKAGE /* 包宣告 */ }
@Retention
:表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期- SOURCE<CLASS<RUNTIME
/** * 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}.(預設是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.4.2 @Retention */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); } //引數取值的說明 package java.lang.annotation; public enum RetentionPolicy { SOURCE, /* 若 Annotation 的型別為 SOURCE,則意味著:Annotation 僅存在於編譯器處理期間,編譯器處理完之後,該 Annotation 就沒用了。 例如," @Override" 標誌就是一個 Annotation。當它修飾一個方法的時候,就意味著該方法覆蓋父類的方法;並且在編譯期間會進行語法檢查!編譯器處理完後,"@Override" 就沒有任何作用了。 */ CLASS, /* 編譯器將Annotation儲存於類對應的.class檔案中。預設行為 */ RUNTIME /* 編譯器將Annotation儲存於class檔案中,並且可由JVM讀入 */ }
@Document
:說明該註解將被包含在Javadoc中@Inherited
:說明子類可以繼承父類中的註解- 假設,我們定義了某個 Annotaion,它的名稱是 MyAnnotation,並且 MyAnnotation 被標註為 @Inherited。現在,某個類 Base 使用了MyAnnotation,則 Base 具有了"具有了註解 MyAnnotation";現在,Sub 繼承了 Base,由於 MyAnnotation 是 @Inherited的(具有繼承性),所以,Sub 也 "具有了註解 MyAnnotation"。
4.自定義註解
- 使用
@interface
- 每個方法實際上聲明瞭一個配置該註解的引數
- 方法的名稱就是引數的名稱
- 返回值型別就配置引數的型別(引數型別只能是Class,String,enum)
- 可以通過預設default來宣告引數的預設值
- 只有一個方法設定方法名稱為value,在使用時可以省略引數的名稱
//自定義註解
@Target({ElementType.Type,ElementType.METHOD})//可以用在類,方法上
@Retention(RetentionPolicy.RUNTIME)//執行時作用
public @interface MyAnnotation{
String name() default ""; //設定預設值為空,可以另外顯示賦值
}
反射
1.動態語言與靜態語言
- 動態語言:可以在執行時根據某些條件改變自身結構
- 主要動態語言:C#,JavaScript,PHP,Python,Object-C
- 靜態語言:執行時結構不可變的語言就是靜態語言
- 如:java,c,c++
2.反射
-
Reflection
(反射)是java被視為動態語言的關鍵,反射機制允許程式在 在執行期間通過反射取得任何類的內部資訊,並能夠直接操作任意物件。正常方式:
引入需要的"包類"名稱
---->通過new例項化
---->取得例項化物件
反射方式:
例項化物件
---->getClass()方法
---->得到完整的"包類"
-
在載入完類之後,在堆記憶體的方法區就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件包含了完整類的結構資訊。
Class c = Class.forName(" ")
3. Class類
-
物件照鏡子後的得到的資訊:某個類的屬性,方法,構造器,實現的介面,對每個類而言,JRE都為其保留了一個不變的Class型別的物件。一個Class物件包含了特定某個結構的有關資訊。
-
Class本身也是一個類
-
Class只能由系統建立物件
-
一個載入的類在JVM中只會有一個Class例項
-
一個Class物件對應於一個載入到JVM的一個Class檔案
-
每個例項都可溯源其由哪一個Class例項所建立
-
通過Class可以完整的得到一個類中所被載入的結構
-
Class類是Reflection的根源,唯有先獲取對應的Class物件才能動態的載入
-
只要元素型別和維度一樣就是同一個Class
//舉例 public class Demo2 { public static void main(String[] args) { //Cylinder是一個圓柱體類 Class<Cylinder> aClass = Cylinder.class; Cylinder[] cylinders = new Cylinder[10];//維度不同 Class<? extends Cylinder[]> aClass1 = cylinders.getClass(); System.out.println(aClass1.hashCode()); System.out.println(aClass.hashCode()); } } //結果: //所設定的圓柱體的體積是:3141.59 //1556595366 //985922955
-
-
Class類中常用的方法
-
static forName(String name) //返回指定類名的Class物件
-
Object newInstance() //呼叫預設建構函式函式(不帶引數的建構函式),返回Class物件的一個例項
-
Field[] getDeclaredFields() //返回 Field 物件的一個數組,這些物件反映此 Class 物件所表示的類或介面所宣告的所有欄位。
-
-
獲取Class類的方法(Runtime階段、Class類物件階段、Source原始碼階段)
-
若已知某個物件的例項,呼叫該例項的
getClass()
方法獲取Class物件Class c = person.getClass();
-
a.若已知具體的類,通過class屬性獲取,該方法最為安全可靠,程式效能最高
Class c = Preson.class;
-
已知一個類的全名稱,且該類在類的路徑下,可以通過Class類的靜態方法
forName()
獲取,可能丟擲ClassNotFoundException
Class c = Class.forName("demo.Person");
-
基本資料型別的包裝類
Inteage.TYPE
Java記憶體
public class Student { int score; int age; String name; Computer computer; public void study() { System.out.println("studying..."); } }
-
- 棧空間(stack),連續的儲存空間,遵循後進先出的原則,用於存放區域性變數、基本變數型別及其值、引用物件的變數即引用的地址。
- 堆空間(heap),不連續的空間,用於存放new出的物件,或者說是類的例項,可以被所用的執行緒共享,不會存放別的物件的引用。
- 方法區(method),方法區在堆空間內,用於存放①Class類的程式碼資訊;②靜態變數和方法;③常量池(字串常量等,具有共享機制)
-
類的載入及
ClassLoder
的理解-
載入:將class檔案的位元組碼內容載入到記憶體中,並將這些靜態資料轉換成為方法區的執行時資料結構,然後生成一個代表該類的java.lang.Class物件。(意味著不可自己建立class物件,必須由系統建立)
-
連結:將java類的二進位制程式碼合併到JVM的執行狀態之中的過程。
- 驗證:驗證類的資訊是否符合JVM規範及安全要求
- 準備:為類變數(static)分配記憶體空間並設定預設初始值的階段,記憶體在方法區中被分配(static首先分配記憶體)
- 解析:將常量池中的符號引用替換為直接引用(地址)
-
初始化
靜態程式碼塊
---->構造方法
---->程式碼
- 什麼時候會發生類初始化
- 當虛擬機器啟動時,先初始化main方法所在的類
- new一個類的物件(new 一個物件的陣列不會發生初始化)
- 呼叫類的靜態成員(除了final常量)和靜態方法
- 使用
java.lang.reflect
報的方法對類進行反射呼叫 - 初始化一個類但是其父類沒有被初始化,會先初始化其父類
- 是麼時候不會被動引用
- 當訪問一個靜態域時只有真正宣告這個域的類才會被初始化。如:通過子類引用弗雷德靜態變數,子類不會被初始化。
- 通過陣列定義類的引用,不會觸發此類的初始化。(只是開闢了記憶體空間)
- 引用常量不會觸發(例如final修飾的)
- 什麼時候會發生類初始化
-
載入器的型別
-
引導類載入器:C++編寫,JVM自帶的類載入器,負責java核心庫,無法直接獲取,rt.jar包下。
-
擴充套件類載入器
-
系統類載入器
雙親委派機制:許可權由上至下降低
-
-
動態建立物件執行
//獲取指定類的class物件
Class<Cylinder> cylinderClass = Cylinder.class;
//獲取指定的構造器方法
Constructor<Cylinder> constructor =cylinderClass.getConstructor(double.class);
//使用指定的構造器方法建立一個例項物件
Cylinder cylinder = constructor.newInstance(10); //此時已經可以以直接呼叫方法
//獲取指定類的方法
Method volume = cylinderClass.getMethod("volume", double.class);//指定方法的名字,返回值的clss物件
System.out.println(volume.invoke(cylinder,10));//呼叫invoke()方法傳遞一個例項化物件去執行及需要執行的方法的引數列表,invoke啟用。
Cylinder cylinder1 = constructor.newInstance(20);
//獲取指定的欄位
Field height = cylinderClass.getDeclaredField("height");
//關閉安全檢測,暴力反射
height.setAccessible(true);
//設定屬性
height.set(cylinder1,30);
System.out.println(cylinder1.getHeight());//輸出結果為30
Object invoke(Object obj,Object... args)
//Object對應原方法的返回值,如原方法沒有返回值,此時返回null
//若原方法為靜態方法,此時形參Object obj可為null
//若原方法的形參列表為空,則Object[] args為null
public class Cylinder {
private double height;
public double getHeight() {
return height;
}
public Cylinder(double height) {
this.height = height;
}
public double volume(double radius){
return height*(new AreaImpl().CalculateArea(radius));
}
}
獲取註解資訊
-
ORM ---->物件關係對映
- 類和表結構的對應
- 屬性和欄位的對應
- 物件和記錄對應
-
反射操作註解
//首先獲取要獲得的註解所對應的元素 Class c = Person.class; //舉例獲得的是一個類的Class物件,想要獲得其註解 <A extends Annotation> A getAnnotation(Class<A> annotationClass) //如果存在該元素的指定型別的註釋,則返回這些註釋,否則返回 //獲得註解的物件獲得其資訊