今天复习了Java的反射机制,说实话,对于这方面的知识自己以前只是了解一点,并没有透彻的研究理解过,今天通过听张老师的视频,对java反射机制有了整体上的一个理解,反射就是把java中的各种成分映射成各种java类(真是类外有类啊),
java反射机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
Class类:代表一个类。
Constructor 类:代表类的构造方法。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法
一、Class类 ( Class类,反射的基石,)
Class类是Reflection API 中的核心类,它有以下方法
getName():获得类的完整名字。
getFields():获得类的public类型的属性。
getDeclaredFields():获得类的所有属性,
getMethods():获得类的public类型的方法。
getDeclaredMethods():获得类的所有方法。
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数 指定方法的参数类型。
getConstructors():获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
二、Constructor类
1、获得某个类的所有构造方法:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
2、获得某个类的某个构造方法:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);//获得构造方法时要指明参数类型,注意,在编译阶段编译器并不知道constructor是谁的构造方法,只有到程序运行时才知道是String的构造方法。
3、创建实例对象方式有两种:
常用方式:String str = new String(new StringBuffer("itcast"));
反射方式:String str = (String)constructor.newInstance(new StringBuffer("heima"));//注意要强制类型转换,另外注意传递参数的一致性,即得到构造方法的参数类型与实例化构造方法的参数类型要一致,若将上述方式改成这样,String str = (String)construtor.newInstance("CSDN");这样的话,编译期虽然能够通过,但是运行时会报类型不匹配的异常。
三、Filed类
public class Point{
private int x;
public int y;
............这里为xy的构造方法
}
public class TestReflect{
............只写了相关代码,部分代码省略
Point p = new Point(12,3);
Field fieldY = p.getClass().getField("y");
System.out.println(fieldY.get(p));//打印出来应该为12,
System.out.println(fieldY);//打印出来为变量的定义,我这里是 public int com.itcast.Test.Point.y
Field fieldX = p.getClass.getDeclaredField("x");//因为x声明为私有变量,要用getDeclaredField(type arg)才能拿到,
fieldX.setAccessible(true);//由于x为私有变量,对上面拿到的fieldX还要进行设置可访问后才能够用,这也叫暴力反射
System.out.println(fieldX.get(p));
............
}
注意fieldY不是对象p上的变量,而是代表字节码文件上的一个变量,所以取变量x的值应该到对象p上去取,如fieldY.get(p)
四、 Method类
Class.getMethod方法的时候接受哪些参数?显然要一个方法名,可以是同名的方法有多个,这又怎么识别出哪一个呢?这就需要靠参数类型和参数个数了。参数类型用什么来表示啊?用Class,例如,int.class,String.class等
Method.invoke()接受哪些参数,如果一个参数为null,表示的是调用这个方法不需要对象,从侧面说明了是静态方法!
题目:要你写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
public class TestReflect{
..........部分代码略
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
//mainMethod.invoke(null, new Object[]{new String[]{"a","b","c"}});
mainMethod.invoke(null, (Object)new String[]{"a","b","c"});
...........
class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
.............
}
}因为方法的重载,main方法可能会有多个,到底选哪一个呢?getMethod("main", args);中的第二个参数,就是指定要选择的那个main方法的参数类型,根据参数类型与个数,Method就知道去调用哪一个main方法了。
由于我们知道main方法的参数是String [] args,所以,String[].class来表示他的类型,mainMethod.invoke(null, (Object)new String[]{"a","b","c"});中的第一个参数是null,不是一个对象,那说明不用对象即调用这个方法,那么,这个方法应该是静态方法,第二个参数,就是传递给main方法的参数。
在给main方法传递参数时,不能使用如下代码mainMethod.invoke(null, new new String[]{"a","b","c"}});虽然这符合jdk1.5的语言要求,但是,jdk把它理解成了jdk1.4,对这个String数组进行了拆包,所以,出现了参数类型不对的问题。对于传数组的情况,只能按jdk1.4的语法进行调用,因为如果参数类型就是Object,它是没有办法区别的。
五、Array类
Array工具类用于完成对数组的反射操作
public class TestReflect{
........
public static void main(String[] args){
....
String [] a1 = new String[]{"a","b","c"};
printObject(a1);
printObject("abc");
....
}
private static void printObject(Object obj) {
Class clazz = obj.getClass();
if(clazz.isArray()){ //判断传入的对象是不是数组,
int len = Array.getLength(obj); //利用反射工具类Array得到数组的长度
for(int i=0;i<len;i++){ //循环遍历数组
System.out.println(Array.get(obj, i)); //拿到数组的第i个值。
}
}else{
System.out.println(obj);
}
......
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lganggang131/archive/2010/12/28/6103893.aspx