可以分析類能力的程序稱之爲反射(Reflection)java
反射機制能夠用來:數組
toString()
在程序運行時,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
類來構造一個類實例呢?答案確定是能的
使用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
對象getResource()
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");
反射機制中經常使用來作類分析的重要類:Field
、Method
、Constructor
。這些類都在java.lang.reflect
包中
接下來對這幾個類用來分析的方法進行簡單介紹:
String getName() // 返回該類型的類名字 String getPackageName() // 返回該類所在的包名 Field[] getFields() // 返回對象的全部公共字段,包括超類的公共字段 Field[] getDeclaredFields() // 返回對象的所有字段,若是類中沒有字段,或者對象是基本類型或者數組,則返回0長度數組 Class getSuperClass() // 獲取該類的父類Class對象 Method[] getMethods() // 返回對象所屬類或者接口的全部公共方法,包括超類的公共方法 Method[] getDeclaredMethods() // 返回對象所屬類或者接口的所有方法,不包括超類 Constructor[] getConstructors() // 返回這個類的全部公共構造器 Constructor[] getDeclaredConstructors() // 返回所有構造器
String getName() // 返回類中的字段名(屬性名)的字符串 Class getType() // 返回字段的類型(int、long、Date...) int getModifiers() // 獲取字段的修飾符(public、static、final...),返回1/0的二進制標誌位,能夠配合reflect包中的toString()來顯示具體的修飾符 Class getDeclaringClass() //獲取字段所屬的類對應的Class對象
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對象數組,其中各個對象表示該方法所拋出的異常的類型
String getName() // 返回類中的構造方法的字符串 int getModifiers() // 獲取構造方法的修飾符(public、static、final...),返回1/0的二進制標誌位,能夠配合reflect包中的toString()來顯示具體的修飾符 Class getDeclaringClass() //獲取構造方法所屬的類對應的Class對象 Class[] getParameterTypes() // 返回Class對象的數組,其中各個對象表示參數的類型 Class[] getExceptionTypes() // 返回Class對象數組,其中各個對象表示該方法所拋出的異常的類型
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()