反射(Reflection)

Java學習筆記——反射(Reflection)

關於反射

可以分析類能力的程序稱之爲反射(Reflection)java

反射機制能夠用來:數組

  1. 在運行時分析類的能力
  2. 在運行時檢查對象,例如:編寫一個適合全部類的toString()
  3. 實現泛型數組操做代碼
  4. 利用Method對象(相似C++中的函數指針)
  5. 用戶界面生成器

Class類

在程序運行時,JRE始終爲全部對象維護一個運行時類型標識(我的猜想其實是一個結構體)。這個類型標識會跟蹤記錄每個對象所屬的類。虛擬機利用這些信息來保證調用正確的方法安全

Class類保存了類型標識Class類是泛型類,你懂我什麼意思的吧)內部的信息,所以能夠使用這個特殊的類來訪問這些信息。dom

那麼何如獲取這些信息呢?

Object類中有一個getClass(),能夠返回一個Class類型實例函數

class cl = e.getclass();

class類中有個方法getName(),能夠獲取對象所屬的類名(字符串形式),固然若是這個類在一個包中,則返回的類名也會包含包名。學習

var ram = new Random();
Class cl = ram.getClass();
String name = cl.getName()  // name is "java.util.Random"

固然java還有一種方便的方法來獲取每個類的Class類對象。若是T是任意的Java類型,則T.class表明該類的Class類對象指針

Class cl = Random.class;
Class c2 = int[].class;
Class c3 = int.class;

Class對象實際上表示的是一種類型(type),這種類型能夠是類,亦能夠不是類,所以int.class、int[].class是合法的。code

若是我想實現動態加載加載類,或者我如今知道這個類的類名(或者接口名),則還能夠使用Class類自己的靜態方法來實現類加載orm

String classname = "java.util.Random";
try{
	Class cl = Class.forName(classname);
}catch(Exception e){
	e.printStackTrace();
}

classname不是一個接口或者類,則會拋出檢查型異常。所以捕獲異常。對象

使用這種方法得到Class對象,classname對應的類會被加載,也就是說類裏面的靜態代碼塊(Static Code)會被執行。同時能夠變換classname的值來實現動態加載類。

更加準確的類型比較

JVM爲每一種類型管理一個惟一的Class類對象,也就是說父類和子類被區分爲不一樣的Class類型,所以能夠利用==來進行同類型對象比較

father.getClass() == son.getClass();	// 表達式爲False,即使father是son的父類

使用Class類對象構造類實例

前文說過,Class類實際上表示的是一種類型,既然如此我能不能用一個Class類來構造一個類實例呢?答案確定是能的

使用getConstructor()newInstance()

Class cl = Class.forName(classname);
Object obj = Cl.getConstructor().newInstance();

那若是我想要調用有參數的構造器來建立對象呢?

先看看getConstructor()newInstance()方法的聲明:

Constructor getConstructor(Class ...paramterTypes)		// 生成一個構造器對象,並描述這個構造器有什麼參數類型
Object newInstance(Object ...params)					// 生成對象實例,params參數爲構造器傳進的參數

所以,能夠

Class cl = Class.forName(classname);
Object obj = Cl.getConstructor(int.class).newInstance(25);		// 調用classname類中帶int類型的構造器,並傳入參數整型25

資源

Class類一般也用在讀入資源上,例如顯示一張圖片等

加載資源的方法

若是資源文件和類文件放在同一個包中,則能夠

  • 獲取資源類的Class對象
  • 有些方法須要獲取資源位置的URL則須要調用getResource()
  • 若是不想獲取URL而是直接將文件的全部字節存放在輸入流中的則須要調用getResourceAsStream()
Class cl = ResourceTest.class;
URL aboutURL = c1.getResource("about.png");
Image icon = new Image(aboutURL);

InputStream stream = cl.getResourceAsStream("../Date/about.txt");		// 支持相對和絕對路徑,若是沒找到資源則返回null
var about = new String(stream.readAllBytes(), "UTF-8");

反射應用

利用反射分析類

反射機制中經常使用來作類分析的重要類:FieldMethodConstructor。這些類都在java.lang.reflect包中

接下來對這幾個類用來分析的方法進行簡單介紹:

Class類

