當程序主動使用某個類時,若是該類尚未被加載到內存中,則系統會經過加載、鏈接、初始化這三個步驟對該類進行初始化。有時會把這一整個流程統稱爲類加載或類初始化。
類加載指的是將類的class文件讀入內存中,併爲之建立一個 java.lang.Class 對象,也就是說程序使用任何類的時候,都會爲其建立一個class對象。java
類被加載以後,系統會爲之生成一個Class對象,接着會進入鏈接階段,鏈接階段負責把類的二進制數據合併到JRE中。類的鏈接又分爲下面三個階段:數組
驗證:確保被加載類的正確性ide
準備:負責爲類的靜態成員分配內存,並設置默認初始化值spa
解析:將類中的符號引用替換爲直接引用.net
在java中對類變量指定初始值得方法有兩種:1. 聲明類變量時指定初始值;2. 使用靜態初始化塊爲類變量指定初始值。翻譯
類加載的時機設計
建立類的實例的時候代理
訪問類的靜態變量的時候對象
調用類的靜態方法的時候blog
使用反射方式來強制建立某個類或接口對應的java.lang.Class對象
初始化某個類的子類的時候
直接使用java.exe命令來運行某個主類
類加載器負責將.class文件加載到內存中,併爲之生成對應的Class對象。類加載器負責加載全部的類,系統爲全部加載到內存中的類生成一個java.lang.Class 的實例。
類加載器的組成:
Bootstrap ClassLoader 根類加載器 : 也被稱爲引導類加載器,負責Java核心類的加載,好比System類,在JDK中JRE的lib目錄下rt.jar文件中的類
Extension ClassLoader 擴展類加載器 : 負責JRE的擴展目錄中jar包的加載,在JDK中JRE的lib目錄下ext目錄
System ClassLoader 系統類加載器 : 負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑,主要是咱們開發者本身寫的類
Java反射就是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;而且能改變它的屬性。
反射機制容許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麼咱們即可以更靈活的編寫代碼,代碼能夠在運行時裝配,無需在組件之間進行源代碼連接,下降代碼的耦合度;還有動態代理的實現等等。
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。
實驗類
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 + "]";
}
}
2.2.1 獲取Class對象
在java中獲取Class對象有三種方法:
經過類名獲取:Class c1 = Student.class; 調用某各種的class屬性來獲取Class對象。
經過對象獲取:Class c2 = stu.getClass(); 經過getClass方法,該方法是Object類下的一個方法。
經過全類名獲取:Class c3 = Class.forName("全限定類名"); 會有一個ClassNotFoundException異常
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
2.2.2 獲取構造器並建立對象
getConstructors():返回了表示此類公共構造方法的Constructor對象數組。
getDeclaredConstructors():這個方法返回Constructor對象的全部構造方法。
獲取構造器
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);語句的緣由。
2.2.3 獲取成員變量和成員方法
獲取成員變量
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]
小魚乾真好吃~
2.2.4 反射越過泛型檢查
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]