反射 之 Class 類的經常使用方法

http://blog.csdn.net/dream_broken/article/details/8830489java

 反射中,最基礎的是對Class類的瞭解和使用。在JAVA中Object是一切類的父類,而getClass()方法是Object中定義的,以下spring

[java]  view plain copy
  1. public final native Class<?> getClass();  

       那麼能夠這麼說,全部類的對象實際上都是Class類的實例。若是你對類加載及JVM方法區有所瞭解,這個應該很容易理解。數組

       本文主要是寫點代碼認識Class類的一些經常使用方法。app

1.獲取Class對象框架

      在Class類中,只定義了個私有的構造方法,這意味着,沒法經過new Class()方式建立一個Class對象。eclipse

[java]  view plain copy
  1. /* 
  2.      * Constructor. Only the Java Virtual Machine creates Class 
  3.      * objects. 
  4.      */  
  5.     private Class() {}  


      雖然沒法直接使用new Class()方式建立對象,可是Class類中提供了forName()方法,經過它仍然能夠得到Class對象。ide

[java]  view plain copy
  1. public static Class<?> forName(String className)   
  2.                 throws ClassNotFoundException {  
  3.         return forName0(className, true, ClassLoader.getCallerClassLoader());  
  4.     }  
  5.   
  6.   
  7.   
  8.   
  9. public static Class<?> forName(String name, boolean initialize,  
  10.                    ClassLoader loader)  
  11.         throws ClassNotFoundException  


        除了使用forName()方法得到Class對象外,上面說過了Object是全部類的父類,而Object中有getClass()方法,因此經過"類名.getClass()"也能夠得到Class對象,也能夠經過「類名.class"工具

