JAVA反射学习之——深入研究(反射与泛型)

本文介绍了如何使用Java反射技术实现代码的通用性,并探讨了反射带来的性能影响。此外,还详细讲解了如何利用反射绕过泛型检查及获取泛型信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVA反射学习之——深入研究(反射与泛型)

原创  2015年07月13日 15:25:35
JAVA反射学习之——深入研究(反射与泛型)

    通过上节的学习,了解了反射的基本功能,下面关于反射有如下几个需要注意的地方。在学习JAVA与泛型之前,先说两个小知识点。

    一、学会使用反射编写具有通用性功能的代码
    设计一个方法,将Object对像中的propertyName属性的值设置为value,方法声明如下:
    public void setProperty(Object obj, String propertyName, Object value);

    用反射,完成具有通用性的功能:
[java]  view plain  copy
  1. class Persion{  
  2.     private String name;  
  3.     private int age;  
  4.       
  5.     @Override  
  6.     public String toString()  
  7.     {  
  8.         return name + " --- " + age;  
  9.     }  
  10. }  
  11.   
  12. public class ReflectTest2  
  13. {  
  14.     public static void setProperty(Object obj,String propertyName,Object value)   
  15.            throws Exception  
  16.     {  
  17.           
  18.         //获得类的class文件对像  
  19.         Class clazz = obj.getClass();  
  20.           
  21.         //获得propertyName字段  
  22.         Field field = clazz.getDeclaredField(propertyName);  
  23.         //设置访问权限  
  24.         field.setAccessible(true);  
  25.         //给obj对像赋值  
  26.         field.set(obj, value);  
  27.     }  
  28.       
  29.     public static void main(String[] args) throws Exception   
  30.     {  
  31.         Persion p = new Persion();  
  32.         System.out.println(p);  
  33.       
  34.         setProperty(p,"name","刘德华");  
  35.         setProperty(p,"age",50);  
  36.           
  37.         System.out.println(p);    
  38.     }  
  39. }  
    从上面代码来看,利用反射 不但可以完成具有通用性的功能,而且,还是访问的private型字段,可见反射的好用之处。
    通过反射来获得配置文件的内容,如果配置文件中包含有类名,那我们就可以通过类名来获得类的class文件对像,从而调用该对像中的方法,如果应用层编写的是具有通用性的功能,那底层改变只需改一下配置文件就可以了。

    二、反射带来发灵活性,但同时也损失了性能
    下面通过一个测试来比较一下,对于同一个类,(1)利用成员方法进行普通赋值;(2)利用反射动态调用类方法
赋值;(3)取消访问权限检查后,再利用反射动态调用成员方法赋值。
[java]  view plain  copy
  1. class Persion1{  
  2.     private String name;  
  3.     private int age;  
  4.   
  5.     public void setName(String name){  
  6.         this.name=name;  
  7.     }  
  8. }  
  9.   
  10.   
  11. public class ReflectTest3  
  12. {  
  13.     public static void main(String[] args) throws Exception  
  14.     {  
  15.         Persion1 p = new Persion1();  
  16.           
  17.         //用普通的方法进行赋值操作  
  18.         long start = System.currentTimeMillis();  
  19.         for(int i = 0; i < 100000000L; i ++){  
  20.             p.setName("张三");  
  21.         }  
  22.         long end = System.currentTimeMillis();  
  23.         System.out.println("普通方法赋值1亿次,执行时间: " + (end-start) + "ms");  
  24.           
  25.         //利用反射动态赋值  
  26.         Class clazz = p.getClass();  
  27.         Method m = clazz.getDeclaredMethod("setName", String.class);  
  28.         long start1 = System.currentTimeMillis();  
  29.         for(int i = 0; i < 100000000L; i ++){  
  30.             m.invoke(p, "张三");  
  31.         }  
  32.         long end1 = System.currentTimeMillis();  
  33.         System.out.println("反射动态赋值1亿次,执行时间: " + (end1-start1) + "ms");  
  34.           
  35.         //利用反射禁止访问检查,动态赋值测试  
  36.         m.setAccessible(true);  
  37.         long start2 = System.currentTimeMillis();  
  38.         for(int i = 0; i < 100000000L; i ++){  
  39.             m.invoke(p, "张三");  
  40.         }  
  41.         long end2 = System.currentTimeMillis();  
  42.         System.out.println("反射取消访问检查,动态赋值1亿次,执行时间: " + (end2-start2) + "ms");  
  43.     }  
  44.   
  45. }  
     下面是执行结果:

    普通方法赋值1亿次,执行时间: 645ms
    反射动态赋值1亿次,执行时间: 48561ms
    反射取消访问检查,动态赋值1亿次,执行时间: 3043ms

    从上面执行结果来看,这几种方法之间的性能差异是具大的,所以在程序中对性能有要求的地方,比如循环处,关键代码段,能不使用反射就不要使用,即使用了,也可以关掉访问权限检查以提高性能。

    三、反射与泛型
    1. 使用反射越过泛型检查
    首先,这个知识点的背景是这样的,如果你创建了一个集合,限制传入Integer类型,但是后面使用时,你想传入String类型,怎么办呢?先看几行代码的例子:
[java]  view plain  copy
  1. public static void main(String[] args) throws Exception  
  2.     {  
  3.         //创建一个List只能接收Integer类型  
  4.         ArrayList<Integer> list = new ArrayList<Integer>();  
  5.           
  6.         list.add(100);  
  7.         // 以下代码会报错  
  8.         //list.add("hello");  
  9.           
  10.         //下面使用反射来越过泛型检查  
  11.         Class clazz = list.getClass();  
  12.         //获得List的add方法,在反射内部接收的都是Object对像  
  13.         Method add = clazz.getDeclaredMethod("add", Object.class);  
  14.         //执行add操作  
  15.         add.invoke(list, "hello");  
  16.         add.invoke(list, "world");  
  17.         add.invoke(list, "java se");  
  18.           
  19.         System.out.println(list);  
  20.     }  
     对于泛型,在编写程序时都是给编译器看的,可以查看class文件的反编译结果,发现是没有泛型的,所以从理论上来说,对于上面的list对像,可以存储任意类型,在反射内部处理中,都是处理的Object类型,所以就有了以上代码。

    2. 使用反射来获取泛型信息
    java采用泛型擦除的机制来引入泛型,也就是说,泛型仅仅是给编译器javac看的,来确保数据的安全性和免去数据类型转换,但是,一旦编译完成,所有和泛型相关的东西都被擦除,这一点也可以从类编译的class文件反编译看到。

    在实际应用中,为了获得和泛型有关的信息,Java就新增了几种类型来代表不能被归一到Class类中的类型,但又和基本数据类型齐名的类型,通常使用的是如下两个:
    GenericType: 表示一种元素类型是参数化的类型或者类型变量的数组类型。
    ParameterizedType: 表示一种参数化的类型。

    为什么要引入这两种呢,实际上,在通过反射获得成员变量时,Field类有一个方法是getType,可以获得该字段的属性,但是这种属性如果是泛型就获取不到了,所以才引入了上面两种类型。
    看下面一个例子:
[java]  view plain  copy
  1. class student  
  2. {  
  3.     private Map<String,Integer> score ;  
  4.       
  5. }  
  6.   
  7. public class ReflectTest5  
  8. {  
  9.     public static void main(String[] args) throws Exception  
  10.     {  
  11.         Class<student> clazz = student.class;  
  12.           
  13.         Field f = clazz.getDeclaredField("score");  
  14.           
  15.         //通过getType方法只能获得普通类型  
  16.         System.out.println("score的类型是:" + f.getType()); //打印Map  
  17.           
  18.         //1. 获得f的泛型类型  
  19.         Type gType = f.getGenericType();  
  20.           
  21.         //2.如果gType是泛型类型对像  
  22.         if(gType instanceof ParameterizedType)  
  23.         {  
  24.             ParameterizedType pType = (ParameterizedType)gType;  
  25.             //获取原始类型  
  26.             Type rType = pType.getRawType();  
  27.             System.out.println("原始类型是: " + rType);  
  28.               
  29.             //获得泛型类型的泛型参数  
  30.             Type[] gArgs = pType.getActualTypeArguments();  
  31.             //打印泛型参数  
  32.             for(int i=0; i < gArgs.length; i ++)  
  33.             {  
  34.                 System.out.println("第"+ i +"个泛型类型是:" + gArgs[i]);  
  35.             }  
  36.         }  
  37.         else{  
  38.             System.out.println("获取泛型信息失败");  
  39.         }  
  40.     }  
  41. }  

    Type是java.lang.reflect包下的一个接口,该接口代表所有类型的公共接口,Class是Type接口的实现类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值