運行時類型信息使得你能夠在程序運行時發現和使用類型信息java
經過
Class
對象能夠在運行時發現一個對象完整的類繼承結構數組
類是程序的一部分,每個類都會有一個Class
對象。換句話說既每編寫一個新的類,就會產生一個Class
對象。而這些Class
對象信息是保存在咱們用javac 類名.java
進行編譯時產生的.class
文件中的。爲了生成這個對象,運行這個程序的java虛擬機(JVM)會使用類加載器進行加載ide
Java類加載器(Java Classloader)是Java運行時環境(Java Runtime Environment)的一部分,負責動態加載Java類到Java虛擬機的內存空間中函數
即類加載器是Java虛擬機將描述類的數據,例如類的各類方法,構造參數之類的信息從Class
文件加載到內存中去,而且對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的java類型。this
全部的類都是在對其進行第一次使用的時候,動態加載到JVM中的,即和編譯時須要進行鏈接工做的語言不通,在java中,類型的加載、鏈接和初始化都是在程序運行期間完成的。code
類加載器在進行加載的時候會首先檢查這個類的Class對象是否已經進行了加載,若是沒有加載,那麼默認的類加載器就會根據類名進行查找.class
文件。經過下面例子,咱們能夠清楚看出類是在第一次被使用的時候纔會加載。對象
static靜態代碼塊的加載是在類加載的時候進行的繼承
class Tom{ static { System.out.println("Loading Tom"); } } class Jerry{ static { System.out.println("Loading Jerry"); } } class Mary{ static { System.out.println("Loading Mary"); } } public class Demo4 { public static void main(String[] args) { System.out.println("Inside Main"); new Tom(); System.out.println("After Loading Tom"); new Mary(); System.out.println("After Loading Mary"); new Jerry(); } }
輸出結果以下:接口
Inside Main Loading Tom After Loading Tom Loading Mary After Loading Mary Loading Jerry
類加載器在加載類的過程當中會分爲三個階段內存
.class
文件的字節碼文件同一個類加載器下,一個類型只會初始化一次。
.getClass()
方法獲取Tom tom = new Tom(); Class class = tom.getClass();
.class
獲取Class class = Tom.class;
Class.forName
,其中的參數必須爲帶包名的類路徑Class c = Class.forName("Tom");
一般在反射中建立Class對象時使用的第二種方法,由於第一種已經有這個對象了,幹嗎還須要反射,第三種的話會有侷限性,須要導入所要建立對象的包路徑。
咱們在上面獲取到Class
對象,而咱們拿到了Class
對象之後就能對其進行操做
Class
類和javalang.reflect
類庫一塊兒對反射的概念進行了支持,該類庫包含了Field
,Method
以及Constructor
類(每一個類都實現了Member接口).這些類型的對象是由JVM在運行時建立的,用以表示未知類裏所對應的成員信息.這樣就能夠用Constructor
建立新的對象。下面演示一下經過反射獲取構造方法。
public class A { private String a; private String b; public String c; public A(String a, String b) { this.a = a; this.b = b; } private A(String a){ this.a=a; } public A(){} ——————————get.set方法省略 }
Class a=Class.forName("Practice.Day05.A"); Constructor[] constructors = a.getConstructors(); for (Constructor constructor:constructors){ System.out.println(constructor); }
此時咱們發現打印出來的信息以下:
public Practice.Day05.A() public Practice.Day05.A(java.lang.String,java.lang.String)
咱們發現沒有私有的構造函數,由於getConstructors()
方法得到是public
的構造函數,而getDeclaredFields()
方法得到是包括public
, protected
, default
,private
的構造函數,此時若是咱們將代碼改爲以下:
Class a=Class.forName("Practice.Day05.A"); Constructor[] constructors = a.getDeclaredConstructors(); for (Constructor constructor:constructors){ System.out.println(constructor); }
咱們發現打印的參數就會多了一個private的構造函數
public Practice.Day05.A() private Practice.Day05.A(java.lang.String) public Practice.Day05.A(java.lang.String,java.lang.String)
咱們在上面都是得到了一個構造方法的數組,若是想要得到具體的構造方法的話,那麼能夠經過傳入構造方法的入參的類型,能夠得到這個構造方法,具體例子以下:
Class a=Class.forName("Practice.Day05.A"); Constructor constructor = a.getConstructor(null); Constructor stringConstructor = a.getConstructor(String.class,String.class); Constructor stringPrivateConstructor=a.getDeclaredConstructor(String.class); System.out.println(constructor); System.out.println(stringConstructor); System.out.println(stringPrivateConstructor);
打印的信息以下:
public Practice.Day05.A() public Practice.Day05.A(java.lang.String,java.lang.String) private Practice.Day05.A(java.lang.String)
####2.1.4 經過構造方法實例化類
咱們得到了Constructor
對象之後,能夠經過其中的newInstance
方法進行實例化對象。例子以下:
Class a=Class.forName("Practice.Day05.A"); Constructor constructor = a.getConstructor(null); Constructor stringConstructor = a.getConstructor(String.class,String.class); A nullA= (A) constructor.newInstance(); A stringA= (A) stringConstructor.newInstance("BuXueWuShu","BuXueWuShu"); nullA.setA("BuXueWuShu"); System.out.println("nullA:"+nullA.getA()); System.out.println("stringA:"+stringA.getA());
打印信息以下:
nullA:BuXueWuShu stringA:BuXueWuShu
仍是上面的實體類的例子,其中有私有的兩個變量是a和b,私有的變量是c。
若是類中有屬性的話,兩個方法都是返回一個Field
數組.其中getDeclaredFields()
方法返回的是全部參數包括public
, protected
, default
,private
,而getFields()
只返回public
的參數。例如以下
Class a=Class.forName("Practice.Day05.A"); Field[] allDeclaredFields = a.getDeclaredFields();--得到全部成員變量 Field[] fields = a.getFields();--只得到public的成員變量 for (Field field:allDeclaredFields){ System.out.println(field); } System.out.println("-----------------------"); for (Field field:fields){ System.out.println(field); }
打印的參數以下:
private java.lang.String Practice.Day05.A.a private java.lang.String Practice.Day05.A.b public java.lang.String Practice.Day05.A.c ----------------------- public java.lang.String Practice.Day05.A.c
得到指定的成員變量其實和上面的獲取指定的構造方法是同樣的。舉例以下:
Class a=Class.forName("Practice.Day05.A"); Field c1 = a.getField("c"); Field a1 = a.getDeclaredField("a"); System.out.println(c1); System.out.println("---------------------"); System.out.println(a1);
打印參數以下:
public java.lang.String Practice.Day05.A.c --------------------- private java.lang.String Practice.Day05.A.a
得到了Field的對象後,能夠調用get()
和set()
方法對某個對象中的屬性進行取值和賦值的操做。例子以下
Class a=Class.forName("Practice.Day05.A"); Constructor nullClass=a.getDeclaredConstructor(null); A nullA= (A) nullClass.newInstance();--得到A的實例化對象 Field a1 = a.getDeclaredField("a"); a1.setAccessible(true);--變量a是private的,因此須要解除私有限定 a1.set(nullA,"BuXueWuShu");--爲nullA對象中的變量a進行賦值操做 System.out.println("nullA="+a1.get(nullA));--取出nullA對象中的變量a
打印信息以下:
nullA=BuXueWuShu
注意在對私有的成員變量進行賦值操做時,要解除私有限定,調用
setAccessible()
方法,賦值爲true
經過得到的Class
對象,調用它的getDeclaredMethods()
和getMethods()
方法能夠得到類中的方法的信息。例若有如下的一個類。
public class TestMethod { private String playName; public void show(String playName){ System.out.println("I Love "+playName); } private String returnPlayName(){ return playName; } }
作以下的調用:
Class a=Class.forName("Practice.Day05.TestMethod"); Method[] allDeclaredMethods = a.getDeclaredMethods(); Method[] methods = a.getMethods(); for (Method method:allDeclaredMethods){ System.out.println(method); } System.out.println("-----------------"); for (Method method:methods){ System.out.println(method); }
能夠發現打印以下的信息:
public void Practice.Day05.TestMethod.show(java.lang.String) private java.lang.String Practice.Day05.TestMethod.returnPlayName() ----------------- public void Practice.Day05.TestMethod.show(java.lang.String) public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
getDeclaredMethods()
方法會得到自身和實現的接口中全部的方法,可是不會得到繼承來的方法,getMethods()
方法會得到全部的不管是實現的接口仍是繼承來的public
的方法
經過方法名和方法中的參數類型就能夠得到指定的方法
Class a=Class.forName("Practice.Day05.TestMethod"); Method show = a.getDeclaredMethod("show", String.class); System.out.println(show);
打印信息以下:
public void Practice.Day05.TestMethod.show(java.lang.String)
能夠經過調用Method
對象的invoke()
方法進行調用方法。例子以下:
Class a=Class.forName("Practice.Day05.TestMethod"); Constructor nullClass=a.getDeclaredConstructor(null); TestMethod nullTestMethod= (TestMethod) nullClass.newInstance(); Method show = a.getDeclaredMethod("show",String.class); show.invoke(nullTestMethod,"BasketBall");
打印參數以下:
I Love BasketBall
若是調用的是private的方法,那麼在使用
invoke()
方法以前要先解除私有限定,即調用setAccessible()
方法,賦值爲true