版權聲明:轉載前請留言獲得作者許可,轉載后標明作者 張拭心 與 原文鏈接。大家都是成年人,創作不易,感謝您的支持!
什麼是 Reflection 反射,為什麼要用它
Java 強類型語言,但是我們在運行時有了解、修改信息的需求,包括類信息、成員信息以及數組信息。
Java 中 Reflection 和 Introspection 區別?
說起反射,還有一個相似的概念 『Introspection』,字面意思是「自省、內省」,它們之間的區別如下:
- 內省
- 在運行時檢查一個對象的類型或者屬性
- 最常見的例子就是運行時通過 a instanceof A 來判斷 a 對象的類型
- 反射
- 用來在運行時檢查或者修改一個對象信息
- 可以用來實現看似不可能的操作,比如訪問私有方法,動態創建對象
可以看到,反射是在內省的基礎上,增加了修改的能力。
反射的入口:
java.lang.Class
日常開發中的對象,分為兩種,基本類型和引用類型:
- 基本類型,(固定的 8 種)
- 整數:byte, short, int, long
- 小數:float, double
- 字元:char
- 布爾值:boolean
- 引用類型
- 所有的引用類型都繼承自 java.lang.Object
- 類,枚舉,數組,介面都是引用類型
- java.io.Serializable 介面,基本類型的包裝類(比如 java.lang.Double)也是引用類型
對每一種對象,JVM 都會實例化一個 java.lang.Class 的實例,java.lang.Class 為我們提供了在運行時訪問對象的屬性和類型信息的能力。Class 還提供了創建新的類和對象的能力。最重要的是,Class 是調用其他反射 API 的入口,我們必須先獲得一個 Class 實例才可以進行接下來的操作。
得到一個 Class 對象
除了 java.lang.reflect.ReflectPermission 以外,java.lang.reflect 中的其他類都沒有 public 的構造函數,也就是說要得到這些類,我們必須通過 Class 。
下面是幾種得到 Class 對象的不同方法:
1.Object.getClass 方法
如果我們已經拿到了一個對象,可以很方便地使用它的 getClass 方法獲得一個 Class 對象(當然這僅限於引用類型的對象):
Class c = "shixinzhang.top".getClass;
返回的對象 c 是 String 類型。
enum Sex{ FEMALE, MALE } Class c = FEMALE.getClass;
上述例子中 FEMALE 是 枚舉 Sex 的實例,因此 FEMALE.getClass 返回的就是 枚舉類型 Sex 的 Class。
byte bytes = newbyte[1024]; Classbyte>c = bytes.getClass;
由於數組也是 Object 的一種,因此我們可以調用 getClass 方法獲得 byte 數組類型的 Class。
2. .class 語法
如果我們當前沒有某個類的對象,無法使用 getClass 方法,那還可以使用另外一種方法獲取 Class:在要獲得的類名后加上 .class ,比如這樣:
Integer.class.newInstance; int.class.newInstance
可以看到,這種方式不僅能用於引用類型,基本類型也可以。
當然數組也可以嘍:
Class b = int.class;
3.Class.forName
如果我們有一個類的完整路徑,就可以使用 Class.forName(「類完整的路徑」) 來得到相應的 Class,這個方法只能用於引用類型,比如:
Class c = Class.forName("java.lang.String"); Class aClass = Class.forName("top.shixinzhang.androiddemo2.beans.BookBean");
4.靜態屬性 TYPE
上面介紹,使用 .class 後綴可以很方便地獲得基本類型的 Class。
對於基本類型和 void 的包裝類,還有另外一種方式獲得 Class,那就是靜態屬性 TYPE 。
每個包裝類都有 TYPE 屬性,以 Double 為例:
TYPE = (Class)
double.class.getComponentType;
可以看到這個屬性就是使用 .class 的方式獲得 Class 並保存。
因此我們可以直接調用包裝類的 TYPE:
ClassintegerWrapper = Integer.TYPE; ClassdoubleWrapper = Double.TYPE; ClassvoidWrapper = Void.TYPE;
5.返回 Class 的方法
如果我們已經有了一個 Class,可以使用下面的一些方法來獲得它相關的類:
- Class.getSuperclass
- 返回調用類的父類
- Class.getClasses
- 返回調用類的所有公共類、介面、枚舉組成的 Class 數組,包括繼承的
- Class.getDeclaredClasses
- 返回調用類顯式聲明的所有類、介面、枚舉組成的 Class 數組
- Class.getDeclaringClass
- java.lang.reflect.Field.getDeclaringClass
- java.lang.reflect.Method.getDeclaringClass
- java.lang.reflect.Constructor.getDeclaringClass
- 返回類/屬性/方法/構造器所在的類
Class 的修飾符:Modifier
一個 Class 可以被以下修飾符的一種或者多種修飾:
- 訪問許可權控制符:
public
,protected
,private
- 抽象的、需要實現的:
abstract
- 限制只能有一個實例的:
static
- 不允許修改的:
final
- 線程同步鎖:
synchronized
- 原生函數:
native
- 採用嚴格的浮點精度:
strictfp
- 介面
- 註解
當然上面的修飾符不是所有 Class 都可以修飾,比如:
Interface
不能是final
的enum
不能是abstract
的
java.lang.reflect.Modifier
提供了對 Class 修飾符的解碼,我們可以使用
Class.getModifiers
獲得調用類的修飾符的二進位值,然後使用
Modifier.toString(int modifiers)
將二進位值轉換為字元串,
Modifier.toString
方法實現如下:
publicstatic java.lang.String toString(int modifiers) { StringBuilder buf = new StringBuilder; if (isPublic(modifiers)) { buf.append("public "); } if (isProtected(modifiers)) { buf.append("protected "); } if (isPrivate(modifiers)) { buf.append("private "); } if (isAbstract(modifiers)) { buf.append("abstract "); } if (isStatic(modifiers)) { buf.append("static "); } if (isFinal(modifiers)) { buf.append("final "); } if (isTransient(modifiers)) { buf.append("transient "); } if (isVolatile(modifiers)) { buf.append("volatile "); } if (isSynchronized(modifiers)) { buf.append("synchronized "); } if (isNative(modifiers)) { buf.append("native "); } if (isStrict(modifiers)) { buf.append("strictfp "); } if (isInterface(modifiers)) { buf.append("interface "); } if (buf.length == 0) { return""; } buf.setLength(buf.length - 1); return buf.toString; }
注意:
Interface
默認是abstract
的,雖然我們沒有添加,編譯器會在編譯器為每個 Interface 添加這個修飾符。- 只有被
@Retention(RetentionPolicy.RUNTIME)
修飾的註解才可以在運行時被發射獲取 - Java 中預定義的註解
@Deprecated
@Override
, 和@SuppressWarnings
中只有@Deprecated
可以在運行時被訪問到
java.lang.reflect.Member
是一個介面,代表 Class 的成員,每個成員都有類型,分為是否從父類繼承,還有是否可以直接訪問。
Member 有三個實現類:
java.lang.reflect.Constructor
:表示該 Class 的構造函數java.lang.reflect.Field
:表示該 Class 的成員變數java.lang.reflect.Method
:表示該 Class 的成員方法
獲取構造函數
java.lang.Class
提供了以下方法用於獲取該類的構造函數:
注意:構造函數無法從父類繼承
java.lang.Class
提供了以下方法用於獲取該類的成員變數:
獲取成員方法
提供了以下方法用於獲取該類的成員方法: