Java学习之反射技术

反射技术也算得上是Java基础知识里较高新的一项技术了(但反射不是java5的新特性),犹记得老师课上说过,当反射遇到Private时,会让我们深深地领悟到啥叫一“招”回到解放前。
如果将Java语言中的各种技术比喻成江湖上纷繁的门派,反射技术的存在总是给人一种隐秘而强悍的幽灵般的感觉,它让限制不再,让规矩死亡,颇有几分C中的goto给人的第一印象。今天讨论反射,首先从下面一个题开刀吧!

ArrayList list = new ArrayList(); 如何在这个泛型为Integer的ArrayList中存放一个String类型的对象。

我们来分析分析给泛型为Integer的ArrayList中添加一个String类型对象,为什么会添加不进去?是因为ArrayList只接受Integer类型,那么,我们就让它不只是接受Integer。如何让它不是只接受Integer呢?既然是接收,靠的是ArrayList中的add方法了。
所以我们需要通过反射将方法接受类型改变,
若要反射,首先要对象字节码,想获取方法,还需要一个ArrayList对象。
其实所谓的泛型,也就是在方法上对于接受的类型进行了一个限定。而反射方法和字段以及构造,字节码就是必须的。运行方法,对象也是必要的。只是构造本来就是new对象的,而获取字段和方法需要有字节码对象,运行,就要有对象参与。
但是涉及反射,一定需求注意的是反射的对象所被修饰的修饰符是什么?
如果是public修饰的,直接可以getMethod();
如果是默认的,就需要通过getDeclaredMethod();
如果是private的话,就要通过暴力反射了,就是要通getDeclaredMethod(),并设置me.setAccessible(true)来进行设置权限
上题的操作如下:

public class Test {
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> al = new ArrayList<Integer>();
		Class clazz = ArrayList.class; //获取字节码对象.
		Method me = clazz.getMethod("add", Object.class); //反射方法.
		me.invoke(al,"我是字符串"); //运行方法.
		System.out.println(al);		
	}
}

联系泛型可以解释:泛型的出现阻止了形形色色的元素混入,可是,有了反射,就好比让接受重新回到解放前。反射的确是让程序重新变得不安全,因此,对于特别修饰的private,我们需求慎重来考虑一下,毕竟private自然有private的道理。

反射的概念

反射就是把java类中的各种成分映射成相应的java类。一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示。
反射的基础:Class–>用来描述java中的类
如何得到各个字节码对应的实例对象
->类名.class 或者 类名.TYPE
->对象.getClass()
->Class.forName(“类名”)
总之,只要在源程序中出现的类型,都有各自的Class实例对象

String str1 = "abc";  
 //三种获取字节码的方法  
Class cls1 = str1.getClass();  
Class cls2 = String.class;  
Class cls3 = Class.forName("java.lang.String");  
System.out.println(cls1 == cls2);  //true
System.out.println(cls2 == cls3);  //true

反射的作用
反射机制最重要的内容——检查类的结构。
在java.lang.reflect包中有三个类Field、Method、Constructor分别用于描述类的域、方法和构造器。
这三个类共有方法:
getModifiers //返回一个整形数值,用不同的位开关描述public和static这样的修饰符使用状况
getName //用来返回项目的名称

构造方法的反射应用
constructor代表一个构造方法,constructor对象上的方法有:得到方法名字,得到所属类,产生实例对象。
无参:Constructor[] constructors =
Class.forName(“java.lang.String”).getConstructors();
有参:Constructor constructor =
Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

成员变量的反射:
Field类代表某个类中的一个成员变量 ,通过Field调用成员变量

public class ReflectPoint {  
private int x;  
public int y ;  
public ReflectPoint(int x, int y) {  
         super( );  
         this.x = x;  
         this.y = y;  
}  
}  

import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
public class ReflectTest {  
       public static void main(String[] args) throws Exception {  
          ReflectPoint rp = new ReflectPoint(3, 5);  
          Field fieldY = rp.getClass().getField( "y");  
          System. out.println(fieldY.get(rp));  
      }  
}  

如果想直接通过getField方法获取私有对象,会出现如下错误:java.lang.NoSuchFieldException : x
Field fieldX = rp.getClass().getDeclaredField(“x”);
fieldX.setAccessible(true);
这种方法称之为暴力反射,使用setAccessible(true)使private类型的成员变量也可以被获取。

成员方法的反射:

Method类用于描述类中的成员方法。
得到类中的某一个方法:Method charAt = Class.forName(“java.lang.String”)
.getMethod(“charAt”, int.class);
调用方法:
通常方法:System.out.println(str.charAt(1));
反射方法:System.out.println(charAt.invoke(str, 1));
注意:如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是静态方法!

框架的概念及用反射技术开发框架的原理:

框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架程序怎样调用以后写的类呢?很多时候程序无法知道被调用的类名,所以,在程序中无法直接new某个类的实例对象,而要用 反射 方式来做。

public class ReflectTest {    
     public static void main(String[] args) throws Exception {          
        InputStream is = new FileInputStream("config.properties" );  
        Properties props = new Properties();  
         props.load(is);  
              is.close();           
         String className = (String)props.get( "className");  
        Collection collections = (Collection)Class.forName(className).newInstance();           
        ReflectPoint pt1 = new ReflectPoint(3, 3);  
        ReflectPoint pt2 = new ReflectPoint(5, 5);  
        ReflectPoint pt3 = new ReflectPoint(3, 3);  
           
        collections.add(pt1);  
        collections.add(pt2);  
        collections.add(pt3);  
        collections.add(pt1);             
        System. out.println(collections.size());  
       }  
}  

className = java.util.ArrayList    //4
className = java.util.HashSet    //3

用类加载器的方式管理资源和配置文件:

方式一:采用类加载器进行加载,使用相对路径的方式
InputStream is = ReflectTest.class.getClassLoader().getResourceAsStream
(“com/mytest/config.properties”);
方式二:利用Class方式进行加载,使用相对路径的方式
nputStream is = ReflectTest.class.getResourceAsStream(“config.properties”);
方式三:利用Class方式进行加载,使用绝对路径的方式
InputStream is = ReflectTest.class .getResourceAsStream(“com/mttest/config.
properties”);

Array工具类 用于完成数组的反射操作:

public class ReflectTest {  
    public static void main(String[] args) throws Exception {  
           String[] a1 = new String[]{"a" ,"b" ,"c" };  
           String a2 = "xyz";               
            printObject(a1);  
            printObject(a2);  
      }       
      public static void printObject(Object obj){  
         Class clazz = obj.getClass();  
         if(clazz.isArray()){  
            int len = Array.getLength(obj);  
              for(int i = 0; i < len; i++){  
                 System. out.println(Array.get(obj, i));  
              }  
        } else{  
              System. out.println(obj);  
        }  
    }  
}  

ArrayList和HashSet的比较及Hashcode分析:

ArrayList 
Collection collections = new ArrayList( );  
ReflectPoint pt1 = new ReflectPoint(3, 3);  
ReflectPoint pt2 = new ReflectPoint(5, 5);  
ReflectPoint pt3 = new ReflectPoint(3, 3);  
collections.add(pt1);  
collections.add(pt2); 
collections.add(pt3);  
collections.add(pt1);  
 System. out.println(collections.size());  
  Collection collections = new ArrayList();  
ReflectPoint pt1 = new ReflectPoint(3, 3);  
ReflectPoint pt2 = new ReflectPoint(5, 5);  
ReflectPoint pt3 = new ReflectPoint(3, 3);  
collections.add(pt1);  
collections.add(pt2);  
collections.add(pt3);  
collections.add(pt1);  
System. out.println(collections.size());  //4  

HashSet: 
Collection collections = new HashSet();  
ReflectPoint pt1 = new ReflectPoint(3, 3);  
ReflectPoint pt2 = new ReflectPoint(5, 5);  
ReflectPoint pt3 = new ReflectPoint(3, 3);  
collections.add(pt1);  
collections.add(pt2);  
collections.add(pt3);  
collections.add(pt1);  
System. out.println(collections.size());  //3 

分析:
由以上两示例可以看到:
当集合为ArrayList时,其实质是一个数组,因此放入4个元素后,集合size为4。
当集合为HashSet时,需要通过比较hashcode值以及equals方法是否返回true决定是否放入。
如果hashcode值相等并且equals方法返回true,那么就不会放入。因此,集合size为3。
如果想让size为2,也就是pt1与pt3作为同一个元素存入HashSet集合,那就需要覆盖ReflectPoint类的hashCode方法以及equals方法。

总结:
反射在本质上就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等,明显反射技术的基石就是其class。所以在以后的编程工作中一定要清晰头脑,做到从字节码中反射出自己的思维。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值