一、简介

    所谓反射,就是将一个Java类中的各个成分映射成相应的Java类。要理解反射,首先要具备Java中万物皆对象的思想,要明白Java类中的各个成分也是对象,比如方法、字段构造函数等等都是,就连类本身也是Class类,即使类类。所谓的类类(Class),就是Java类在内存中的字节码文件,Java可以根据类的字节码文件创建若干类的对象。

    内省:内省的英文为introspector,字面意思是内部检查,顾名思义,其主要用于对JavaBean进行操作。那么这里就要了解何为JavaBean。JavaBean是一中特殊的类,其里面的方法的名称符合某种特定的规则,比如获取这个类的age属性的方法必须public int getAge(){return age;}的形式,其修改age属性就必须为public void setAge(int age){this.age = age}的形式。JavaBean可以当成一个普通的Java类来操作,但是我是要当做JavaBean来操作呢?自然是有些额外的好处而已。普通的类不一定可以当成JavaBean来操作,但是JavaBean一定可以当成普通的类来操作。一般如果要在两个模块之间传递消息,便可以把这些消息封装在JavaBean中,,这样的JavaBean的实例对象通常称之为值对象(Value-Object,简称VO)。

二、反射实例

    对于反射,首先要得到类型对应的字节码文件,有了字节码文件才能够通过反射提取各个类的成分对象,才能有后续的操作。

    如何得到各个字节码对应的实例对象(Class类)?一般有三种方式:--类名.class--对象.getClass()--Class.forName("类名")

//通过反射得到String类的String(StringBuilder builder)构造函数,并创建String对象,打印出来


import java.lang.reflect.*;
class ReflectTest
{
	public static void main(String[] args)throws Exception
	{
		//通过字节码文件对象得到指定构造函数对象(参数为指定字节码文件所表示的类型)
		Constructor con = String.class.getConstructor(StringBuilder.class);
		//根据构造函数对象,创建类,返回是Object类型,需强转
		String str = (String)con.newInstance(new StringBuilder("asjdadnajdka"));
		System.out.println(str);
	}
}



//通过反射得到对象指定字段的值



import java.lang.reflect.*;
class ReflectTest
{
	public static void main(String[] args)throws Exception
	{	
		ReflectPoint rp = new ReflectPoint(3);
		//通过字节码文件对象得到指定字段类对象
		//Field类表示字段类,字段类对象表示这个字段所在的类的任何一个对象中的这个字段,他是一个综合体,并不是一个具体的指向
		Field fieldX = ReflectPoint.class.getField("x");
		int value = (int)fieldX.get(rp);
		System.out.println(value);
	}
}
class ReflectPoint
{
	public int x;
	ReflectPoint(int x)
	{
		this.x = x;
	}
}



//通过反射得到String类的char charAt(int index)函数,并执行指定String对象的此方法



import java.lang.reflect.*;
class ReflectTest
{
	public static void main(String[] args)throws Exception
	{	
		String s = "abc";
		//反射得到指定方法
		Method charAtMethod = String.class.getMethod("charAt", int.class);
		//执行指定对象的这个方法
		char ch = (char)charAtMethod.invoke(s, 1);
		System.out.println(ch);
	}
}


注:如果传递给Method对象的invoke()方法的第一个参数为null,这说明该Method对象对应的是一个静态方法


//综合练习,将一个对象中的所有String类型的变量中的字符'b'全部换成字符'k'

import java.lang.reflect.*;
class ReflectStringTest
{
	public static void main(String[] args)throws Exception
	{
		//所要操作的对象
		ReflectPoint rp = new ReflectPoint(3,5);
		System.out.println(rp.toString());
		rp = (ReflectPoint)fieldStringReplace(rp);
		System.out.println(rp.toString());
	}
	public static Object fieldStringReplace(Object obj)throws Exception
	{
		//根据对象获得字节码文件,根据字节码文件获得所有字段类数组
		Field[] fiels = obj.getClass().getDeclaredFields(); 
		for(Field field : fiels)
		{
			//遍历字段是否是字符串类型
			if(field.getType() == String.class)
			{
				//替换字符
				field.setAccessible(true);
				String oldString = (String)field.get(obj);
				String newString = oldString.replace('b', 'k');
				field.set(obj, newString);		
			}
		}
		return obj;
	}
}
class ReflectPoint
{
	public int x;
	private int y;

