理解Java反射機制
轉載請注明出處,謝謝!
一、Java反射簡介
-
什么是反射?
Java的反射機制是Java特性之一,反射機制是構建框架技術的基礎所在。靈活掌握Java反射機制,對學習框架技術有很大的幫助。
首先,我們先區分下編譯和運行:編譯時刻加載類是靜態加載類、運行時刻加載類是動態加載類
大家都知道,要讓Java程序能夠運行,那么就得讓Java類要被Java虛擬機加載。Java類如果不被Java虛擬機加載,是不能正常運行的。現在我們運行的所有的程序都是在編譯期的時候就已經知道了你所需要的那個類的已經被加載了。
Java的反射機制是在編譯並不確定是哪個類被加載了,而是在程序運行的時候才加載、探知、自審。使用在編譯期並不知道的類。
Java反射(放射)機制:“程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言”。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。但是Java有着一個非常突出的動態相關機制:Reflection,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
-
Java反射有什么作用?
假如我們有兩個程序員,一個程序員在寫程序的時候,需要使用第二個程序員所寫的類,但第二個程序員並沒完成他所寫的類。那么第一個程序員的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程序員在沒有得到第二個程序員所寫的類的時候,來完成自身代碼的編譯。
Java的反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱為Java類的“自審”。大家都用過Jcreator和eclipse。當我們構建出一個對象的時候,去調用該對象的方法和屬性的時候。一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供用戶進行選擇。這就是利用了Java反射的原理,是對我們創建對象的探知、自審。
-
得到反射的三種方法?
類.class;
對象.getClass();
Class.forName("類的全稱"),不僅表示了,類的類類型,還代表了動態加載類
-
一個類里都有什么?
類名、類修飾符、包信息、父類、實現的接口、屬性、方法、構造器(構造方法)、注解多部分組成。
-
屬性有幾部分組成?
修飾符、類型、屬性名、屬性值四部分組成。
-
方法有幾部分組成?
修飾符、返回類型、方法名、參數列表、方法體、返回值
-
構造器幾部分組成?
修飾符、構造器名稱(類名)、參數列表、方法體
二、Class類簡介
-
java.lang.Class類介紹
要正確使用Java反射機制就得使用java.lang.Class這個類。它是Java反射機制的起源。當一個類被加載以后,Java虛擬機就會自動產生一個Class對象。通過這個Class對象我們就能獲得加載到虛擬機當中這個Class對象對應的方法、成員以及構造方法的聲明和定義等信息。
Class類的實例表示正在運行的 Java 應用程序中的類和接口。枚舉是一種類,注釋是一種接口。每個數組屬於被映射為 Class對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。基本的 Java 類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字void也表示為Class對象。
Class沒有公共構造方法。Class對象是在加載類時由Java虛擬機以及通過調用類加載器中的defineClass方法自動構造的。
在面向對象的世界里,萬事萬物皆對象。(java語言中,靜態的成員、普通數據類型除外)
那么類是不是對象呢?類是(哪個類的對象呢?)誰的對象呢?
類是對象,類是java.lang.Class類的實例對象
-
java.lang.Class類的API介紹
| 方法摘要 | ||
|---|---|---|
|
asSubclass(Class<U> clazz) 強制轉換該 Class 對象,以表示指定的 class 對象所表示的類的一個子類。 |
|
T |
cast(Object obj) 將一個對象強制轉換成此 Class 對象所表示的類或接口。 |
|
boolean |
desiredAssertionStatus() 如果要在調用此方法時將要初始化該類,則返回將分配給該類的斷言狀態。 |
|
static Class<?> |
forName(String className) 返回與帶有給定字符串名的類或接口相關聯的 Class 對象。 |
|
static Class<?> |
forName(String name, boolean initialize, ClassLoader loader) 使用給定的類加載器,返回與帶有給定字符串名的類或接口相關聯的 Class 對象。 |
|
|
getAnnotation(Class<A> annotationClass) 如果存在該元素的指定類型的注釋,則返回這些注釋,否則返回 null。 |
|
Annotation[] |
getAnnotations() 返回此元素上存在的所有注釋。 |
|
String |
getCanonicalName() 返回 Java Language Specification 中所定義的底層類的規范化名稱。 |
|
Class<?>[] |
getClasses() 返回一個包含某些 Class 對象的數組,這些對象表示屬於此 Class 對象所表示的類的成員的所有公共類和接口。 |
|
ClassLoader |
getClassLoader() 返回該類的類加載器。 |
|
Class<?> |
getComponentType() 返回表示數組組件類型的 Class。 |
|
Constructor<T> |
getConstructor(Class<?>... parameterTypes) 返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法。 |
|
Constructor<?>[] |
getConstructors() 返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共構造方法。 |
|
Annotation[] |
getDeclaredAnnotations() 返回直接存在於此元素上的所有注釋。 |
|
Class<?>[] |
getDeclaredClasses() 返回 Class 對象的一個數組,這些對象反映聲明為此 Class 對象所表示的類的成員的所有類和接口。 |
|
Constructor<T> |
getDeclaredConstructor(Class<?>... parameterTypes) 返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或接口的指定構造方法。 |
|
Constructor<?>[] |
getDeclaredConstructors() 返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。 |
|
Field |
getDeclaredField(String name) 返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。 |
|
Field[] |
getDeclaredFields() 返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或接口所聲明的所有字段。 |
|
Method |
getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。 |
|
Method[] |
getDeclaredMethods() 返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。 |
|
Class<?> |
getDeclaringClass() 如果此 Class 對象所表示的類或接口是另一個類的成員,則返回的 Class 對象表示該對象的聲明類。 |
|
Class<?> |
getEnclosingClass() 返回底層類的立即封閉類。 |
|
Constructor<?> |
getEnclosingConstructor() 如果該 Class 對象表示構造方法中的一個本地或匿名類,則返回 Constructor 對象,它表示底層類的立即封閉構造方法。 |
|
Method |
getEnclosingMethod() 如果此 Class 對象表示某一方法中的一個本地或匿名類,則返回 Method 對象,它表示底層類的立即封閉方法。 |
|
T[] |
getEnumConstants() 如果此 Class 對象不表示枚舉類型,則返回枚舉類的元素或 null。 |
|
Field |
getField(String name) 返回一個 Field 對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段。 |
|
Field[] |
getFields() 返回一個包含某些 Field 對象的數組,這些對象反映此 Class 對象所表示的類或接口的所有可訪問公共字段。 |
|
Type[] |
getGenericInterfaces() 返回表示某些接口的 Type,這些接口由此對象所表示的類或接口直接實現。 |
|
Type |
getGenericSuperclass() 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的直接超類的 Type。 |
|
Class<?>[] |
getInterfaces() 確定此對象所表示的類或接口實現的接口。 |
|
Method |
getMethod(String name, Class<?>... parameterTypes) 返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。 |
|
Method[] |
getMethods() 返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。 |
|
int |
getModifiers() 返回此類或接口以整數編碼的 Java 語言修飾符。 |
|
String |
getName() 以 String 的形式返回此 Class 對象所表示的實體(類、接口、數組類、基本類型或 void)名稱。 |
|
Package |
getPackage() 獲取此類的包。 |
|
ProtectionDomain |
getProtectionDomain() 返回該類的 ProtectionDomain。 |
|
URL |
getResource(String name) 查找帶有給定名稱的資源。 |
|
InputStream |
getResourceAsStream(String name) 查找具有給定名稱的資源。 |
|
Object[] |
getSigners() 獲取此類的標記。 |
|
String |
getSimpleName() 返回源代碼中給出的底層類的簡稱。 |
|
Class<? super T> |
getSuperclass() 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的超類的 Class。 |
|
TypeVariable<Class<T>>[] |
getTypeParameters() 按聲明順序返回 TypeVariable 對象的一個數組,這些對象表示用此 GenericDeclaration 對象所表示的常規聲明來聲明的類型變量。 |
|
boolean |
isAnnotation() 如果此 Class 對象表示一個注釋類型則返回 true。 |
|
boolean |
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定類型的注釋存在於此元素上,則返回 true,否則返回 false。 |
|
boolean |
isAnonymousClass() 當且僅當底層類是匿名類時返回 true。 |
|
boolean |
isArray() 判定此 Class 對象是否表示一個數組類。 |
|
boolean |
isAssignableFrom(Class<?> cls) 判定此 Class 對象所表示的類或接口與指定的 Class 參數所表示的類或接口是否相同,或是否是其超類或超接口。 |
|
boolean |
isEnum() 當且僅當該類聲明為源代碼中的枚舉時返回 true。 |
|
boolean |
isInstance(Object obj) 判定指定的 Object 是否與此 Class 所表示的對象賦值兼容。 |
|
boolean |
isInterface() 判定指定的 Class 對象是否表示一個接口類型。 |
|
boolean |
isLocalClass() 當且僅當底層類是本地類時返回 true。 |
|
boolean |
isMemberClass() 當且僅當底層類是成員類時返回 true。 |
|
boolean |
isPrimitive() 判定指定的 Class 對象是否表示一個基本類型。 |
|
boolean |
isSynthetic() 如果此類是復合類,則返回 true,否則 false。 |
|
T |
newInstance() 創建此 Class 對象所表示的類的一個新實例。 |
|
String |
toString() 將對象轉換為字符串。 |
|
三、示例分析
-
Class類的使用
1 package me.reflect; 2
3 /**
4 * Class類的使用 5 * @author Administrator 6 * 7 */
8 public class ClassDemo1 { 9 public static void main(String[] args) { 10 // Foo的實例對象如何表示
11 Foo foo1 = new Foo();// foo1就表示出來了. 12 // Foo這個類 也是一個實例對象,Class類的實例對象,如何表示呢 13 // 任何一個類都是Class的實例對象,這個實例對象有三種表示方式 14
15 // 第一種表示方式--->實際在告訴我們任何一個類都有一個隱含的靜態成員變量class
16 Class c1 = Foo.class; 17
18 // 第二中表達方式 已經知道該類的對象通過getClass方法
19 Class c2 = foo1.getClass(); 20
21 /*
22 * 官網 c1 ,c2 表示了Foo類的類類型(class type) 萬事萬物皆對象, 類也是對象,是Class類的實例對象 23 * 這個對象我們稱為該類的類類型 24 * 25 */
26 // 不管c1 or c2都代表了Foo類的類類型,一個類只可能是Class類的一個實例對象
27 System.out.println(c1 == c2); 28
29 // 第三種表達方式
30 Class c3 = null; 31 try { 32 c3 = Class.forName("com.imooc.reflect.Foo"); 33 } catch (ClassNotFoundException e) { 34 // TODO Auto-generated catch block
35 e.printStackTrace(); 36 } 37 System.out.println(c2 == c3); 38
39 // 我們完全可以通過類的類類型創建該類的對象實例---->通過c1 or c2 or c3創建Foo的實例對象
40 try { 41 Foo foo = (Foo) c1.newInstance();// 需要有無參數的構造方法
42 foo.print(); 43 } catch (InstantiationException e) { 44 // TODO Auto-generated catch block
45 e.printStackTrace(); 46 } catch (IllegalAccessException e) { 47 // TODO Auto-generated catch block
48 e.printStackTrace(); 49 } 50 } 51 } 52
53 class Foo { 54
55 void print() { 56 System.out.println("foo"); 57 } 58 }
-
方法的反射
1 public static void printClassMethodMessage(Object obj) { 2 // 要獲取類的信息 首先要獲取類的類類型
3 Class c = obj.getClass();// 傳遞的是哪個子類的對象 c就是該子類的類類型 4 // 獲取類的名稱
5 System.out.println("類的名稱是:" + c.getName()); 6 /**
7 * Method類,方法對象 一個成員方法就是一個Method對象 8 * getMethods()方法獲取的是所有的public的函數,包括父類繼承而來的 9 * getDeclaredMethods()獲取的是所有該類自己聲明的方法,不問訪問權限 10 */
11 Method[] ms = c.getMethods();// c.getDeclaredMethods()
12 for (int i = 0; i < ms.length; i++) { 13 // 得到方法的返回值類型的類類型
14 Class returnType = ms[i].getReturnType(); 15 System.out.print(returnType.getName() + " "); 16 // 得到方法的名稱
17 System.out.print(ms[i].getName() + "("); 18 // 獲取參數類型--->得到的是參數列表的類型的類類型
19 Class[] paramTypes = ms[i].getParameterTypes(); 20 for (Class class1 : paramTypes) { 21 System.out.print(class1.getName() + ","); 22 } 23 System.out.println(")"); 24 } 25 }
-
成員變量的反射
1 public static void printFieldMessage(Object obj) { 2 Class c = obj.getClass(); 3 /**
4 * 成員變量也是對象 java.lang.reflect.Field Field類封裝了關於成員變量的操作 5 * getFields()方法獲取的是所有的public的成員變量的信息 6 * getDeclaredFields獲取的是該類自己聲明的成員變量的信息 7 */
8 // Field[] fs = c.getFields();
9 Field[] fs = c.getDeclaredFields(); 10 for (Field field : fs) { 11 // 得到成員變量的類型的類類型
12 Class fieldType = field.getType(); 13 String typeName = fieldType.getName(); 14 // 得到成員變量的名稱
15 String fieldName = field.getName(); 16 System.out.println(typeName + " " + fieldName); 17 } 18 }
-
構造函數的反射
1 public static void printConMessage(Object obj) { 2 Class c = obj.getClass(); 3 /*
4 * 構造函數也是對象 java.lang.Constructor中封裝了構造函數的信息 5 * getConstructors獲取所有的public的構造函數 getDeclaredConstructors得到所有的構造函數 6 */
7 // Constructor[] cs = c.getConstructors();
8 Constructor[] cs = c.getDeclaredConstructors(); 9 for (Constructor constructor : cs) { 10 System.out.print(constructor.getName() + "("); 11 // 獲取構造函數的參數列表--->得到的是參數列表的類類型
12 Class[] paramTypes = constructor.getParameterTypes(); 13 for (Class class1 : paramTypes) { 14 System.out.print(class1.getName() + ","); 15 } 16 System.out.println(")"); 17 } 18 }
-
Java類加載機制
1 package me.reflect.dynamic; 2
3 public interface OfficeAble { 4
5 public void start(); 6 } 7
8
9 package me.reflect.dynamic; 10
11 public class Word implements OfficeAble { 12
13 public void start() { 14 System.out.print("word start....."); 15 } 16 } 17
18 package me.reflect.dynamic; 19
20 public class Excel implements OfficeAble{ 21
22 @Override 23 public void start() { 24 System.out.print("Excel start...."); 25 } 26
27 } 28
29 package me.reflect.dynamic; 30
31 /**
32 * Java動態加載類測試類 33 * @author Administrator 34 * 35 */
36 public class Office { 37
38 public static void main(String[] args) { 39 //new創建對象是靜態加載類,在編譯的時刻就需要加載所有可能使用到的類 40 //通過動態加載類可以解決該問題
41 if("Word".equals(args[0])) { 42 Word w = new Word(); 43 w.start(); 44 } 45 if("Excel".equals(args[0])) { 46 Excel e = new Excel(); 47 e.start(); 48 } 49 } 50 } 51
52 package me.reflect.dynamic; 53
54 public class OfficeBetter { 55
56 public static void main(String[] args) { 57 try { 58 //動態加載類,在運行時刻加載類
59 Class c = Class.forName(args[0]); 60 //通過類類型創建該類對象
61 OfficeAble oa = (OfficeAble) c.newInstance(); 62 oa.start(); 63 } catch (ClassNotFoundException e) { 64 e.printStackTrace(); 65 } catch (InstantiationException e) { 66 e.printStackTrace(); 67 } catch (IllegalAccessException e) { 68 e.printStackTrace(); 69 } 70 } 71 }
參考文章:

