首先咱們瞭解一下JVM,什麼是JVM,就是Java的虛擬機。這個JVM的最大特色就是可使你的程序移植到其餘平臺上也能夠運行使用,你能夠簡單的理解成一個進程,程序,只不過他的做用是用來跑你的代碼的。上圖是java的內存模型,咱們主要關注的是方法區,Java堆和Java棧。接下來說講JVM的運行過程:java
假如你寫了一段代碼:Object obj=new Object();數據結構
而後把他運行起來!(真正的代碼不止這一段,這裏只是舉個例子)jvm
首先JVM會啓動,你的代碼會編譯成一個xxx.class文件,而後被類加載器加載進jvm的內存中,而後類Object加載到方法區中,建立了Object類的class對象到堆中,可是請注意這個對象不是new出來的對象,而是類的類型對象,每一個類只有一個class對象,做爲方法區類的數據結構的接口。jvm建立對象前,會先檢查類是否加載,尋找類對應的class對象,若加載好,則爲你的對象分配內存,以後就初始化,也就是執行代碼:new Object()。this
在jvm內存模型中有個叫類裝載子系統,接下來說講這個系統中的類加載器吧!spa
A.類的加載
當程序要使用某個類時,若是該類還未被加載到內存中,則系統會經過加載,鏈接(驗證,準備,解析),初始化三步來實現對這個類進行初始化。對象
a 加載
就是指將編譯後的class文件讀入內存,併爲之建立一個Class對象。
任何類被使用時系統都會創建一個Class對象
b 鏈接
驗證:是否有正確的內部結構,並和其餘類協調一致
準備:負責爲類的靜態成員分配內存,並設置默認初始化值
解析:將類的二進制數據中的符號引用替換爲直接引用(在方法中的賦值或者四則運算直接替換成最終值,好比a=1替換爲1)
c 初始化
簡單而言就是new 對象,初始化成員變量等等
注:簡單的說就是:把.class文件加載到內存裏,並把這個.class文件封裝成一個Class類型的對象。
B.類的加載時機
在如下的狀況,會把這個類加載進來
a. 建立類的實例
b. 類的靜態變量,或者爲靜態變量賦值
c. 使用到類的靜態方法
d. 使用反射方式來強制建立某個類或接口對應的java.lang.Class對象
e. 初始化某個類的子類
f. 直接使用java.exe命令來運行某個主類
C: 類加載器的種類(3種)
a. Bootstrap ClassLoader 根類加載器
也被稱爲引導類加載器,負責Java核心類的加載
好比System,String等。在JDK中JRE的lib目錄下rt.jar文件中繼承
b. Extension ClassLoader 擴展類加載器
負責JRE的擴展目錄中jar包的加載。
在JDK中JRE的lib目錄下ext目錄接口
c. System ClassLoader 系統類加載器
負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑。遊戲
A. 反射定義
a. JAVA反射機制是在運行狀態中,
對於任意一個類,都可以知道這個類的全部屬性和方法;
對於任意一個對象,都可以調用它的任意一個方法和屬性;
這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。進程
b.反射技術
條件:運行狀態
已知:一個類或一個對象(根本是已知.class文件)
結果:獲得這個類或對象的全部方法和屬性
注: 要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法.因此先要獲取到每個字節碼文件對應的Class類型的對象。
B. Class類
a. Class類及Class對象的瞭解
要想解剖一個類,必須先了解Class對象。
閱讀API的Class類得知,Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的 defineClass 方法自動構造的。
b. 獲得Class對象
1. 有三個方法(假設事先寫好了一個類Person)
方式一: 經過Object類中的getClass()方法
Person person = new Person();
Class c1 = person.getClass();
方式二: 經過 類名.class 獲取到字節碼文件對象(任意數據類型都具有一個class靜態屬性,看上去要比第一種方式簡單)。
Class c2 = Person.class;
方式三: 經過Class類中的方法(將類名做爲字符串傳遞給Class類中的靜態方法forName便可)。
Class c3 = Class.forName("Person");//注意引號內是類的全路徑或者配置文件
注:第三種和前兩種的區別是:
前兩種你必須明確Person類型.
後面是指定這種類型的字符串就行.這種擴展更強.我不須要知道你的類.我只提供字符串,按照配置文件加載就能夠了。另外要注意,獲得class對象的操做中可能會有Exception,在main方法中throws Exception出來就能夠了。
獲取一個類的class文件對象的代碼部分:
package cn.example.demo1;
/*
* 獲取一個類的class文件對象的三種方式
* 1. 對象獲取
* 2. 類名獲取
* 3. Class類的靜態方法獲取
*/
public class ReflectDemo {
public static void main(String[] args)throws ClassNotFoundException {
//1. 對象獲取
Person p = new Person();
//調用Person類的父類的方法 getClass
Class c = p.getClass();
System.out.println(c);
//2. 類名獲取
//每一個類型,包括基本和引用,都會賦予這個類型一個靜態的屬性,屬性名字class
Class c1 = Person.class;
System.out.println(c1);
//3. Class類的靜態方法獲取 forName(字符串的類名)包名.類名
Class c2 = Class.forName("cn.example.demo1.Person");
System.out.println(c2);
}
}
下面是Person類的代碼
package cn.example.demo1;
public class Person {
public String name;
private int age;
/*static{
System.out.println("靜態代碼塊");
}*/
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
private Person(int age,String name){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("人吃飯");
}
public void sleep(String s, int a,double d){
System.out.println("人在睡覺"+s+"....."+a+"....."+d);
}
private void playGame(){
System.out.println("人在打遊戲");
}
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在反射機制中,把類中的成員(構造方法、成員方法、成員變量)都封裝成了對應的類進行表示。其中,構造方法使用類Constructor表示。可經過Class類中提供的方法獲取構造方法:
返回一個構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
獲取public修飾, 指定參數類型所對應的構造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
獲取指定參數類型所對應的構造方法(包含私有的)
返回多個構造方法
public Constructor<?>[] getConstructors()
獲取全部的public 修飾的構造方法
public Constructor<?>[] getDeclaredConstructors()
獲取全部的構造方法(包含私有的)
獲取構造方法的代碼部分:
package cn.example.demo1;
import java.lang.reflect.Constructor;
/*
* 經過反射獲取class文件中的構造方法,運行構造方法
* 運行構造方法,建立對象
* 獲取class文件對象
* 從class文件對象中,獲取須要的成員
*
* Constructor 描述構造方法對象類
*/
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("cn.example.demo1.Person");
//使用class文件對象,獲取類中的構造方法
// Constructor[] getConstructors() 獲取class文件對象中的全部公共的構造方法
/*Constructor[] cons = c.getConstructors();
for(Constructor con : cons){
System.out.println(con);
}*/
//獲取指定的構造方法,空參數的構造方法
Constructor con = c.getConstructor();
//運行空參數構造方法,Constructor類方法 newInstance()運行獲取到的構造方法
Object obj = con.newInstance();
System.out.println(obj.toString());
}
}
在反射機制中,把類中的成員變量使用類Field表示。可經過Class類中提供的方法獲取成員變量:
返回一個成員變量
public Field getField(String name)
獲取指定的 public修飾的變量
public Field getDeclaredField(String name)
獲取指定的任意變量
返回多個成員變量
public Field[] getFields()
獲取全部public 修飾的變量
public Field[] getDeclaredFields()
獲取全部的 變量 (包含私有)
獲取成員變量的代碼部分:
package cn.example.demo1;
import java.lang.reflect.Field;
/*
* 反射獲取成員變量,並修改值
* Person類中的成員String name
*/
public class ReflectDemo5 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("cn.itcast.demo1.Person");
Object obj = c.newInstance();
//獲取成員變量 Class類的方法 getFields() class文件中的全部公共的成員變量
//返回值是Field[] Field類描述成員變量對象的類
/*Field[] fields = c.getFields();
for(Field f : fields){
System.out.println(f);
}*/
//獲取指定的成員變量 String name
//Class類的方法 Field getField(傳遞字符串類型的變量名) 獲取指定的成員變量
Field field = c.getField("name");
//Field類的方法 void set(Object obj, Object value) ,修改爲員變量的值
//Object obj 必須有對象的支持, Object value 修改後的值
field.set(obj,"王五");
System.out.println(obj);
}
}
在反射機制中,把類中的成員方法使用類Method表示。可經過Class類中提供的方法獲取成員方法:
返回獲取一個方法:
public Method getMethod(String name, Class<?>... parameterTypes)
獲取public 修飾的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
獲取任意的方法,包含私有的
參數1: name 要查找的方法名稱; 參數2: parameterTypes 該方法的參數類型
返回獲取多個方法:
public Method[] getMethods()
獲取本類與父類中全部public 修飾的方法
public Method[] getDeclaredMethods()
獲取本類中全部的方法(包含私有的)
獲取成員方法的代碼演示:
package cn.example.demo1;
import java.lang.reflect.Method;
/*
* 反射獲取成員方法並運行
* public void eat(){}
*/
public class ReflectDemo6 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("cn.itcast.demo1.Person");
Object obj = c.newInstance();
//獲取class對象中的成員方法
// Method[] getMethods()獲取的是class文件中的全部公共成員方法,包括繼承的
// Method類是描述成員方法的對象
/*Method[] methods = c.getMethods();
for(Method m : methods){
System.out.println(m);
}*/
//獲取指定的方法eat運行
// Method getMethod(String methodName,Class...c)
// methodName獲取的方法名 c 方法的參數列表
Method method = c.getMethod("eat");
//使用Method類中的方法,運行獲取到的方法eat
//Object invoke(Object obj, Object...o)
method.invoke(obj);
}
}
經過反射配置文件,運行配置文件中指定類的對應方法
讀取Peoperties.txt文件中的數據,經過反射技術,來完成Person對象的建立
Peoperties.txt文件內容:
#className=cn.example.demo3.Student
#methodName=study
className=cn.example.demo3.Person
methodName=eat
#className=cn.example.demo3.Worker
#methodName=job
反射配置文件運行類中的方法代碼部分:
package cn.example.demo3;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;
/* * 調用Person方法,調用Student方法,調用Worker方法 * 類不清楚,方法也不清楚 * 經過配置文件實現此功能 * 運行的類名和方法名字,以鍵值對的形式,寫在文本中 * 運行哪一個類,讀取配置文件便可 * 實現步驟: * 1. 準備配置文件,鍵值對 * 2. IO流讀取配置文件 Reader * 3. 文件中的鍵值對存儲到集合中 Properties * 集合保存的鍵值對,就是類名和方法名 * 4. 反射獲取指定類的class文件對象 * 5. class文件對象,獲取指定的方法 * 6. 運行方法 */ public class Test { public static void main(String[] args) throws Exception{ //IO流讀取配置文件 FileReader r = new FileReader("config.properties"); //建立集合對象 Properties pro = new Properties(); //調用集合方法load,傳遞流對象 pro.load(r); r.close(); //經過鍵獲取值 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //反射獲取指定類的class文件對象 Class c = Class.forName(className); Object obj = c.newInstance(); //獲取指定的方法名 Method method = c.getMethod(methodName); method.invoke(obj); } }