java反射詳解之反射作用

本文轉載兩篇文章,前一部分很好的解釋了Java 反射在開發中的作用機制,後一部分詳細給出了反射的運用案例。


第一部分: 反射作用

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在運行的時候知道一個類的信息呢?

代碼示例:

[java] view plain copy
  1. <span style="font-size:16px;">import java.lang.reflect.Field;  
  2. import java.lang.reflect.Method;  
  3. import javax.swing.JOptionPane;  
  4. /** 
  5.   *本類用於測試反射API,利用用戶輸入類的全路徑, 
  6. *找到該類所有的成員方法和成員屬性 
  7.   */  
  8. public class MyTest {  
  9.      /** 
  10.      *構造方法 
  11.      */  
  12.     public MyTest(){  
  13.        String classInfo=JOptionPane.showInputDialog(null,"輸入類全路徑");//要求用戶輸入類的全路徑  
  14.        try {  
  15.            Class cla=Class.forName(classInfo);//根據類的全路徑進行類加載,返回該類的Class對象  
  16.             
  17.            Method[] method=cla.getDeclaredMethods();//利用得到的Class對象的自審,返回方法對象集合  
  18.             
  19.            for(Method me:method){//遍歷該類方法的集合  
  20.               System.out.println(me.toString());//打印方法信息  
  21.            }  
  22.             
  23.            System.out.println("********");  
  24.             
  25.            Field[] field=cla.getDeclaredFields();//利用得到的Class對象的自審,返回屬性對象集合  
  26.            for(Field me:field){ //遍歷該類屬性的集合  
  27.               System.out.println(me.toString());//打印屬性信息  
  28.            }  
  29.        } catch (ClassNotFoundException e) {  
  30.            e.printStackTrace();  
  31.        }  
  32.     }  
  33.     public static void main(String[] args) {  
  34.        new MyTest();  
  35.     }  
  36. }</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把指定字符串的類加載到內存中,所以也叫類加載器加載方法。這樣的話,它會把該類的靜態方法和靜態屬性,以及靜態代碼全部加載到內存中。但這時候,對象還沒有產生。所以爲什麼靜態方法不能訪問非靜態屬性和方法。因爲靜態方法和屬性產生的時機在非靜態屬性和方法之前。

代碼示例:

[java] view plain copy
  1. <span style="font-size:16px;">package  com;  
  2.    
  3. public class MyTest {  
  4.     public static void main(String[] args) {  
  5.        TestOne  one=null;  
  6.        try{  
  7.        Class  cla=Class.forName("com.TestOne");//進行com.TestOne類加載,返回一個Class對象  
  8.        System.out.println("********");  
  9.        one=(TestOne)cla.newInstance();//產生這個Class類對象的一個實例,調用該類無參的構造方法,作用等同於new TestOne()  
  10.        }catch(Exception e){  
  11.            e.printStackTrace();  
  12.        }  
  13.        TestOne two=new TestOne();  
  14.   System.out.println(one.getClass() == two.getClass());//比較兩個TestOne對象的Class對象是否是同一個對象,在這裏結果是true。說明如果兩個對象的類型相同,那麼它們會有相同的Class對象  
  15.     }  
  16. }  
  17.    
  18. class TestOne{  
  19.     static{  
  20.        System.out.println("靜態代碼塊運行");  
  21.     }  
  22.     TestOne(){  
  23.        System.out.println("構造方法");  
  24.     }  
  25. }</span>  

  以上代碼過行的結果是:

靜態代碼塊運行

***********

構造方法

構造方法


代碼分析:

在進行Class.forName("com.TestOne")的時候,實際上是對com.TestOne進行類加載,這時候,會把靜態屬性、方法以及靜態代碼塊都加載到內存中。所以這時候會打印出"靜態代碼塊運行"。但這時候,對象卻還沒有產生。所以"構造方法"這幾個字不會打印。當執行cla.newInstance()的時候,就是利用反射機制將Class對象生成一個該類的一個實例。這時候對象就產生了。所以打印"構造方法"。當執行到TestOne two=new TestOne()語句時,又生成了一個對象。但這時候類已經加載完畢,靜態的東西已經加載到內存中,而靜態代碼塊只執行一次,所以不用再去加載類,所以只會打印"構造方法",而"靜態代碼塊運行"不會打印。

反射機制不但可以例出該類對象所擁有的方法和屬性,還可以獲得該類的構造方法及通過構造方法獲得實例。也可以動態的調用這個實例的成員方法。

代碼示例:

[java] view plain copy
  1. <span style="font-size:16px;">package reflect;  
  2.    
  3. import java.lang.reflect.Constructor;  
  4.    
  5.    
  6. /** 
  7.  * 
  8.  * 本類測試反射獲得類的構造器對象, 
  9.  * 並通過類構造器對象生成該類的實例 
  10.  * 
  11.  */  
  12. public class ConstructorTest {  
  13.    
  14.     public static void main(String[] args) {  
  15.        try {  
  16.            //獲得指定字符串類對象  
  17.            Class cla=Class.forName("reflect.Tests");  
  18.            //設置Class對象數組,用於指定構造方法類型  
  19.            Class[] cl=new Class[]{int.class,int.class};  
  20.             
  21.            //獲得Constructor構造器對象。並指定構造方法類型  
  22.            Constructor con=cla.getConstructor(cl);  
  23.             
  24.            //給傳入參數賦初值  
  25.            Object[] x={new Integer(33),new Integer(67)};  
  26.             
  27.            //得到實例  
  28.            Object obj=con.newInstance(x);  
  29.        } catch (Exception e) {  
  30.            e.printStackTrace();  
  31.        }  
  32.     }  
  33.    
  34. }  
  35.    
  36. class Tests{  
  37.     public Tests(int x,int y){  
  38.        System.out.println(x+"    "+y);  
  39.     }  
  40. }</span>  

運行的結果是” 33    67。說明我們已經生成了Tests這個類的一個對象。 


第二部分: 反射詳細介紹


【案例1】通過一個對象獲得完整的包名和類名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Reflect;
 
/**
 * 通過一個對象獲得完整的包名和類名
 * */
class Demo{
    //other codes...
}
 
class hello{
    public static void main(String[] args) {
        Demo demo=new Demo();
        System.out.println(demo.getClass().getName());
    }
}

【運行結果】:Reflect.Demo

添加一句:所有類的對象其實都是Class的實例。

【案例2】實例化Class類對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Reflect;
class Demo{
    //other codes...
}
 
class hello{
    public static void main(String[] args) {
        Class<?> demo1=null;
        Class<?> demo2=null;
        Class<?> demo3=null;
        try{
            //一般儘量採用這種形式
            demo1=Class.forName("Reflect.Demo");
        }catch(Exception e){
            e.printStackTrace();
        }
        demo2=new Demo().getClass();
        demo3=Demo.class;
         
        System.out.println("類名稱   "+demo1.getName());
        System.out.println("類名稱   "+demo2.getName());
        System.out.println("類名稱   "+demo3.getName());
         
    }
}

【運行結果】:

類名稱   Reflect.Demo

類名稱   Reflect.Demo

類名稱   Reflect.Demo

【案例3】通過Class實例化其他類的對象

通過無參構造實例化對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package Reflect;
 
class Person{
     
    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;
    }
    @Override
    public String toString(){
        return "["+this.name+"  "+this.age+"]";
    }
    private String name;
    private int age;
}
 
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Person per=null;
        try {
            per=(Person)demo.newInstance();
        catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        per.setName("Rollen");
        per.setAge(20);
        System.out.println(per);
    }
}

【運行結果】:

[Rollen  20]

但是注意一下,當我們把Person中的默認的無參構造函數取消的時候,比如自己定義只定義一個有參數的構造函數之後,會出現錯誤:

比如我定義了一個構造函數:

1
2
3
4
public Person(String name, int age) {
        this.age=age;
        this.name=name;
    }

