反射 之 Class 类的常用方法

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

 反射中,最基础的是对Class类的了解和使用。在JAVA中Object是一切类的父类,而getClass()方法是Object中定义的,如下

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

       那么可以这么说,所有类的对象实际上都是Class类的实例。如果你对类加载及JVM方法区有所了解,这个应该很容易理解。

       本文主要是写点代码认识Class类的一些常用方法。

1.获取Class对象

      在Class类中,只定义了个私有的构造方法,这意味着,无法通过new Class()方式创建一个Class对象。

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


      虽然无法直接使用new Class()方式创建对象,但是Class类中提供了forName()方法,通过它仍然可以获得Class对象。

[java] view plaincopy
  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 plaincopy
  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

       通过运行结果可知,3种实例化Class对象的方式是一样的,但是使用forName()是较为常用的一种(当然,如果使用Hibernate或spring等框架时,经常使用"类.class“方式传送一个JavaBean实体)。

     

   下面先看过设计上比较丑陋的例子。

[java] view plaincopy
  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 plaincopy
  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 plaincopy
  1. apple=test.Apple  
  2. banana=test.Banana  


 运行结果

hello,I'm apple!

 

    这样改写后,以后有新增水果时,只要编写新增水果类,并再配置文件中配置新水果的类路径就OK了,原有的代码不需要修改。

这样的设计就具有很好的扩展性。

 

2.获取类中的成员变量

       getFields():获得类(包括父类)的public成员变量

       getDeclaredFields():获得类(不包括父类)的全部成员变量

   

 

[java] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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

posted @ 2014-06-12 22:03  小尼人00  阅读(564)  评论(0编辑  收藏  举报