反射原理

一:什么是反射

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


二:反射的实现

我们先来看一个实例:

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);
		}
	}
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值