然後繼續運行上面的程序,會出現:

java.lang.InstantiationException: Reflect.Person

    at java.lang.Class.newInstance0(Class.java:340)

    at java.lang.Class.newInstance(Class.java:308)

    at Reflect.hello.main(hello.java:39)

Exception in thread "main" java.lang.NullPointerException

    at Reflect.hello.main(hello.java:47)

所以大家以後再編寫使用Class實例化其他類的對象的時候,一定要自己定義無參的構造函數

 

【案例】通過Class調用其他類中的構造函數 (也可以通過這種方式通過Class創建其他類的對象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package Reflect;
 
import java.lang.reflect.Constructor;
 
class Person{
     
    public Person() {
         
    }
    public Person(String name){
        this.name=name;
    }
    public Person(int age){
        this.age=age;
    }
    public Person(String name, int age) {
        this.age=age;
        this.name=name;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString(){
        return "["+this.name+"  "+this.age+"]";
    }
    private String name;
    private int age;
}
 
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Person per1=null;
        Person per2=null;
        Person per3=null;
        Person per4=null;
        //取得全部的構造函數
        Constructor<?> cons[]=demo.getConstructors();
        try{
            per1=(Person)cons[0].newInstance();
            per2=(Person)cons[1].newInstance("Rollen");
            per3=(Person)cons[2].newInstance(20);
            per4=(Person)cons[3].newInstance("Rollen",20);
        }catch(Exception e){
            e.printStackTrace();
        }
        System.out.println(per1);
        System.out.println(per2);
        System.out.println(per3);
        System.out.println(per4);
    }
}

【運行結果】:

[null  0]

[Rollen  0]

[null  20]

[Rollen  20]

【案例】 

返回一個類實現的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package Reflect;
 
interface China{
    public static final String name="Rollen";
    public static  int age=20;
    public void sayChina();
    public void sayHello(String name, int age);
}
 
class Person implements China{
    public Person() {
         
    }
    public Person(String sex){
        this.sex=sex;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public void sayChina(){
        System.out.println("hello ,china");
    }
    @Override
    public void sayHello(String name, int age){
        System.out.println(name+"  "+age);
    }
    private String sex;
}
 
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        //保存所有的接口
        Class<?> intes[]=demo.getInterfaces();
        for (int i = 0; i < intes.length; i++) {
            System.out.println("實現的接口   "+intes[i].getName());
        }
    }
}

【運行結果】:

實現的接口   Reflect.China

(注意,以下幾個例子,都會用到這個例子的Person類,所以爲節省篇幅,此處不再粘貼Person的代碼部分,只粘貼主類hello的代碼)

【案例】:取得其他類中的父類

1
2
3
4
5
6
7
8
9
10
11
12
13
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        //取得父類
        Class<?> temp=demo.getSuperclass();
        System.out.println("繼承的父類爲:   "+temp.getName());
    }
}

【運行結果】

繼承的父類爲:   java.lang.Object

【案例】:獲得其他類中的全部構造函數

這個例子需要在程序開頭添加import java.lang.reflect.*;

然後將主類編寫爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Constructor<?>cons[]=demo.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            System.out.println("構造方法:  "+cons[i]);
        }
    }
}

【運行結果】:

構造方法:  public Reflect.Person()

構造方法:  public Reflect.Person(java.lang.String)

但是細心的讀者會發現,上面的構造函數沒有public 或者private這一類的修飾符

下面這個例子我們就來獲取修飾符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Constructor<?>cons[]=demo.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            Class<?> p[]=cons[i].getParameterTypes();
            System.out.print("構造方法:  ");
            int mo=cons[i].getModifiers();
            System.out.print(Modifier.toString(mo)+" ");
            System.out.print(cons[i].getName());
            System.out.print("(");
            for(int j=0;j<p.length;++j){
                System.out.print(p[j].getName()+" arg"+i);
                if(j<p.length-1){
                    System.out.print(",");
                }
            }
            System.out.println("){}");
        }
    }
}

