• 反射 之 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

  • 相关阅读:
    pip或easy_install安装库报错:SSL: CERTIFICATE_VERIFY_FAILED
    js 闭包
    php 安装xdebug进行调试(phpstorm)
    Linux下一个最简单的不依赖第三库的的C程序(2)
    Linux下一个最简单的不依赖第三库的的C程序(1)
    windbg .net 程序的死锁检测 常用方法(个人备份笔记)
    自定义经纬度索引(非RTree、Morton Code[z order curve]、Geohash的方式)
    通过经纬度坐标计算距离的方法(经纬度距离计算)
    根据2个经纬度点,计算这2个经纬度点之间的距离(通过经度纬度得到距离)
    The version of SOS does not match the version of CLR you are debugging; SOS.dll版本不匹配; Dump文件不同环境mscordacwks.dll版本问题
  • 原文地址:https://www.cnblogs.com/robbychan/p/3786481.html
Copyright © 2020-2023  润新知