Java的反射機制是Java特性之一,反射機制是構建框架技術的基礎所在。靈活掌握Java反射機制,對大家以後學習框架技術有很大的幫助。
那麼什麼是Java的反射呢?
大家都知道,要讓Java程序能夠運行,那麼就得讓Java類要被Java虛擬機加載。Java類如果不被Java虛擬機加載,是不能正常運行的。現在我們運行的所有的程序都是在編譯期的時候就已經知道了你所需要的那個類的已經被加載了。
Java的反射機制是在編譯並不確定是哪個類被加載了,而是在程序運行的時候才加載、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射。
那麼Java反射有什麼作用呢?
假如我們有兩個程序員,一個程序員在寫程序的時候,需要使用第二個程序員所寫的類,但第二個程序員並沒完成他所寫的類。那麼第一個程序員的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程序員在沒有得到第二個程序員所寫的類的時候,來完成自身代碼的編譯。
Java的反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱爲Java類的“自審”。大家都用過Jcreator和eclipse。當我們構建出一個對象的時候,去調用該對象的方法和屬性的時候。一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供用戶進行選擇。這就是利用了Java反射的原理,是對我們創建對象的探知、自審。
Class類
要正確使用Java反射機制就得使用java.lang.Class這個類。它是Java反射機制的起源。當一個類被加載以後,Java虛擬機就會自動產生一個Class對象。通過這個Class對象我們就能獲得加載到虛擬機當中這個Class對象對應的方法、成員以及構造方法的聲明和定義等信息。
反射API
u反射API用於反應在當前Java虛擬機中的類、接口或者對象信息
u功能
—獲取一個對象的類信息.
—獲取一個類的訪問修飾符、成員、方法、構造方法以及超類的信息.
—檢獲屬於一個接口的常量和方法聲明.
—創建一個直到程序運行期間才知道名字的類的實例.
—獲取並設置一個對象的成員,甚至這個成員的名字是
在程序運行期間才知道.
—檢測一個在運行期間才知道名字的對象的方法
利用Java反射機制我們可以很靈活的對已經加載到Java虛擬機當中的類信息進行檢測。當然這種檢測在對運行的性能上會有些減弱,所以什麼時候使用反射,就要靠業務的需求、大小,以及經驗的積累來決定。
那麼如何利用反射API在運行的時候知道一個類的信息呢?
代碼示例:
- <span style="font-size: 16px;">import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import javax.swing.JOptionPane;
- /**
- *本類用於測試反射API,利用用戶輸入類的全路徑,
- *找到該類所有的成員方法和成員屬性
- */
- public class MyTest {
- /**
- *構造方法
- */
- public MyTest(){
- String classInfo=JOptionPane.showInputDialog(null,"輸入類全路徑");//要求用戶輸入類的全路徑
- try {
- Class cla=Class.forName(classInfo);//根據類的全路徑進行類加載,返回該類的Class對象
- Method[] method=cla.getDeclaredMethods();//利用得到的Class對象的自審,返回方法對象集合
- for(Method me:method){//遍歷該類方法的集合
- System.out.println(me.toString());//打印方法信息
- }
- System.out.println("********");
- Field[] field=cla.getDeclaredFields();//利用得到的Class對象的自審,返回屬性對象集合
- for(Field me:field){ //遍歷該類屬性的集合
- System.out.println(me.toString());//打印屬性信息
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- public static void main(String[] args) {
- new MyTest();
- }
- }</span>
<span style="font-size: 16px;">import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.swing.JOptionPane;
/**
*本類用於測試反射API,利用用戶輸入類的全路徑,
*找到該類所有的成員方法和成員屬性
*/
public class MyTest {
/**
*構造方法
*/
public MyTest(){
String classInfo=JOptionPane.showInputDialog(null,"輸入類全路徑");//要求用戶輸入類的全路徑
try {
Class cla=Class.forName(classInfo);//根據類的全路徑進行類加載,返回該類的Class對象
Method[] method=cla.getDeclaredMethods();//利用得到的Class對象的自審,返回方法對象集合
for(Method me:method){//遍歷該類方法的集合
System.out.println(me.toString());//打印方法信息
}
System.out.println("********");
Field[] field=cla.getDeclaredFields();//利用得到的Class對象的自審,返回屬性對象集合
for(Field me:field){ //遍歷該類屬性的集合
System.out.println(me.toString());//打印屬性信息
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new MyTest();
}
}</span>
運行的時候,我們輸入javax.swing.JFrame,那麼運行結果如下:
public void javax.swing.JFrame.remove(java.awt.Component)
public void javax.swing.JFrame.update(java.awt.Graphics)
…………
********
public static final int javax.swing.JFrame.EXIT_ON_CLOSE
private int javax.swing.JFrame.defaultCloseOperation
…………
大家可以發現,類的全路徑是在程序運行的時候,由用戶輸入的。所以虛擬機事先並不知道所要加載類的信息,這就是利用反射機制來對用戶輸入的類全路徑來對類自身的一個自審。從而探知該類所擁有的方法和屬性。
通過上面代碼,大家可以知道編譯工具爲什麼能夠一按點就能列出用戶當前對象的屬性和方法了。它是先獲得用戶輸入對象的字符串,然後利用反射原理來對這樣的類進行自審,從而列出該類的方法和屬性。
使用反射機制的步驟:
u導入java.lang.relfect 包
u遵循三個步驟
第一步是獲得你想操作的類的 java.lang.Class 對象
第二步是調用諸如 getDeclaredMethods 的方法
第三步使用 反射API 來操作這些信息
獲得Class對象的方法
u如果一個類的實例已經得到,你可以使用
【Class c = 對象名.getClass(); 】
例: TextField t = new TextField();
Class c = t.getClass();
Class s = c.getSuperclass();
u如果你在編譯期知道類的名字,你可以使用如下的方法
Class c = java.awt.Button.class;
或者
Class c = Integer.TYPE;
u如果類名在編譯期不知道, 但是在運行期可以獲得, 你可以使用下面的方法
Class c = Class.forName(strg);
這樣獲得Class類對象的方法,其實是利用反射API把指定字符串的類加載到內存中,所以也叫類加載器加載方法。這樣的話,它會把該類的靜態方法和靜態屬性,以及靜態代碼全部加載到內存中。但這時候,對象還沒有產生。所以爲什麼靜態方法不能訪問非靜態屬性和方法。因爲靜態方法和屬性產生的時機在非靜態屬性和方法之前。
代碼示例:
- <span style="font-size: 16px;">package com;
- public class MyTest {
- public static void main(String[] args) {
- TestOne one=null;
- try{
- Class cla=Class.forName("com.TestOne");//進行com.TestOne類加載,返回一個Class對象
- System.out.println("********");
- one=(TestOne)cla.newInstance();//產生這個Class類對象的一個實例,調用該類無參的構造方法,作用等同於new TestOne()
- }catch(Exception e){
- e.printStackTrace();
- }
- TestOne two=new TestOne();
- System.out.println(one.getClass() == two.getClass());//比較兩個TestOne對象的Class對象是否是同一個對象,在這裏結果是true。說明如果兩個對象的類型相同,那麼它們會有相同的Class對象
- }
- }
- class TestOne{
- static{
- System.out.println("靜態代碼塊運行");
- }
- TestOne(){
- System.out.println("構造方法");
- }
- }</span>
<span style="font-size: 16px;">package com;
public class MyTest {
public static void main(String[] args) {
TestOne one=null;
try{
Class cla=Class.forName("com.TestOne");//進行com.TestOne類加載,返回一個Class對象
System.out.println("********");
one=(TestOne)cla.newInstance();//產生這個Class類對象的一個實例,調用該類無參的構造方法,作用等同於new TestOne()
}catch(Exception e){
e.printStackTrace();
}
TestOne two=new TestOne();
System.out.println(one.getClass() == two.getClass());//比較兩個TestOne對象的Class對象是否是同一個對象,在這裏結果是true。說明如果兩個對象的類型相同,那麼它們會有相同的Class對象
}
}
class TestOne{
static{
System.out.println("靜態代碼塊運行");
}
TestOne(){
System.out.println("構造方法");
}
}</span>
以上代碼過行的結果是:
靜態代碼塊運行
***********
構造方法
構造方法
代碼分析:
在進行Class.forName("com.TestOne")的時候,實際上是對com.TestOne進行類加載,這時候,會把靜態屬性、方法以及靜態代碼塊都加載到內存中。所以這時候會打印出"靜態代碼塊運行"。但這時候,對象卻還沒有產生。所以"構造方法"這幾個字不會打印。當執行cla.newInstance()的時候,就是利用反射機制將Class對象生成一個該類的一個實例。這時候對象就產生了。所以打印"構造方法"。當執行到TestOne two=new TestOne()語句時,又生成了一個對象。但這時候類已經加載完畢,靜態的東西已經加載到內存中,而靜態代碼塊只執行一次,所以不用再去加載類,所以只會打印"構造方法",而"靜態代碼塊運行"不會打印。
反射機制不但可以例出該類對象所擁有的方法和屬性,還可以獲得該類的構造方法及通過構造方法獲得實例。也可以動態的調用這個實例的成員方法。
代碼示例:
- <span style="font-size: 16px;">package reflect;
- import java.lang.reflect.Constructor;
- /**
- *
- * 本類測試反射獲得類的構造器對象,
- * 並通過類構造器對象生成該類的實例
- *
- */
- public class ConstructorTest {
- public static void main(String[] args) {
- try {
- //獲得指定字符串類對象
- Class cla=Class.forName("reflect.Tests");
- //設置Class對象數組,用於指定構造方法類型
- Class[] cl=new Class[]{int.class,int.class};
- //獲得Constructor構造器對象。並指定構造方法類型
- Constructor con=cla.getConstructor(cl);
- //給傳入參數賦初值
- Object[] x={new Integer(33),new Integer(67)};
- //得到實例
- Object obj=con.newInstance(x);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- class Tests{
- public Tests(int x,int y){
- System.out.println(x+" "+y);
- }
- }</span>
<span style="font-size: 16px;">package reflect;
import java.lang.reflect.Constructor;
/**
*
* 本類測試反射獲得類的構造器對象,
* 並通過類構造器對象生成該類的實例
*
*/
public class ConstructorTest {
public static void main(String[] args) {
try {
//獲得指定字符串類對象
Class cla=Class.forName("reflect.Tests");
//設置Class對象數組,用於指定構造方法類型
Class[] cl=new Class[]{int.class,int.class};
//獲得Constructor構造器對象。並指定構造方法類型
Constructor con=cla.getConstructor(cl);
//給傳入參數賦初值
Object[] x={new Integer(33),new Integer(67)};
//得到實例
Object obj=con.newInstance(x);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Tests{
public Tests(int x,int y){
System.out.println(x+" "+y);
}
}</span>
運行的結果是” 33 67”。說明我們已經生成了Tests這個類的一個對象。