【運行結果】:

構造方法:  public Reflect.Person(){}

構造方法:  public Reflect.Person(java.lang.String arg1){}

有時候一個方法可能還有異常,呵呵。下面看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Method method[]=demo.getMethods();
        for(int i=0;i<method.length;++i){
            Class<?> returnType=method[i].getReturnType();
            Class<?> para[]=method[i].getParameterTypes();
            int temp=method[i].getModifiers();
            System.out.print(Modifier.toString(temp)+" ");
            System.out.print(returnType.getName()+"  ");
            System.out.print(method[i].getName()+" ");
            System.out.print("(");
            for(int j=0;j<para.length;++j){
                System.out.print(para[j].getName()+" "+"arg"+j);
                if(j<para.length-1){
                    System.out.print(",");
                }
            }
            Class<?> exce[]=method[i].getExceptionTypes();
            if(exce.length>0){
                System.out.print(") throws ");
                for(int k=0;k<exce.length;++k){
                    System.out.print(exce[k].getName()+" ");
                    if(k<exce.length-1){
                        System.out.print(",");
                    }
                }
            }else{
                System.out.print(")");
            }
            System.out.println();
        }
    }
}

【運行結果】:

public java.lang.String  getSex ()

public void  setSex (java.lang.String arg0)

public void  sayChina ()

public void  sayHello (java.lang.String arg0,int arg1)

public final native void  wait (long arg0) throws java.lang.InterruptedException

public final void  wait () throws java.lang.InterruptedException

public final void  wait (long arg0,int arg1) throws java.lang.InterruptedException

public boolean  equals (java.lang.Object arg0)

public java.lang.String  toString ()

public native int  hashCode ()

public final native java.lang.Class  getClass ()

public final native void  notify ()

public final native void  notifyAll ()

【案例】接下來讓我們取得其他類的全部屬性吧,最後我講這些整理在一起,也就是通過class取得一個類的全部框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        try {
            demo = Class.forName("Reflect.Person");
        catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===============本類屬性========================");
        // 取得本類的全部屬性
        Field[] field = demo.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 權限修飾符
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 屬性類型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + field[i].getName() + ";");
        }
        System.out.println("===============實現的接口或者父類的屬性========================");
        // 取得實現的接口或者父類的屬性
        Field[] filed1 = demo.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 權限修飾符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 屬性類型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + filed1[j].getName() + ";");
        }
    }
}

【運行結果】:

===============本類屬性========================

private java.lang.String sex;

===============實現的接口或者父類的屬性========================

public static final java.lang.String name;

public static final int age;

【案例】其實還可以通過反射調用其他類中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        try {
            demo = Class.forName("Reflect.Person");
        catch (Exception e) {
            e.printStackTrace();
        }
        try{
            //調用Person類中的sayChina方法
            Method method=demo.getMethod("sayChina");
            method.invoke(demo.newInstance());
            //調用Person的sayHello方法
            method=demo.getMethod("sayHello", String.class,int.class);
            method.invoke(demo.newInstance(),"Rollen",20);
             
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  【運行結果】:

hello ,china

Rollen  20

【案例】調用其他類的set和get方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        Object obj=null;
        try {
            demo = Class.forName("Reflect.Person");
        catch (Exception e) {
            e.printStackTrace();
        }
        try{
         obj=demo.newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        setter(obj,"Sex","男",String.class);
        getter(obj,"Sex");
    }
 
    /**
     * @param obj
     *            操作的對象
     * @param att
     *            操作的屬性
     * */
    public static void getter(Object obj, String att) {
        try {
            Method method = obj.getClass().getMethod("get" + att);
            System.out.println(method.invoke(obj));
        catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
     * @param obj
     *            操作的對象
     * @param att
     *            操作的屬性
     * @param value
     *            設置的值
     * @param type
     *            參數的屬性
     * */
    public static void setter(Object obj, String att, Object value,
            Class<?> type) {
        try {
            Method method = obj.getClass().getMethod("set" + att, type);
            method.invoke(obj, value);
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}// end class

【運行結果】:

 【案例】通過反射操作屬性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class hello {
    public static void main(String[] args) throws Exception {
        Class<?> demo = null;
        Object obj = null;
 
        demo = Class.forName("Reflect.Person");
        obj = demo.newInstance();
 
        Field field = demo.getDeclaredField("sex");
        field.setAccessible(true);
        field.set(obj, "男");
        System.out.println(field.get(obj));
    }
}// end class

【案例】通過反射取得並修改數組的信息:

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.*;
class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5};
        Class<?>demo=temp.getClass().getComponentType();
        System.out.println("數組類型: "+demo.getName());
        System.out.println("數組長度  "+Array.getLength(temp));
        System.out.println("數組的第一個元素: "+Array.get(temp, 0));
        Array.set(temp, 0100);
        System.out.println("修改之後數組第一個元素爲: "+Array.get(temp, 0));
    }
}

【運行結果】:

數組類型: int

數組長度  5

數組的第一個元素: 1

修改之後數組第一個元素爲: 100

【案例】通過反射修改數組大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5,6,7,8,9};
        int[] newTemp=(int[])arrayInc(temp,15);
        print(newTemp);
        System.out.println("=====================");
        String[] atr={"a","b","c"};
        String[] str1=(String[])arrayInc(atr,8);
        print(str1);
    }
     
    /**
     * 修改數組大小
     * */
    public static Object arrayInc(Object obj,int len){
        Class<?>arr=obj.getClass().getComponentType();
        Object newArr=Array.newInstance(arr, len);
        int co=Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    /**
     * 打印
     * */
    public static void print(Object obj){
        Class<?>c=obj.getClass();
        if(!c.isArray()){
            return;
        }
        System.out.println("數組長度爲: "+Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i)+" ");
        }
    }
}

【運行結果】:

數組長度爲: 15

1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 =====================

數組長度爲: 8

a b c null null null null null

動態代理

【案例】首先來看看如何獲得類加載器:

1
2
3
4
5
6
7
8
9
class test{
     
}
class hello{
    public static void main(String[] args) {
        test t=new test();
        System.out.println("類加載器  "+t.getClass().getClassLoader().getClass().getName());
    }
}

【程序輸出】:

類加載器  sun.misc.Launcher$AppClassLoader

其實在java中有三種類類加載器。

1)Bootstrap ClassLoader 此加載器採用c++編寫,一般開發中很少見。

2)Extension ClassLoader 用來進行擴展類的加載,一般對應的是jre\lib\ext目錄中的類

3)AppClassLoader 加載classpath指定的類,是最常用的加載器。同時也是java中默認的加載器。

如果想要完成動態代理,首先需要定義一個InvocationHandler接口的子類,已完成代理的具體操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package Reflect;
import java.lang.reflect.*;
 
//定義項目接口
interface Subject {
    public String say(String name, int age);
}
 
// 定義真實項目
class RealSubject implements Subject {
    @Override
    public String say(String name, int age) {
        return name + "  " + age;
    }
}
 
class MyInvocationHandler implements InvocationHandler {
    private Object obj = null;
 
    public Object bind(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), this);
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object temp = method.invoke(this.obj, args);
        return temp;
    }
}
 
class hello {
    public static void main(String[] args) {
        MyInvocationHandler demo = new MyInvocationHandler();
        Subject sub = (Subject) demo.bind(new RealSubject());
        String info = sub.say("Rollen"20);
        System.out.println(info);
    }
}

【運行結果】:

Rollen  20

類的生命週期

在一個類編譯完成之後,下一步就需要開始使用類,如果要使用一個類,肯定離不開JVM。在程序執行中JVM通過裝載,鏈接,初始化這3個步驟完成。

類的裝載是通過類加載器完成的,加載器將.class文件的二進制文件裝入JVM的方法區,並且在堆區創建描述這個類的java.lang.Class對象。用來封裝數據。 但是同一個類只會被類裝載器裝載以前

鏈接就是把二進制數據組裝爲可以運行的狀態。

 

鏈接分爲校驗,準備,解析這3個階段

校驗一般用來確認此二進制文件是否適合當前的JVM(版本),

準備就是爲靜態成員分配內存空間,。並設置默認值

解析指的是轉換常量池中的代碼作爲直接引用的過程,直到所有的符號引用都可以被運行程序使用(建立完整的對應關係)

完成之後,類型也就完成了初始化,初始化之後類的對象就可以正常使用了,直到一個對象不再使用之後,將被垃圾回收。釋放空間。

當沒有任何引用指向Class對象時就會被卸載,結束類的生命週期

將反射用於工廠模式

先來看看,如果不用反射的時候,的工廠模式吧:

http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144851.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * @author Rollen-Holt 設計模式之 工廠模式
 */
 
interface fruit{
    public abstract void eat();
}
 
class Apple implements fruit{
    public void eat(){
        System.out.println("Apple");
    }
}
 
class Orange implements fruit{
    public void eat(){
        System.out.println("Orange");
    }
}
 
// 構造工廠類
// 也就是說以後如果我們在添加其他的實例的時候只需要修改工廠類就行了
class Factory{
    public static fruit getInstance(String fruitName){
        fruit f=null;
        if("Apple".equals(fruitName)){
            f=new Apple();
        }
        if("Orange".equals(fruitName)){
            f=new Orange();
        }
        return f;
    }
}
class hello{
    public static void main(String[] a){
        fruit f=Factory.getInstance("Orange");
        f.eat();
    }
 
}

這樣,當我們在添加一個子類的時候,就需要修改工廠類了。如果我們添加太多的子類的時候,改的就會很多。

現在我們看看利用反射機制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package Reflect;
 
interface fruit{
    public abstract void eat();
}
 
class Apple implements fruit{
    public void eat(){
        System.out.println("Apple");
    }
}
 
class Orange implements fruit{
    public void eat(){
        System.out.println("Orange");
    }
}
 
class Factory{
    public static fruit getInstance(String ClassName){
        fruit f=null;
        try{
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
class hello{
    public static void main(String[] a){
        fruit f=Factory.getInstance("Reflect.Apple");
        if(f!=null){
            f.eat();
        }
    }
}

現在就算我們添加任意多個子類的時候,工廠類就不需要修改。

 

上面的代碼雖然可以通過反射取得接口的實例,但是需要傳入完整的包和類名。而且用戶也無法知道一個接口有多少個可以使用的子類,所以我們通過屬性文件的形式配置所需要的子類。

下面我們來看看: 結合屬性文件的工廠模式

首先創建一個fruit.properties的資源文件,

內容爲:

1
2
apple=Reflect.Apple
orange=Reflect.Orange

 然後編寫主類代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package Reflect;
 
import java.io.*;
import java.util.*;
 
interface fruit{
    public abstract void eat();
}
 
class Apple implements fruit{
    public void eat(){
        System.out.println("Apple");
    }
}
 
class Orange implements fruit{
    public void eat(){
        System.out.println("Orange");
    }
}
 
//操作屬性文件類
class init{
    public static Properties getPro() throws FileNotFoundException, IOException{
        Properties pro=new Properties();
        File f=new File("fruit.properties");
        if(f.exists()){
            pro.load(new FileInputStream(f));
        }else{
            pro.setProperty("apple""Reflect.Apple");
            pro.setProperty("orange""Reflect.Orange");
            pro.store(new FileOutputStream(f), "FRUIT CLASS");
        }
        return pro;
    }
}
 
class Factory{
    public static fruit getInstance(String ClassName){
        fruit f=null;
        try{
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
class hello{
    public static void main(String[] a) throws FileNotFoundException, IOException{
        Properties pro=init.getPro();
        fruit f=Factory.getInstance(pro.getProperty("apple"));
        if(f!=null){
            f.eat();
        }
    }
}

【運行結果】:Apple


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章