Java語法進階15-反射
反射
類載入
類在記憶體中的生命週期:載入-->使用-->解除安裝
當程式主動使用某個類時,如果該類還未被載入到記憶體中,系統會通過載入、連線、初始化三個步驟來對該類進行初始化
類的載入又分為三個階段:
(1)載入:load
就是指將型別的class位元組碼資料讀入記憶體
(2)連線:link
①驗證:校驗合法性等
②準備:準備對應的記憶體(方法區),建立Class物件,為類變數賦預設值,為靜態常量賦初始值。
③解析:把位元組碼中的符號引用替換為對應的直接地址引用
(3)初始化:initialize(類初始化)即執行<clinit>類初始化方法,大多數情況下,類的載入就完成了類的初始化,有些情況下,會延遲類的初始化。
類初始化
1、哪些操作會導致類的初始化?
(1)執行主方法所在的類,要先完成類初始化,再執行main方法
(2)第一次使用某個型別就是在new它的物件,此時這個類沒有初始化的話,先完成類初始化再做例項初始化
(3)呼叫某個類的靜態成員(類變數和類方法),此時這個類沒有初始化的話,先完成類初始化
(4)子類初始化時,發現它的父類還沒有初始化的話,那麼先初始化父類
(5)通過反射操作某個類時,如果這個類沒有初始化,也會導致該類先初始化
2、哪些使用類的操作,但是不會導致類的初始化?
(1)使用某個類的靜態的常量(static final)
(2)通過子類呼叫父類的靜態變數,靜態方法,只會導致父類初始化,不會導致子類初始化。
(3)用某個型別宣告陣列並建立陣列物件時,不會導致這個類初始化
類載入器
(1)引導類載入器(Bootstrap Classloader)又稱為根類載入器
它負責載入jre/rt.jar核心庫,它本身不是Java程式碼實現的,也不是ClassLoader的子類,獲取它的物件時往往返回null
(2)擴充套件類載入器(Extension ClassLoader)
它負責載入jre/lib/ext擴充套件庫,它是ClassLoader的子類
(3)應用程式類載入器(Application Classloader)
它負責載入專案的classpath路徑下的類,它是ClassLoader的子類
(4)自定義類載入器
當你的程式需要載入“特定”目錄下的類,可以自定義類載入器;
載入器的雙親委託模式
下一級的類載入器,如果接到任務時,會先搜尋是否載入過,如果沒有,會先把任務往上傳,如果都沒有載入過,一直到根載入器,如果根載入器在它負責的路徑下沒有找到,會往回傳,如果一路回傳到最後一級都沒有找到,那麼會報ClassNotFoundException或NoClassDefError
類載入器之間不是繼承關係,是組合的方式實現的。
Class
Java反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;
簡單理解為JVM執行時會在方法區記錄所有class檔案中所有類的資訊(類名,屬性,方法),並將所有類又做為Class類的物件。當呼叫某個類時就會將其先載入到記憶體中,
也就是將這個類作為Class類的物件被創建出來。在Class類中定義了許多供操作(此類)的方法(建立物件,呼叫屬性和方法),從而可以動態的修改程式結構
Class物件是反射的根源,所有Java型別都可以獲取它的型別物件。
Class
類的物件是在載入類時由 Java 虛擬機器以及通過呼叫類載入器中的 defineClass方法自動構造,在方法區建立的,不是由程式設計師建立的,Class
沒有公共構造方法。
獲取Class物件的四種方式
(1)型別名.class
要求編譯期間已知型別
(2)物件.getClass()
獲取物件的執行時型別
(3)Class.forName(型別全名稱)
可以獲取編譯期間未知的型別
(4)ClassLoader的類載入器物件.loadClass(型別全名稱)
可以用系統類載入物件或自定義載入器物件載入指定路徑下的型別
獲取型別的詳細資訊
可以獲取:包、修飾符、型別名、父類(包括泛型父類)、父介面(包括泛型父介面)、成員(屬性、構造器、方法)、註解(類上的、方法上的、屬性上的)
public ClassLoader getClassLoader() | 【返回該類的類載入器】 |
public String toString() | 【將物件轉換為字串】 |
public Package getPackage() | 【獲取此類的包】 |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回一個 Constructor 物件,該物件反映此 Class 物件所表示的類或介面的指定構造方法 |
public Constructor<?>[] getDeclaredConstructors() throws SecurityException | 【返回此 Class 物件表示的類宣告的所有構造方法】 |
public native int getModifiers(); |
【 返回此類或介面以整數編碼的 Java 語言修飾符,用二進位制的某一位1,來代表一種修飾符】 |
public String getName() |
【以 String 的形式返回此 Class 物件所表示的實體(類、介面、陣列類、基本型別或 void)名稱】 |
public native Class<? super T> getSuperclass(); |
【返回表示此 Class 所表示的實體(類、介面、基本型別或 void)的超類的 Class】 |
public Type getGenericSuperclass() |
【返回此 Class 所表示的實體(類、介面、基本型別或 void) 的直接超類的 Type】 |
public Class<?>[] getInterfaces() | 【確定此物件所表示的類或介面實現的介面】 |
public Field getField(String name) | 【返回此 Class 物件所表示的類或介面的指定公共成員欄位】 |
public Field[] getFields() throws SecurityException | 【返回此 Class 物件所表示的類或介面的所有可訪問公共欄位】 |
public Field getDeclaredField(String name) | 【返回此 Class 物件所表示的類或介面的指定已宣告欄位】 |
public Field[] getDeclaredFields() throws SecurityException | 【返回此 Class 物件所表示的類或介面所宣告的所有欄位】 |
public Method getMethod(String name, Class<?>... parameterTypes) | 【返回此 Class 物件所表示的類或介面的指定公共成員方法】 |
public Method[] getMethods() throws SecurityException |
【返回此 Class 物件所表示的類或介面(包括那些由該類或介面 宣告的以及從超類和超介面繼承的那些的類或介面)的公共 member 方法】 |
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 【返回此 Class 物件所表示的類或介面的指定已宣告方法】 |
public Method[] getDeclaredMethods() throws SecurityException |
【此 Class 物件表示的類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法】 |
public T newInstance() | 【建立此 Class 物件所表示的類的一個新例項】 |
public Annotation[] getAnnotations() | 【返回此元素上存在的所有註釋】 |
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) | 【如果存在該元素的指定型別的註釋,則返回這些註釋,否則返回 null】 |
public T[] getEnumConstants() | 【如果此 Class 物件不表示列舉型別,則返回列舉類的元素或 null】 |
public boolean isMemberClass() | 【當且僅當底層類是成員類時返回 true】 |
public native boolean isInterface(); | 【判定指定的 Class 物件是否表示一個介面型別】 |
建立任意引用型別的物件
1、直接通過Class物件來例項化(要求必須有無參構造)
- (1)獲取該型別的Class物件(2)建立物件 newInstance()
2、通過獲取構造器物件來進行例項化
- (1)獲取該型別的Class物件(2)獲取構造器物件(3)建立物件 newInstance(Object... initargs)
如果構造器的許可權修飾符修飾的範圍不可見,也可以呼叫setAccessible(true)
操作任意型別的屬性
(1)獲取該型別的Class物件
Class clazz = Class.forName("com.guigu.bean.User");
(2)獲取屬性物件
Field field = clazz.getDeclaredField("username");
(3)設定屬性可訪問
field.setAccessible(true);
(4)建立例項物件:如果操作的是非靜態屬性,需要建立例項物件
Object obj = clazz.newInstance();
(4)設定屬性值
field.set(obj,"chai");
(5)獲取屬性值 Object value = field.get(obj);
如果操作靜態屬性變數,那麼例項物件可以省略,用null表示
呼叫任意型別的方法
(1)獲取該型別的Class物件
Class clazz = Class.forName("com.atguigu.service.UserService");
(2)獲取方法物件
Method method = clazz.getDeclaredMethod("login",String.class,String.class);
(3)建立例項物件
Object obj = clazz.newInstance();
(4)呼叫方法
Object result = method.invoke(obj,"chai","123);
如果方法的許可權修飾符修飾的範圍不可見,也可以呼叫setAccessible(true)
如果方法是靜態方法,例項物件也可以省略,用null代替
獲取泛型父類
1、獲取子類的Class物件
2、呼叫getGenericSuperClass()獲取泛型父類
3、強轉為ParameterizedType型別
4、呼叫getActualTypeArguments()獲取實際型別引數
讀取註解資訊
獲取類上的註解:
1、獲取Class物件
2、呼叫getAnnotation()方法得到註解物件
3、呼叫註解物件的配置引數的方法獲取配置引數值
獲取屬性上的註解:
1、獲取Class物件
2、獲取某個屬性Field物件
3、呼叫getAnnot ation()方法得到註解物件
4、呼叫註解物件的配置引數的方法獲取配置引數值
獲取方法上的註解:
1、獲取Class物件
2、獲取某個方法method物件
3、呼叫getAnnot ation()方法得到註解物件
4、呼叫註解物件的配置引數的方法獲取配置引數值
獲取內部類或外部類資訊
public Class<?>[] getClasses():
【返回所有公共內部類和內部介面。包括從超類繼承的公共類和介面成員以及該類宣告的公共類和介面成員。】
public Class<?>[] getDeclaredClasses():
【返回 Class 物件的一個數組,這些物件反映宣告為此 Class 物件所表示的類的成員的所有類和介面。包括該類所宣告的公共、保護、預設(包)訪問及私有類和介面,但不包括繼承的類和介面】
public Class<?> getDeclaringClass():
【如果此 Class 物件所表示的類或介面是一個內部類或內部介面,則返回它的外部類或外部介面,否則返回null。】
動態建立和操作任意型別的陣列
在java.lang.reflect包下還提供了一個Array類,Array物件可以代表所有的陣列。程式可以通過使用Array類來動態的建立陣列,運算元組元素等。
Array類提供瞭如下幾個方法:
public static Object newInstance(Class<?> componentType, int... dimensions):
【建立一個具有指定的元件型別和維度的新陣列。】
public static void setXxx(Object array,int index,xxx value):
【將array陣列中[index]元素的值修改為value。此處的Xxx對應8種基本資料型別,如果該屬性的型別是引用資料型別,則直接使用set(Object array,int index, Object value)方法。】
public static xxx getXxx(Object array,int index,xxx value):
【將array陣列中[index]元素的值返回。此處的Xxx對應8種基本資料型別,如果該屬性的型別是引用資料型別,則直接使用get(Object array,int index)方法。】
Field
String getName() | 返回此 Field 物件表示的欄位的名稱 |
Object get(Object obj) | 返回指定物件上此 Field 表示的欄位的值 |
int getModifiers() | 以整數形式返回由此 Field 物件表示的欄位的 Java 語言修飾符 |
boolean equals(Object obj) | 將此 Field 與指定物件比較 |
Type getGenericType() | 返回一個 Type 物件,它表示此 Field 物件所表示欄位的宣告型別 |
boolean getBoolean(Object obj) | 獲取一個靜態或例項 boolean 欄位的值 |
int getInt(Object obj) | 獲取 int 型別或另一個通過擴充套件轉換可以轉換為 int 型別的基本型別的靜態或例項欄位的值 |
void set(Object obj, Object value) | 將指定物件變數上此 Field 物件表示的欄位設定為指定的新值 |
void setShort(Object obj, short s) | 將欄位的值設定為指定物件上的一個 short 值 |
void setInt(Object obj, int i) | 將欄位的值設定為指定物件上的一個 int 值 |
Method
boolean equals(Object obj) | 將此 Method 與指定物件進行比較 |
String getName() | 以 String 形式返回此 Method 物件表示的方法名稱 |
Object invoke(Object obj, Object... args) | 對帶有指定引數的指定物件呼叫由此 Method 物件表示的底層方法 |
Class<?> getReturnType() | 返回一個 Class 物件,該物件描述了此 Method 物件所表示的方法的正式返回型別 |
Class<?>[] getParameterTypes() | 按照宣告順序返回 Class 物件的陣列,這些物件描述了此 Method 物件所表示的方法的形參型別 |
int getModifiers() | 以整數形式返回此 Method 物件所表示方法的 Java 語言修飾符 |
Type getGenericReturnType() | 返回表示由此 Method 物件所表示方法的正式返回型別的 Type 物件 |
Type[] getGenericParameterTypes() | 返回一個 Type物件的陣列, Type以宣告順序表示由該物件表示的可執行檔案的形式引數型別 |
Type[] getGenericExceptionTypes() | 返回一個 Type物件的陣列, Type此可執行物件宣告丟擲的異常 |
Class<?>[] getExceptionTypes() | 返回 Class 物件的陣列,這些物件描述了宣告將此 Method 物件表示的底層方法丟擲的異常型別 |
Class<?> getDeclaringClass() | 返回表示宣告由此 Method 物件表示的方法的類或介面的 Class 物件 |
Annotation[] getDeclaredAnnotations() | 返回直接存在於此元素上的所有註釋 |
Constructor
boolean equals(Object obj) | 將此 Constructor 物件與指定的物件進行比較 |
String getName() | 以字串形式返回此構造方法的名稱 |
Class<T> getDeclaringClass() | 返回 Class 物件,該物件表示宣告由此 Constructor 物件表示的構造方法的類 |
int getModifiers() | 以整數形式返回此 Constructor 物件所表示構造方法的 Java 語言修飾符 |
T newInstance(Object... initargs) | 使用此 Constructor 物件表示的構造方法來建立該構造方法的宣告類的新例項,並用指定的初始化引數初始化該例項 |
Class<?>[] getParameterTypes() | 按照宣告順序返回一組 Class 物件,這些物件表示此 Constructor 物件所表示構造方法的形參型別 |
Type[] getGenericParameterTypes() | 按照宣告順序返回一組 Type 物件,這些物件表示此 Constructor 物件所表示的方法的形參型別 |