什么是反射:
反射就是把java类中的各种成分映射成一个个的java对象(加载类,然后解刨出类的各个组成部分)。例如,一个类由:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
反射一般会用在什么地方:相信大家在学习JavaEE的时候用过很多框架,比如,spring框架中的IOC反向控制就是利用反射机制,其实大部分框架都会使用到反射,在举一个就是在设计模式中的动态代理,其实也是使用反射机制实现的。
反射的使用:
Class类:
在使用反射的一些方法之前相信,让我们先来了解Class这个类。大家在很多代码中都会遇到这个类,那么这个类到底表示的是什么呢?其实Class类是用来表示一个字节码的对象,大家应该都知道一个类从编译成.class文件到类加载器加载到内存的过程。其实Class类就是用来表示一个类在内存中字节码的对象。我们之所以要了解Class这个类,因为我们后面的反射方法都是基于这个类对象的。
如何得到某个class文件的class对象:
- 类名.class
Class clazz = ArrayList.class
- 对象.getClass()
Class clazz = new ArrayList().getClass()
- Class.forNme("类名")
Class clazz = Class.forName("java.util.ArrayList")
Constructor类:
Constructor类的实例对象代表类的一个构造函数。
1)如果我们想得到某个类的所有构造方法,例:
Constructor [] constructors = Class.forName("java.lang.String").getConstructors();
2)如果我们想得到某个类的特定构造方法,例:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class)
3)让你想不到是我们可以利用构造方法创建实例对象:
String str = (String)constructor.newInstance("abc");
4)Class类的newInstance()方法也可以创建类的实例,其内部工作原理就是先得无参构造方法
构造方法,再用构造方法创建实例对象:
String obj = (String)Class.forName("java.lang.String").newInstance();
Field类:
Field类代表某各类中一个成员变量。
这里就有一个问题等着大家,得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?这里特别解释一下,这里Field对象代表的是变量的定义,而不是具体的变量的值。这里给出Student类的定义:
public class Student {
public String name = "张三";
private int age;
}
1)操作公共变量
public void test1() throws Exception{
Student stu = new Student();
Class clazz = Student.class;
Field f = clazz.getField("name");//如果前面说的只是获取的name变量的定义
System.out.println(f.get(stu)); //这一句使用来获取特定的对象也就是stu这个对象的name值
f.set(stu, "李四"); //这一句是给stu这个对象的name赋值
Field f1 = clazz.getField("name");
System.out.println(f1.get(stu));
}
2)操作私有变量
public void test2() throws Exception{
Student stu = new Student();
Class clazz = Student.class;
Field f = clazz.getDeclaredField("age");//注意与公共成员变量方法的不同
f.setAccessible(true); //这一句不能少,意义及时获取权限
f.set(stu,18);
System.out.println(f.get(stu));
}
Method类
Method这个类代表某个类中的一个成员方法
1)得到类中的某一个方法:
eg: Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
这里getMethod()的第一个参数表示方法名称,第二个参加表示方法参数。
2)调用方法:
- 通常方式:System.out.println(str.charAt(1));
- 反射方式:System.out.println(charAt.invoke(str,1)); 上面的str表示一个String对象。这里如果传递给Method对象的第一个参数为null的话,那么就说明该Method对象对应的是一个静态方法。
3)注意JDK1.4和JDK1.5的invoke方法的区别
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),
Jdk1.4:public Object invoke(Object obj,Object[] args),
这里我们用一个例子来说明这个差别及解决办法,假设我们要写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。我们知道启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语解释,因此会出现参数类型不对的问题。解决办法就是
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});这样我们就能解决上面的问题。