簡單的說,反射機制就是在程序的運行過程中被允許對程序本身進行操作,比如自我檢查,進行裝載,還可以獲取類本身,類的所有成員變量和方法,類的對象,還可以在運行過程中動態的創建類的實例,通過實例來調用類的方法,這就是反射機制一個比較重要的功能了。那麼要通過程序來理解反射機制,首先要理解類的加載過程。
在Java程序執行的時候,要經歷三個步驟:加載、連接和初始化。首先程序要加載到JVM的方法區中,然後進行連接,最後初始化。這裏就主要介紹一下類的加載。如上圖,首先,JVM會從硬盤中讀取Java源文件並將其加載到方法區中同時生成類名.class文件,也就是類對象,這個類對象中包含了我們創建類的實例時所需要的模板信息,也就是源代碼中的成員變量和方法等。Class本身也是一個類,它的主要功能之一就是生成類加載時的class文件,爲類的初始化及實例化做準備。而我們在程序中通過關鍵字new創建的對象創建的是類的對象,而不是類對象,二者的區別如圖中所示。
對類的加載有了一個大致的理解之後,我們來看一下實現反射機制的具體操作:
反射機制在我們所學習的框架中有很大的應用,而在我們實際開發中用的並不多,所以理解反射機制對我們學習框架來說很有幫助。
首先我們創建一個實體類User.java
package cn.itcast_01;
public class User extends Person{
public String name;
private int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
private User(int age)
{
super();
this.age = age;
}
public User(String name)
{
super();
this.name = name;
}
public User() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
public void exit()
{
System.out.println(name+"退出系統");
}
public void login(String username,String password)
{
System.out.println("用戶名:"+username);
System.out.println("密碼:"+password);
}
private String CheckInfo()
{
return "年齡:"+age;
}
}
其中包括成員變量,構造方法和一些成員方法。
利用反射機制可以獲取類對象(也就是我們前面介紹的類對象,獲取類對象之後我們便獲取了類的模板,可以對類進行一些操作),有以下三種方法:
1.類名.class()
2.對象名.getClass()
3.Class.forName(具體的類名)
我們通過代碼來看一下具體的操作:
package cn.itcast_01;
public class Demo {
public static void main(String[] args) throws Exception {
//1.類名.class
Class clz = User.class;
System.out.println(clz);
//2.對象名.getClass()
Class clz1 = new User().getClass();
System.out.println(clz==clz1);
//3.Class.forName()
//Class clz2 = Class.forName("User");
Class clz2 = Class.forName("cn.itcast_01.User");
System.out.println(clz==clz2);
}
}
控制檯輸出如下:
第一行就是所獲取的類對象,下面兩行true的結果表示類對象只有一個(這裏要注意的是第三種方法中類名一定要寫全稱,包名也要包括進去,不然JVM會無法定位這個類的具體位置)。下面在用一張圖來解釋這三種方法的執行時機:
在類加載的三個階段裏都可以獲取類對象,其中第三種方法,在源碼中獲取類對象是最常用的,也是反射機制在框架中的應用,鑑於我目前的理解,在框架中的應用可以是通過配置文件寫入所創建的類名,再利用第三種方法獲取類對象。
獲取類對象之後就可以對類進行一些創建對象、調用方法、訪問成員變量的操作了:
1.創建對象:
Object obj = 類對象.newInstance();
實例:
package cn.itcast_01;
public class Demo2 {
public static void main(String[] args) throws Exception {
Class clz = Class.forName("cn.itcast_01.User");
System.out.println(clz);
Object obj = clz.newInstance();
System.out.println(obj);
}
}
結果:
2.調用方法:
Method md = 類對象.getMethod("類中的公有方法名");
獲取公有方法,其中md是Method類型的方法名,
Object obj1 = lm.invoke(u2,"老趙","aixiaoba");
爲獲取到的方法命名,方便調用。
Method dm = 類對象.getDeclaredMethod("類中的私有方法名");
獲取私有方法名,又叫暴力獲取,此方法無視方法的訪問權限,即使是被private修飾的方法也會被獲取到。
Method lm = 類對象.getMethod("有參方法方法名",參數的類對象...);
獲取有參方法,同時要獲取參數的類對象,格式爲:參數類型.class
Object obj1 = lm.invoke(類的對象,"參數1","參數2");
調用獲取到的方法,使用invoke關鍵字,此處表示調用有參方法。
以上方法中由於不確定獲取到的對象類型,所以用Object接收。
實例1:
package cn.itcast_05_Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import cn.itcast_01.User;
public class Demo {
public static void main(String[] args) throws Exception {
User u = new User("小巴",18);
User u2 = new User("老趙",20);
Class clz = Class.forName("cn.itcast_01.User");
Method em = clz.getMethod("exit");
Object obj = em.invoke(u2);
System.out.println(obj);
Method lm = clz.getMethod("login",String.class,String.class);
Object obj1 = lm.invoke(u2,"老趙","aixiaoba");
System.out.println(obj1);
}
}
結果:
方法名參考實體類User.java,因爲方法爲void類型,所以運行結果中會出現null,表示返回值爲空。
實例2:
package cn.itcast_05_Method;
import java.lang.reflect.Method;
import cn.itcast_01.User;
public class Demo2 {
public static void main(String[] args) throws Exception {
User u = new User("小巴",18);
User u2 = new User("老趙",20);
Class clz = Class.forName("cn.itcast_01.User");
Method dm = clz.getDeclaredMethod("CheckInfo");
dm.setAccessible(true);
Object obj = dm.invoke(u2);
System.out.println(obj);
}
}
結果:
要注意的是,在暴力獲取私有方法後還要獲取調用該方法的權限:
dm.setAccessible(true);
將參數設置爲true.
3.訪問成員變量:
最重要的一個關鍵字:Field
Field nf = 類對象.getField("成員變量名");
和調用私有方法一樣,要訪問私有成員變量也要通過暴力獲取的的方式,同時也要獲取訪問私有成員變量的權限。
Field af = 類對象.getDeclaredField("私有成員變量名");
af.setAccessible(true);
實例1:
package cn.itcast_02Field;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws Exception {
Class clz = Class.forName("cn.itcast_01.User");
Object obj = clz.newInstance();
Field nf = clz.getField("name");
nf.set(obj, "小巴");
Object object = nf.get(obj);
System.out.println(object);
}
}
結果:
實例2:
package cn.itcast_02Field;
import java.lang.reflect.Field;
import cn.itcast_01.User;
public class Demo2 {
public static void main(String[] args) throws Exception {
User u = new User("小巴",18);
Class clz= Class.forName("cn.itcast_01.User");
Field af = clz.getDeclaredField("age");
af.setAccessible(true);
Object obj = af.get(u);
System.out.println(obj);
}
}
結果:
反射機制中還有很多知識需要學習,比如獲取構造方法,獲取全部屬性,獲取全部方法,等等等等,希望能和大家共勉,共同進步!!!