目錄java
java基礎之反射數組
當程序主動使用某個類時,若是該類尚未被加載到內存中,則系統會經過加載、鏈接、初始化這三個步驟對該類進行初始化。有時會把這一整個流程統稱爲類加載或類初始化。
類加載指的是將類的class文件讀入內存中,併爲之建立一個 java.lang.Class 對象,也就是說程序使用任何類的時候,都會爲其建立一個class對象。ide
類被加載以後,系統會爲之生成一個Class對象,接着會進入鏈接階段,鏈接階段負責把類的二進制數據合併到JRE中。類的鏈接又分爲下面三個階段:spa
在java中對類變量指定初始值得方法有兩種:1. 聲明類變量時指定初始值;2. 使用靜態初始化塊爲類變量指定初始值。翻譯
類加載器負責將.class文件加載到內存中,併爲之生成對應的Class對象。類加載器負責加載全部的類,系統爲全部加載到內存中的類生成一個java.lang.Class 的實例。設計
類加載器的組成:代理
Java反射就是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;而且能改變它的屬性。
反射機制容許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麼咱們即可以更靈活的編寫代碼,代碼能夠在運行時裝配,無需在組件之間進行源代碼連接,下降代碼的耦合度;還有動態代理的實現等等。code
java程序中許多對象在運行時會出現兩種類型:運行時類型和編譯時類型,例如Person p = new Student();這句代碼中p在編譯時類型爲Person,運行時類型爲Student。程序須要在運行時發現對象和類的真實信心。而經過使用反射程序就能判斷出該對象和類屬於哪些類。對象
Java文件被編譯後,生成了.class文件,JVM此時就要去解讀.class文件。當程序主動去使用某個類時,JVM會經過前面提到的三個步驟:加載、鏈接和初始化三個步驟對類進行初始化。被編譯後的Java文件.class也被JVM解析爲一個對象,這個對象就是java.lang.Class。這樣當程序在運行時,每一個java文件就最終變成了Class類對象的一個實例。咱們經過Java的反射機制應用到這個實例,就能夠去得到甚至去添加改變這個類的屬性和動做,使得這個類成爲一個動態的類。
Class類的概念儘管很抽象,可是無疑,它是反射機制的起源,是Java語言中一個精巧美妙地設計。
下面是翻譯後的中文文檔的描述:
Class類的實例表示正在運行的Java應用程序的類和接口。枚舉是一種類,註釋(註解)是一種接口。每一個數組屬於被映射爲Class對象的一個類,全部具備相同元素類型和維數的數組都共享該Class對象。基本的Java類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也表示爲 Class 對象。Class沒有公用構造方法。Class對象是在加載類時由JVM以及經過調用類加載器中的defineClass方法自動構造的。繼承
在深刻到反射機制以前,先探析一下反射機制的定義和應用。反射機制定義:Java反射機制是在運行狀態時,對於任意一個類,都可以直到這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。
在Java中,Class類和java.lang.reflect類庫一塊兒構成了對Java反射機制的支持。其中最常使用到的類是Constructor,Field,Method,而這三個類都繼承了一個接口java.lang.reflect.Member。
實驗類
//因篇幅緣由,實驗代碼未展現set,get等等經常使用方法 public class Cat { private String name = ""; private String master = ""; private int age = 0; public Cat() { } private Cat(String name, String master) { } public Cat(String name, String master, int age) { } public void eat() { System.out.println("小魚乾真好吃~"); } private void play() { System.out.println("快來陪我玩~"); } @Override public String toString() { return "Cat [name=" + name + ", master=" + master + ", age=" + age + "]"; } }
在java中獲取Class對象有三種方法:
public static void main(String[] args) throws Exception { Cat c = new Cat(); Class cat1 = Class.forName("algorithms.sort.Cat"); Class cat2 = Cat.class; Class cat3 = c.getClass(); System.out.println(cat1 == cat2); System.out.println(cat2 == cat3); } //輸出結果 true true
public static void main(String[] args) throws Exception { Class cat = Cat.class; //獲取全部公共構造方法 Constructor<?> cons[] = cat.getConstructors(); for (Constructor<?> con : cons) { System.out.println("getConstructors-----------" + con); } System.out.println("**************************************"); //獲取全部構造方法 Constructor<?> cons2[] = cat.getDeclaredConstructors(); for (Constructor<?> con2 : cons2) { System.out.println("getDeclaredConstructors---" + con2); } } //輸出結果 getConstructors-----------public algorithms.sort.Cat(java.lang.String,java.lang.String,int) getConstructors-----------public algorithms.sort.Cat() ************************************** getDeclaredConstructors---public algorithms.sort.Cat(java.lang.String,java.lang.String,int) getDeclaredConstructors---private algorithms.sort.Cat(java.lang.String,java.lang.String) getDeclaredConstructors---public algorithms.sort.Cat()
建立對象
public static void main(String[] args) throws Exception { Class cat = Cat.class; //使用公共構造器實例化對象 Constructor<?> cons1 = cat.getConstructor(); //使用私有構造器實例化對象 Constructor<?> cons2 = cat.getDeclaredConstructor(String.class,String.class); Cat cat1 = (Cat)cons1.newInstance(); //私有的構造方法反射後要打開權限才能進行相應操做 cons2.setAccessible(true); Cat cat2 = (Cat)cons2.newInstance("tom","denny"); System.out.println(cat1); System.out.println(cat2); } //輸出結果 Cat [name=, master=, age=0] Cat [name=tom, master=denny, age=0]
在建立對象的過程當中,值得注意的是若是反射的構造方法是私有的,那麼要打開訪問權限才能進行對象的實例化;也就是使用cons2.setAccessible(true);語句的緣由。
獲取成員變量
public static void main(String[] args) throws Exception { Class cat = Cat.class; //獲取構造器 Constructor<?> cons = cat.getConstructor(String.class,String.class,int.class); //實例化對象 Cat cat1 = (Cat)cons.newInstance("tom","denny",5); System.out.println(cat1); System.out.println("****************"); Field fields = cat.getDeclaredField("name"); //打開訪問權限限制 fields.setAccessible(true); fields.set(cat1, "jack"); System.out.println(cat1); } //輸出結果 Cat [name=tom, master=denny, age=5] **************** Cat [name=jack, master=denny, age=5]
獲取成員方法
public static void main(String[] args) throws Exception { Class cat = Cat.class; //獲取構造器 Constructor<?> cons = cat.getConstructor(String.class, String.class, int.class); //實例化對象 Cat cat1 = (Cat) cons.newInstance("tom", "denny", 5); System.out.println(cat1); System.out.println("****************"); //獲取私有和公共成員方法 Method method1 = cat.getDeclaredMethod("setName", String.class); Method method2 = cat.getDeclaredMethod("eat"); method1.setAccessible(true); method1.invoke(cat1, "petter"); System.out.println(cat1); method2.invoke(cat1); } //輸出結果 Cat [name=tom, master=denny, age=5] **************** Cat [name=petter, master=denny, age=5] 小魚乾真好吃~
public static void main(String[] args) throws Exception { //泛型只在編譯期進行檢查,在運行期會被擦除 ArrayList<Integer> list = new ArrayList<>(); list.add(111); list.add(222); //拿到字節碼文件,字節碼文件屬於運行期 Class cla = Class.forName("java.util.ArrayList"); Method meth = cla.getMethod("add", Object.class); meth.invoke(list, "abc"); System.out.println(list); } //輸出結果 [111, 222, abc]