String	  getName()						// 返回該類型的類名字
String    getPackageName()				// 返回該類所在的包名
Field[]   getFields()					// 返回對象的全部公共字段,包括超類的公共字段
Field[]   getDeclaredFields()			// 返回對象的所有字段,若是類中沒有字段,或者對象是基本類型或者數組,則返回0長度數組
Class	  getSuperClass()				// 獲取該類的父類Class對象
Method[]  getMethods()					// 返回對象所屬類或者接口的全部公共方法,包括超類的公共方法
Method[]  getDeclaredMethods()			// 返回對象所屬類或者接口的所有方法,不包括超類
Constructor[] getConstructors()			// 返回這個類的全部公共構造器
Constructor[] getDeclaredConstructors()	// 返回所有構造器

Field類

String	getName()		// 返回類中的字段名(屬性名)的字符串
Class	getType()		// 返回字段的類型(int、long、Date...)
int		getModifiers()	// 獲取字段的修飾符(public、static、final...),返回1/0的二進制標誌位,能夠配合reflect包中的toString()來顯示具體的修飾符
Class	getDeclaringClass()	//獲取字段所屬的類對應的Class對象

Method類

String	getName()		// 返回類中的方法名的字符串
Class	getReturnType()		// 返回方法的返回值類型對應的Class對象(int、long、Date...)
int		getModifiers()	// 獲取方法的修飾符(public、static、final...),返回1/0的二進制標誌位,能夠配合reflect包中的toString()來顯示具體的修飾符
Class	getDeclaringClass()	//獲取方法所屬的類對應的Class對象
Class[] getParameterTypes()	// 返回Class對象的數組,其中各個對象表示參數的類型
Class[] getExceptionTypes() // 返回Class對象數組,其中各個對象表示該方法所拋出的異常的類型

Constructor類

String	getName()		// 返回類中的構造方法的字符串
int		getModifiers()	// 獲取構造方法的修飾符(public、static、final...),返回1/0的二進制標誌位,能夠配合reflect包中的toString()來顯示具體的修飾符
Class	getDeclaringClass()	//獲取構造方法所屬的類對應的Class對象
Class[] getParameterTypes()	// 返回Class對象的數組,其中各個對象表示參數的類型
Class[] getExceptionTypes() // 返回Class對象數組,其中各個對象表示該方法所拋出的異常的類型

Modifier類

