search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

深入理解 Java 反射:Class

Java(47)

版權聲明:轉載前請留言獲得作者許可,轉載后標明作者 張拭心 與 原文鏈接。大家都是成年人,創作不易,感謝您的支持!

什麼是 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

提供了以下方法用於獲取該類的成員變數:

獲取成員方法

提供了以下方法用於獲取該類的成員方法:



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