一:什么是反射
正常情况下,如果已经有一个类,那么肯定可以通过类创建对象;那么如果现在要求通过一个对象找到一个类的名称,此时就需要用到反射技术。所以,反射就是加载类,并解析出类的各个组成部分。
二:反射的实现
我们先来看一个实例:
package haizhu.com; class X{ } public class GetClassDemo01{ public static void main(String[] args) { X x = new X(); System.out.println(x.getClass().getName()); System.out.println(x.getClass().getSimpleName()); } }
结果:
haizhu.com.X X
从程序中,可以发现,通过一个对象得到了对象所在的完整的“包.类”名称,当然也可以只得到类名称,那么getClass()方法是从哪里来的呢?这是因为,任何一个类如果没有明确声明继承自哪个父类时,默认继承Object类,所以getClass()方法是Object类中的方法,此类的方法定义如下:
public final class getClass()
以上方法返回一个Class类,这个类就是反射的源头。Class类中没有定义任何的构造方法,所以如果要使用,则必须首先通过forName()方法实例化对象,就是将这个类的字节码加载到内存中,比如下面的例子中,“haizhu.com.X”这个类,使用forName()方法之后,字节码就加载到内存中了。除了forName()之外,也可以使用“类.class”或者“对象.getClass()”方法实例化:
范例2:
package haizhu.com; class X{ } public class GetClassDemo02{ public static void main(String[] args) { Class<?> c1 = null; Class<?> c2 = null; Class<?> c3 = null; try{ c1 = Class.forName("haizhu.com.X"); //通过 forName() 静态方法实例化 }catch(ClassNotFoundException e){ e.printStackTrace(); } c2 = new X().getClass(); //通过 Object 类的 getClass() 方法实例化 c3 = X.class; //通过 类.class 实例化 System.out.println(c1.getClass().getName()); System.out.println(c2.getClass().getName()); System.out.println(c3.getClass().getName()); } }
总的来说,反射主要跟两个类有关系,Object 类 和Class 类。
三:反射的用途
反射主要用在框架中,比如,你将一个类的路径配置在xml等配置文件中,就可以通过反射技术进行类的加载。反射得到这个对象的类之后,可以继续解析出这个类的构造函数、基本方法、属性。
范例:公共JAVA类——Person.java
package com.haizhu.reflect; import java.io.InputStream; import java.util.List; public class Person { // ************************ 属性 ***************************** private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // ************************ 构造方法 ***************************** //注意,这里的访问权限默认值,如果想用反射取得构造函数,需要使用 getDeclaredConstructor 方法获取这个构造方法 Person(){ } Person(String name){ this.name=name; } Person(String name,int age){ this.name=name; this.age=age; } // ************************ 基本方法 ***************************** public void personFunction1(){ System.out.println("无参方法"); } public void personFunction2(String name,int age){ System.out.println(name+":"+age); } public static void personFunction3(int num){ // 静态方法 System.out.println(num); } private void personFunction4(InputStream in){ // 私有方法 System.out.println(in); } public String upString(String str){ String upStr = str.toUpperCase(); return upStr; } // ************************ main 方法 ***************************** public static void main(String args[]){ System.out.println("main() 方法"); } }
1、通过反射获得构造函数
ReflectConstructor.java
package com.haizhu.reflect; import java.lang.reflect.Constructor; public class ReflectConstructor { public void function1() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Constructor<?> c = clazz.getDeclaredConstructor(); System.out.println("无参构造函数:"+c.toString()); c.setAccessible(true); // 这里得到的是无参构造,不能传入参数 Person p = (Person) c.newInstance(); p.setName("无参"); System.out.println(p.getName()); } public void function2() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Constructor<?> c = clazz.getDeclaredConstructor(String.class); System.out.println("String 构造函数:"+c.toString()); c.setAccessible(true); Person p = (Person) c.newInstance("海竹"); System.out.println(p.getName()); } public void function3() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Constructor<?> c = clazz.getDeclaredConstructor(String.class,int.class); System.out.println("String & age 构造函数:"+c.toString()); c.setAccessible(true); // 这里得到的是构造函数应该依次按照类型传入两个参数 Person p = (Person) c.newInstance("海竹",26); System.out.println(p.getName()+"、"+p.getAge()); } public void functionTotal() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Constructor<?>[] cs = clazz.getDeclaredConstructors(); System.out.println("一共有构造函数:"+cs.length+" 个"); } public static void main(String[] args) throws Exception{ ReflectConstructor rc = new ReflectConstructor(); rc.function1(); System.out.println("******************************************"); rc.function2(); System.out.println("******************************************"); rc.function3(); System.out.println("******************************************"); rc.functionTotal(); } }
2、通过反射获得属性
ReflectField.java
package com.haizhu.reflect; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectField { /** * 根据属性名称获得(私有)属性 */ public void function1() throws Exception{ Person p = new Person(); // 因为是反射,正常来说应该使用反射得到Person类的构造方法,再进行实例化,这里直接创建简化程序 Class<?> c = Class.forName("com.haizhu.reflect.Person"); // 得到 "name" 这个属性 Field f = c.getDeclaredField("name"); // 将私有属性设置为外部可以访问 f.setAccessible(true); // Field 类 继承 AccessibleObject 类中的方法:将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 // 获取 name 的 value,直接 "get(属性名称)" 就可以取得这个属性的值 Object value = f.get(p); // 获取 name 的数据类型:type,注意这是一个 class 类表示的数据类型 Class<?> type = f.getType(); System.out.println("属性类型:"+type); // 转型:类型转换之前先判断 —— String.class if(type.equals(String.class)){ // 转型 String val = (String)value; System.out.println("属性值:"+val); } } /** * 根据方法名称和传入的参数获得需要的方法 */ public void function2() throws Exception{ Person p = new Person(); Class<?> c = Class.forName("com.haizhu.reflect.Person"); // 找到的方法是一个方法对象"Method对象":需要方法名称和传入的参数(如果多个参数,按照顺序依次输入),共同锁定一个方法,如果没有这个方法,会抛出异常 Method mSet = c.getMethod("setName",String.class); // 如果不传入参数的话,就找不需要参数的setName()方法,而只有setName(String str)方法的话就会报错 // 找到的方法是一个方法对象"Method对象":这里所需要的方法,没有传入参数,只需要一个方法名就可以了 Method mGet = c.getMethod("getName"); // 如果传入参数,也会找不到,会抛出异常 // 使用invoke()执行得到的方法:没有返回值 mSet.invoke(p, "小明"); // 使用方法对象调用invoke(),这样才能执行这个方法对象对应的方法 // 使用invoke()执行得到的方法:通过getter方法,获取属性值 String name = (String)mGet.invoke(p); System.out.println("通过getName()方法得到name:"+name); // ****************** 通过 获取属性,获取属性值 ****************** Field f = c.getDeclaredField("name"); f.setAccessible(true); Object value = f.get(p); System.out.println("通过取得name属性得到name:"+value); } public static void main(String[] args) throws Exception{ ReflectField rf = new ReflectField(); rf.function1(); System.out.println("=================================================================="); rf.function2(); System.out.println("=================================================================="); rf.function1(); } } // 上面的例子可以看出,在方法2中赋值之后,立即调取get方法是可以取得name值的,但是调取方法1是不能获取name值的。 // 这是因为在每个方法中,都有一个Person的构造方法,这个构造方法创建的新的对象,不是同一个name属性了。
3、通过反射获取普通方法
package com.haizhu.reflect; import java.io.FileInputStream; import java.io.InputStream; import java.lang.reflect.Method; public class ReflectMethod { public void function1() throws Exception{ Person p = new Person(); // 因为是反射,正常来说应该使用反射得到Person类的构造方法,再进行实例化,这里直接创建简化程序 Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); // 这里需要的方法是 public 权限声明,所以使用 getMethod 方法即可,对于没后访问权限的构造方法,需要使用 getDeclaredMethod 方法 Method m = clazz.getMethod("personFunction1"); m.invoke(p); } public void function2() throws Exception{ Person p = new Person(); Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Method m = clazz.getMethod("personFunction2",String.class,int.class); m.invoke(p, "小明",25); } public void function3() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Method m = clazz.getMethod("personFunction3",int.class); m.invoke(null,25); // 因为是静态方法,所以不用实例化对象就可以直接调用,当然如果有的话也是能用的 } public void function4() throws Exception{ Person p = new Person(); Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Method m = clazz.getDeclaredMethod("personFunction4",InputStream.class); m.setAccessible(true); m.invoke(p,new FileInputStream("d:\\test.txt")); } public void function5() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Method m = clazz.getDeclaredMethod("main",String[].class); //m.invoke(null,new String[]{"a","b"}); 这个会报参数个数错误,因为会把String[]拆分为a,b作为两个参数 m.invoke(null,(Object)new String[]{"a","b"}); // 这是一种方法 m.invoke(null,new Object[]{new String[]{"a","b"}}); // 这是另外一种方法 } public static void main(String[] args) throws Exception{ ReflectMethod rm = new ReflectMethod(); rm.function1(); rm.function2(); rm.function3(); rm.function4(); rm.function5(); } }
4、通过反射获得main方法
5、反射的小Demo
ReflectDemo.java
package com.haizhu.reflect; import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectDemo { /** * 通过反射取得构造方法,之后再使用这个构造放方法产生的对象,取得属性和方法。 * 这里是根据 方法名 取得方法 */ public void function1() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); // 这里需要的方法是 public 权限声明,所以使用 getMethod 方法即可,对于没后访问权限的构造方法,需要使用 getDeclaredMethod 方法 Constructor<?> con = clazz.getDeclaredConstructor(); // 放开构造函数的访问权限 con.setAccessible(true); Person p = (Person) con.newInstance(); // 取得 setName,getName 方法 Method setM1 = clazz.getDeclaredMethod("setName", String.class); Method getM1 = clazz.getDeclaredMethod("getName"); // 取得 setAge,getAge 方法 Method setM2 = clazz.getDeclaredMethod("setAge", int.class); // 这里,int 和Integer 不是同意类,不能混着用 Method getM2 = clazz.getDeclaredMethod("getAge"); // 执行setName,getName 方法 setM1.invoke(p, "haizhu"); setM2.invoke(p, 26); String name = (String) getM1.invoke(p); Integer age = (Integer) getM2.invoke(p); System.out.println("根据方法名取得name读写方法的结果:"+name); System.out.println("根据方法名取得age 读写方法的结果:"+age); // 通过方法名和参数可以取得任何你想得到的存在的方法 Method upM = clazz.getMethod("upString",String.class); // 这个方法由返回值,可以进行接收 String upName = (String) upM.invoke(p,name); System.out.println("将 name 转换为大写的结果:"+upName); } /** * 通过反射取得构造方法,之后再使用这个构造放方法产生的对象,取得属性和方法。 * 这里是根据 属性 名称,取得属性的 getter 和 setter 方法 */ public void function2() throws Exception{ Class<?> clazz = Class.forName("com.haizhu.reflect.Person"); Constructor<?> con = clazz.getDeclaredConstructor(); con.setAccessible(true); Person p = (Person) con.newInstance(); Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); // PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。因此如果参数名为 "fred",则假定 writer 方法为 "setFred",reader 方法为 "getFred"(对于 boolean 属性则为 "isFred")。注意,属性名应该以小写字母开头,而方法名称中的首写字母将是大写的。 PropertyDescriptor pd = new PropertyDescriptor(nameField.getName(), clazz); // 获得写方法 Method wM = pd.getWriteMethod(); // 因为知道是String 类型的属性,所以传个String 过去就是了。。实际情况中需要判断下他的参数类型 wM.invoke(p, "haizhu"); // 获得读方法 Method rM = pd.getReadMethod(); // 因为知道是String 类型的属性,所以强制转换成 String就是了。。也可以不转换直接打印 String name = (String) rM.invoke(p); System.out.println("通过属性名称得到写入和读出内容方法的结果:"+name); } public static void main(String[] args) { ReflectDemo demo = new ReflectDemo(); try { demo.function1(); demo.function2(); } catch (Exception e) { throw new RuntimeException(e); } } }