反射原理

来源:互联网 发布:web数据挖掘案例 编辑:程序博客网 时间:2024/05/17 23:50

一:什么是反射

正常情况下,如果已经有一个类,那么肯定可以通过类创建对象;那么如果现在要求通过一个对象找到一个类的名称,此时就需要用到反射技术。所以,反射就是加载类,并解析出类的各个组成部分。


二:反射的实现

我们先来看一个实例:

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.XX
从程序中,可以发现,通过一个对象得到了对象所在的完整的“包.类”名称,当然也可以只得到类名称,那么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.classif(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);}}}