static String toString(int modifiers)
static boolean isAbstract(int modifiers)
static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isProtected(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchronized(int modifiers)
static boolean isVolatile(int modifiers)

下面將演示一個經過反射來分析一個類的demo:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.PrivateKey;

public class Test {
    public static void main(String[] args) {
        new Test("java.lang.Double");
    }
    public Test(String classname){
        try {
            Class c = Class.forName(classname);
            printClass(c);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    public void printClass(Class c){
        Class sc = c.getSuperclass();						// 獲取父類
        String modifier = Modifier.toString(c.getModifiers());// 獲取類修飾符
        if(modifier.length()>0)
            System.out.print(modifier + " ");     			
        System.out.print("class " + c.getName());			// class + 類名
        if(sc!=null && sc != Object.class)
            System.out.print(" extends " + sc.getName());	// 繼承的父類
        System.out.println();
        System.out.println("{");
        printConstructor(c);								// 獲取構造器函數
        System.out.println();
        printField(c);										// 獲取字段名
        System.out.println();
        printMethod(c);										// 獲取方法名
        System.out.println("}");
    }
    private void printField(Class c){
        Field[] fields = c.getDeclaredFields();				// 獲取字段名
        for (Field f:fields
             ) {
            Class type = f.getType();						// 字段類型
            String name = f.getName();						// 字段名
            System.out.print("    ");
            String midifier = Modifier.toString(f.getModifiers());
            if(midifier.length()>0)
                System.out.print(midifier + " ");			// 字段修飾符
            System.out.println(type.getName() + " " + name + ";");
        }
    }
    private void printConstructor(Class c){
        Constructor[] constructors = c.getConstructors();	// 獲取構造方法名稱
        for (Constructor constructor:constructors
        ) {
            String midifier = Modifier.toString(constructor.getModifiers());
            String methodName = constructor.getName();		// 獲取構造方法名
            Class[] Params = constructor.getParameterTypes();// 獲取參數類型
            Class[] exceptions = constructor.getExceptionTypes();	// 獲取異常類型
            System.out.print("    ");
            if(midifier.length()>0)
                System.out.print(midifier + " ");			// 獲取修飾符
            System.out.print(methodName + "(");			 
            for(int i=0; i<Params.length; i++){
                if(i == Params.length - 1)
                    System.out.print( Params[i].getName());
                else
                    System.out.print( Params[i].getName() + ", ");
            }
            System.out.print(")");
            if(exceptions.length>0)
                System.out.print("throws ");				// 獲取異常類型
            for(int i=0; i<exceptions.length; i++){
                if(i == exceptions.length - 1)
                    System.out.print( exceptions[i].getName());
                else
                    System.out.print( exceptions[i].getName() + ", ");
            }
            System.out.println(";");
        }

    }

    private void printMethod(Class c){
        Method[] methods = c.getDeclaredMethods();
        for (Method m:methods
             ) {
            String midifier = Modifier.toString(m.getModifiers());
            String ret = m.getReturnType().getName();
            String methodName = m.getName();
            Class[] Params = m.getParameterTypes();
            Class[] exceptions = m.getExceptionTypes();
            System.out.print("    ");
            if(midifier.length()>0)
                System.out.print(midifier + " ");
            System.out.print(ret + " " + methodName + "(");
            for(int i=0; i<Params.length; i++){
               if(i == Params.length - 1)
                   System.out.print( Params[i].getName());
               else
                   System.out.print( Params[i].getName() + ", ");
            }
            System.out.print(")");
            if(exceptions.length>0)
                System.out.print("throws ");
            for(int i=0; i<exceptions.length; i++){
                if(i == exceptions.length - 1)
                    System.out.print( exceptions[i].getName());
                else
                    System.out.print( exceptions[i].getName() + ", ");
            }
            System.out.println(";");
        }
    }
}

輸出信息:

public final class java.lang.Double extends java.lang.Number
{
    public java.lang.Double(double);
    public java.lang.Double(java.lang.String)throws java.lang.NumberFormatException;

    public static final double POSITIVE_INFINITY;
    public static final double NEGATIVE_INFINITY;
    public static final double NaN;
    public static final double MAX_VALUE;
    public static final double MIN_NORMAL;
    public static final double MIN_VALUE;
    public static final int MAX_EXPONENT;
    public static final int MIN_EXPONENT;
    public static final int SIZE;
    public static final int BYTES;
    public static final java.lang.Class TYPE;
    private final double value;
    private static final long serialVersionUID;

    public boolean equals(java.lang.Object);
    public static java.lang.String toString(double);
    public java.lang.String toString();
    public int hashCode();
    public static int hashCode(double);
    public static double min(double, double);
    public static double max(double, double);
    public static native long doubleToRawLongBits(double);
    public static long doubleToLongBits(double);
    public static native double longBitsToDouble(long);
    public volatile int compareTo(java.lang.Object);
    public int compareTo(java.lang.Double);
    public byte byteValue();
    public short shortValue();
    public int intValue();
    public long longValue();
    public float floatValue();
    public double doubleValue();
    public static java.lang.Double valueOf(double);
    public static java.lang.Double valueOf(java.lang.String)throws java.lang.NumberFormatException;
    public static java.lang.String toHexString(double);
    public static int compare(double, double);
    public java.lang.Double resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup);
    public volatile java.lang.Object resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup)throws java.lang.ReflectiveOperationException;
    public java.util.Optional describeConstable();
    public static boolean isNaN(double);
    public boolean isNaN();
    public static boolean isInfinite(double);
    public boolean isInfinite();
    public static boolean isFinite(double);
    public static double sum(double, double);
    public static double parseDouble(java.lang.String)throws java.lang.NumberFormatException;
}

利用反射在運行時分析對象

前文講過如何利用反射分析一個類的組成,那麼對於類運行時的實例而言,能不能獲取到對象實例的具體值呢?能

要作到這一點,須要用到Field類中的get()set()(一樣Method類、Constructor類也有這個方法),例如看下面的代碼:

var harry = new Employee("Harry Hacker", 50000, 10, 1, 1989);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");
// the 'name' field of the Employee class
object v = f.get(harry);		// 獲取harry對象中字段爲name的值
// output:「Harry Hacker」

一樣更改值,能夠使用:

f.set(harry, "Askia");		// 設置harry對象中字段爲name的值

固然上面的get()set()代碼存在問題,由於name字段修飾符是private,所以對該字段的值進行訪問會拋出illegalAccessException

Java安全機制容許查看一個對象有哪些字段,可是除非擁有訪問權限,不然不能對這些字段進行讀寫。

那麼就真的沒有辦法對這些字段進行強制修改了嗎?也不是,咱們能夠調用setAccessible()來覆蓋java的訪問控制

f.setAccessible(true);
f.set(harry, "Askia");
// now harry.name is "Askia"

通用的toString()

使用反射調用任意的方法

使用反射編寫泛型數組代碼

相關文章
相關標籤/搜索