[java]  view plain copy
  1. package test;  
  2.   
  3. public class A1 {  
  4.       
  5. }  
  6.   
  7.   
  8. /** 
  9.  * 獲取Class對象 
  10.  */  
  11. <span style="font-size:18px;">public class A2 {  
  12.       
  13.     public static void main(String[] args){  
  14.         Class<?> c1=null;  
  15.         Class<?> c2=null;  
  16.         Class<?> c3=null;  
  17.         try {  
  18.          c1=Class.forName("test.A1");//經過forName()獲取  
  19.          c2=A1.class;//經過"類名.class"獲取  
  20.          A1 a1=new A1();  
  21.          c3=a1.getClass();//經過"對象.getClass()"獲取  
  22.         } catch (ClassNotFoundException e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.         System.out.println("類路徑:"+c1.getName());  
  26.         System.out.println("類路徑:"+c2.getName());  
  27.         System.out.println("類路徑:"+c3.getName());  
  28.     }  
  29. }</span>  


運行結果測試

類路徑:test.A1
類路徑:test.A1
類路徑:test.A1
ui

       經過運行結果可知,3種實例化Class對象的方式是同樣的,可是使用forName()是較爲經常使用的一種(固然,若是使用Hibernate或spring等框架時,常常使用"類.class「方式傳送一個JavaBean實體)。

     

   下面先看過設計上比較醜陋的例子。

[java]  view plain copy
  1. <span style="font-size:18px;">package test;  
  2. /** 
  3.  * 水果類接口 
  4.  * 全部水果必須實現此接口 
  5.  * 
  6.  */  
  7. public interface Fruit {  
  8.     public void say();  
  9. }  
  10.   
  11.   
  12.   
  13. package test;  
  14. /** 
  15.  * 蘋果類,實現接口Fruit 
  16.  * 
  17.  */  
  18. public class Apple implements Fruit {  
  19.     @Override  
  20.     public void say() {  
  21.         System.out.println("hello,I'm apple!");  
  22.     }  
  23. }  
  24.   
  25.   
  26.   
  27. /** 
  28.  * 香蕉類,實現接口Fruit 
  29.  * 
  30.  */  
  31. public class Banana implements Fruit {  
  32.   
  33.     @Override  
  34.     public void say() {  
  35.         System.out.println("hello,I'm banana!");  
  36.     }  
  37. }  
  38.   
  39.   
  40.   
  41.   
  42. package test;  
  43. /** 
  44.  * 水果工具類 
  45.  * 
  46.  */  
  47. public class FruitUtil {  
  48.   
  49.     public static Fruit createFruit(String fruitName){  
  50.         if("apple".equalsIgnoreCase(fruitName)) return (Fruit)new Apple();  
  51.         if("banana".equalsIgnoreCase(fruitName)) return (Fruit)new Banana();  
  52.         return null;  
  53.     }  
  54. }  
  55.   
  56.   
  57.   
  58. package test;  
  59. /** 
  60.  * 測試類 
  61.  * 
  62.  */  
  63. public class Test {  
  64.       
  65.     public static void main(String[] args){  
  66.         Fruit f=FruitUtil.createFruit("apple");  
  67.         if(f!=null) {  
  68.             f.say();  
  69.         }else{  
  70.             System.out.println("沒有水果!");  
  71.         }  
  72.     }  
  73. </span>}  

運行結果

hello,I'm apple!

 

      代碼沒錯,運行結果也沒錯,我之因此說它醜陋,是從代碼擴展性方面考慮。好比,若是我要再添加一種水果呢?那麼就必須修改FruitUtil.createFruit()方法中的代碼了,增長if判斷。那若是我要新曾幾十種水果呢?那是否是要寫幾十個if判斷。。。。。做爲一個接觸JAVA兩年的菜鳥的我,都以爲代碼設計不友好了,更別說修改原有代碼對系統的危害了。那有沒有一種方法,當新增水果時,不須要對原有代碼作任何修改呢?有,這時,Class.forName()一聲大哄,粉墨登場了。

[java]  view plain copy
  1. <span style="font-size:18px;">package test;  
  2. /** 
  3.  * 水果類接口 
  4.  * 全部水果必須實現此接口 
  5.  * 
  6.  */  
  7. public interface Fruit {  
  8.     public void say();  
  9. }  
  10.   
  11.   
  12. package test;  
  13. /** 
  14.  * 蘋果類,實現接口Fruit 
  15.  * 
  16.  */  
  17. public class Apple implements Fruit {  
  18.     @Override  
  19.     public void say() {  
  20.         System.out.println("hello,I'm apple!");  
  21.     }  
  22. }  
  23.   
  24.   
  25.   
  26. package test;  
  27. /** 
  28.  * 香蕉類,實現接口Fruit 
  29.  * 
  30.  */  
  31. public class Banana implements Fruit {  
  32.   
  33.     @Override  
  34.     public void say() {  
  35.         System.out.println("hello,I'm banana!");  
  36.     }  
  37. }  
  38.   
  39.   
  40. package test;  
  41. /** 
  42.  * 水果工具類 
  43.  * 
  44.  */  
  45. public class FruitUtil {  
  46.   
  47.     public static Fruit createFruit(String classPath)throws Exception{  
  48.         Fruit fruit=null;  
  49.         try {  
  50.             fruit=(Fruit)Class.forName(classPath).newInstance();  
  51.         } catch (Exception e) {  
  52.             e.printStackTrace();  
  53.             throw new Exception("建立水果失敗!");  
  54.         }   
  55.         return fruit;  
  56.     }  
  57. }  
  58.   
  59.   
  60. package test;  
  61.   
  62. import java.io.File;  
  63. import java.io.FileInputStream;  
  64. import java.util.Properties;  
  65.   
  66. /** 
  67.  * 測試類 
  68.  * 
  69.  */  
  70. public class Test {  
  71.   
  72.     private final static String FRUIT_CONF_PATH=System.getProperty("user.dir");  
  73.     public static void main(String[] args){  
  74.         Properties p=new Properties();  
  75.         try {  
  76.             //在eclipse下創建普通JAVA項目,src寫的內容放在bin下了  
  77.             FileInputStream in=new FileInputStream(new File(FRUIT_CONF_PATH+File.separator+"bin"+File.separator+"fruit.properties"));  
  78.             p.load(in);  
  79.             String fruitClassPath=p.getProperty("apple");  
  80.             p.clear();  
  81.             in.close();  
  82.             Fruit f=FruitUtil.createFruit(fruitClassPath);  
  83.             f.say();  
  84.         } catch (Exception e) {  
  85.             e.printStackTrace();  
  86.             System.out.println("獲取水果實例失敗!");  
  87.         }  
  88.     }  
  89. }</span>  

fruit.properties配置文件


[java]  view plain copy
  1. apple=test.Apple  
  2. banana=test.Banana  


 運行結果

hello,I'm apple!

 

    這樣改寫後,之後有新增水果時,只要編寫新增水果類,並再配置文件中配置新水果的類路徑就OK了,原有的代碼不須要修改。

這樣的設計就具備很好的擴展性。

 

2.獲取類中的成員變量

       getFields():得到類(包括父類)的public成員變量

       getDeclaredFields():得到類(不包括父類)的所有成員變量

   

 

[java]  view plain copy
  1. package test;  
  2.   
  3. public class A {  
  4.   
  5.     public int n_A;  
  6.     private String s_A;  
  7.     protected double d_A;  
  8. }  
  9.   
  10. package test;  
  11.   
  12. public class A1 extends A {  
  13.     public int n_A1;  
  14.     private String s_A1;  
  15.     protected double d_A1;  
  16.       
  17. }  
  18.   
  19. package test;  
  20.   
  21. import java.lang.reflect.Field;  
  22.   
  23. public class A2 {  
  24.     public static void main(String[] args){  
  25.         Class<?> c1=null;  
  26.         try {  
  27.          c1=Class.forName("test.A1");//經過forName()獲取A1的Class對象  
  28.          Field[] f1=c1.getFields();//A1(包括父類)中的public成員變量  
  29.          for(Field f:f1)  
  30.              System.out.println("A1(包括父類)類中public 成員變量:"+f.getName());  
  31.          System.out.println("獲取A1(不包括父類)類中的全部成員變量::::::::");  
  32.          Field[] f2=c1.getDeclaredFields();//A1中的全部成員變量  
  33.          for(Field f:f2)  
  34.              System.out.println("A1(不包括父類)類中的成員變量:"+f.getName());  
  35.         } catch (Exception e) {  
  36.             e.printStackTrace();  
  37.         }  
  38.           
  39.     }  
  40.   
  41. }  


運行結果

A1(包括父類)類中public 成員變量:n_A1
A1(包括父類)類中public 成員變量:n_A
獲取A1(不包括父類)類中的全部成員變量::::::::
A1(不包括父類)類中的成員變量:n_A1
A1(不包括父類)類中的成員變量:s_A1
A1(不包括父類)類中的成員變量:d_A1

3.獲取類中的方法

      getMethods():獲取類(包括父類)中的public方法(不包括構造方法)

      getDeclaredMethods():獲取本類(不包括父類)中的全部方法(不包括構造方法)

      getConstructors():獲取本類(不包括父類)中的全部public構造方法

     考慮到篇幅問題,就不貼代碼了。

4.實例化對象

[java]  view plain copy
  1. <span style="font-size:18px;">package test;  
  2.   
  3. public class A1  {  
  4.     private int n;  
  5.       
  6.     public A1(){  
  7.         this.n=0;  
  8.     }  
  9.     public A1(int n){  
  10.         this.n=n;  
  11.     }  
  12.       
  13.     public int getN() {  
  14.         return n;  
  15.     }  
  16.     public void setN(int n) {  
  17.         this.n = n;  
  18.     };  
  19. }  
  20.   
  21.   
  22. package test;  
  23.   
  24. import java.lang.reflect.Constructor;  
  25.   
  26. public class A2 {  
  27.     public static void main(String[] args){  
  28.         Class<?> c1=null;  
  29.         try {  
  30.          c1=Class.forName("test.A1");//經過forName()獲取A1的Class對象  
  31.          A1 a1=(A1)c1.newInstance();//使用這種實例化方式,則A1中必須有無參構造方法  
  32.          System.out.println("使用Class類中的newInstance()方法實例化,a1.n="+a1.getN());  
  33.          Constructor<?>[] con=c1.getConstructors();//類中存在多個構造方法時,數組順序和類中寫構造方法的順序一致  
  34.          A1 a2=(A1)con[0].newInstance();//A1中也必須存在無參構造方法  
  35.          System.out.println("使用Constructor類中的public T newInstance(Object ... initargs)方法實例化,a2.n="+a2.getN());  
  36.          A1 a3=(A1)con[1].newInstance(10);//A1中能夠不存在無參構造方法,或者無參構造方法用private修飾時  
  37.          System.out.println("使用Constructor類中的public T newInstance(Object ... initargs)方法實例化,a3.n="+a3.getN());  
  38.            
  39.         } catch (Exception e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.           
  43.     }  
  44.   
  45. }</span>  

運行結果

使用Class類中的newInstance()方法實例化,a1.n=0
使用Constructor類中的public T newInstance(Object ... initargs)方法實例化,a2.n=0
使用Constructor類中的public T newInstance(Object ... initargs)方法實例化,a3.n=10

5.經過反射調用類中的方法

[java]  view plain copy
  1. <span style="font-size:18px;color:#000000;">package test;  
  2.   
  3. public class A {  
  4.   
  5.     public void sayHello(){  
  6.         System.out.println("hello,world");  
  7.     }  
  8.     public void sayHello(String name){  
  9.         System.out.println("hello,"+name);  
  10.     }  
  11. }  
  12.   
  13. package test;  
  14.   
  15. import java.lang.reflect.Method;  
  16.   
  17. public class Test01 {  
  18.   
  19.     public static void main(String[] args){  
  20.         Class<?> c=null;  
  21.         try {  
  22.          c=Class.forName("test.A");//經過forName()獲取A的Class對象  
  23.          A a=(A)c.newInstance();  
  24.         Method m1=c.getMethod("sayHello");//得到無參數的syaHello方法  
  25.         m1.invoke(a);  
  26.         Method m2=c.getMethod("sayHello", String.class);//得到只含有String類型參數的sayHello方法  
  27.         m2.invoke(a, "everyOne");  
  28.         } catch (Exception e) {  
  29.             e.printStackTrace();  
  30.         }  
  31.     }  
  32. }</span>  

運行結果

hello,world
hello,everyOne


6.經過反射破壞類的封裝性(給私有變量賦值並訪問)

[java]  view plain copy
  1. <span style="font-size:18px;">package test;  
  2.   
  3. public class User {  
  4.   
  5.     private String name;  
  6.     private int age;  
  7.     public String getName() {  
  8.         return name;  
  9.     }  
  10.     public int getAge() {  
  11.         return age;  
  12.     }  
  13.       
  14. }  
  15.   
  16.   
  17. package test;  
  18.   
  19. import java.lang.reflect.Field;  
  20. /** 
  21.  * 經過反射訪問類的私有變量 
  22.  * 
  23.  */  
  24. public class Test01 {  
  25.   
  26.     public static void main(String[] args){  
  27.         Class<?> c=null;  
  28.         try {  
  29.          c=Class.forName("test.User");//經過forName()獲取A的Class對象  
  30.          User user=(User)c.newInstance();  
  31.         Field name=c.getDeclaredField("name");//得到name變量  
  32.         Field age=c.getDeclaredField("age");//得到age變量  
  33.         name.setAccessible(true);//將name屬性設置成可被外部訪問  
  34.         age.setAccessible(true);//將age變量設置成可被外部訪問  
  35.         name.set(user, "張三");  
  36.         age.set(user, 20);  
  37.         System.out.println("姓名:"+name.get(user)+"   "+user.getName());  
  38.         System.out.println("年齡:"+age.get(user)+"   "+user.getAge());  
  39.         } catch (Exception e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.     }  
  43. }</span>  

運行結果

姓名:張三   張三 年齡:20   20

相關文章
相關標籤/搜索