	String a = "abc";
	String b = "bcd";
	String c = "cbcabe";
	public String d = "bsksdjsbsdsbsdisbfdbdsbbbdksd";
	private String e = "sdsbbbbadsaaaabb";

	ReflectPoint(int x, int y)
	{
		this.x = x;
		this.y = y;
	}
	public String toString()
	{
		return a + " : "+ b + " : " + c + " : " + d + " : " + e;
	}
}



    在上面的例子中,我们反射的不管是字段还是方法,都是公共的public的,像private之类私有的元素便不能通过这种普通的方式来做,此时就出现了一种特殊的反射,称之为——暴力反射。


    暴力反射与普通反射其实理解起来原理是一样的,都是使用Java提供的函数来完成,只不过函数的选择和用法上稍许不同。


//暴力反射出对象的私有字段


import java.lang.reflect.*;
class BaoLiReflect
{
	public static void main(String[] args)throws Exception
	{
		//暴力反射
		ReflectPoint rp = new ReflectPoint(5);
		//私有的必须用这个函数
		Field fieldY = ReflectPoint.class.getDeclaredField("y");
		//设置是否允许
		fieldY.setAccessible(true);
		int y = (int)fieldY.get(rp);
		System.out.println("y = " + y);
	}
}
class ReflectPoint
{
	private int y;
	ReflectPoint(int y)
	{
		this.y = y;
	}
}


三、内省的操作方式


对JavaBean的操作方式有两种
--步骤:1.new PropertyDescriptor("属性名", 属性所在的字节码文件——Xxx.class);
        2.调用PropertyDescriptor的读写方法,得到get set的方法对象
        3.操作得到的方法对象,得到或修改属性值。
--步骤:1.BeanInfo bin = Introspector.getBeanInfo(类的字节码文件);
        2.PropertyDescriptor[] pd = bin.getPropertyDescriptor();
        3.迭代比较得到指定的PropertyDescriptor对象
        4.根据上一种方式的2,3操作

如下例子:


import java.lang.reflect.*;
import java.beans.PropertyDescriptor;
import java.beans.Introspector;
import java.beans.BeanInfo;
class ReadJBPointX
{
	public static void main(String[] args)throws Exception
	{
		//有这样一个类对象中,有一个属性名为"x"
		//分析,那就有一个方法为getX()
		JBPoint p = new JBPoint(3);
		String propertyName = "x";
		
		//普通反射的方法
		Method getXMethod = p.getClass().getMethod("getX");
		System.out.println(getXMethod.invoke(p));
		
		//第一种方式
		//属性描述类,参数属性名和哪个类的(字节码文件)属性
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, p.getClass());
		Method getMethod = pd.getReadMethod();
		System.out.println(getMethod.invoke(p));

		Method setMethod = pd.getWriteMethod();
		setMethod.invoke(p, 5);
		System.out.println(getMethod.invoke(p));

		//第二种方式
		//复杂的内省操作方法
		BeanInfo bin = Introspector.getBeanInfo(p.getClass());
		PropertyDescriptor[] pds = bin.getPropertyDescriptors();
		for(PropertyDescriptor pdm : pds)
		{
			if(pdm.getName().equals(propertyName))
			{
				Method getMethodm = pdm.getReadMethod();
				System.out.println(getMethodm.invoke(p));
				break;
			}
		}
	}
}
class JBPoint
{
	private int x;
	JBPoint(int x)
	{
		this.x = x;
	}
	public int getX()
	{
		return x;
	}
	public void setX(int x)
	{
		this.x = x;
	}
}