黑马程序员_Java高新技术_反射

 

                       ----------   期待与您的交流    Android培训  Java培训     期待与您的交流-----
 
JAVA反射是一个非常重要的概念,很多框架都是通过反射来完成的.!
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息,以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制。Java反射机制主要提供了以下功能:
1,在运行时判断任意一个对象所属的类;
2,在运行时构造任意一个类的对象;
3,在运行时判断任意一个类所具有的成员变量和方法;
4,在运行时调用任意一个对象的方法;
5,生成动态代理。
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中。
1,Class类:代表一个类。
2,Field类:代表类的成员变量(成员变量也称为类的属性)。
3,Method类:代表类的方法。
4,Constructor类:代表类的构造方法。
5,Array类:提供了动态创建数组,以及访问数组元素的静态方法。
当真正需要使用一个类时系统才会装入它 java.lang.Class是Java应用程序在运行时装入类和接口的实例 Class只能由系统建立对象我们可以通过Object的getClass()方法來取得某个对象对应Class对象
 
我们知道每一个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象我们就可以访问到JVM中的这个类了,
java程序获取Class对象有三种方式:
1>使用Class类的forName()静态方法,该方法传入字符串参数,该字符串是某个类的全限定类名!
2>调用该类的class属性来获取该类对应的Class对象,例如Dog.class
3>调用某个对象的getClass方法,,该方法是Object类的一个方法,所以Java对象都可以调用这个方法.
我们可以通过API来查看Class提供了那些方法来来获取类的信息:
  1. 通过反射来获取类的基本信息:
下面我们就通过一个简单的程序来详细说Class对象来获取对应的详细信息 :
//使用2个注释修饰该类
@SuppressWarnings(value="unchecked")
@Deprecated
public class ClassTest
{
	//为该类定义一个私有的构造器
	private ClassTest()
	{
	}
	//定义一个有参数的构造器
	public ClassTest(String name)
	{
		System.out.println("执行有参数的构造器");
	}
	//定义一个无参数的info方法
	public void info()
	{
		System.out.println("执行无参数的info方法");
	}
	//定义一个有参数的info方法
	public void info(String str)
	{
		System.out.println("执行有参数的info方法"
			+ ",其实str参数值:" + str);
	}
	//定义一个测试用的内部类
	class Inner
	{
	}
	public static void main(String[] args) 
		throws Exception
	{
		//下面代码可以获取ClassTest对应的Class
		Class clazz = ClassTest.class;
		//获取该Class对象所对应类的全部构造器
		Constructor[] ctors = clazz.getDeclaredConstructors();
		System.out.println("ClassTest的全部构造器如下:");
		for (Constructor c : ctors)
		{
			System.out.println(c);
		}
		//获取该Class对象所对应类的全部public构造器
		Constructor[] publicCtors = clazz.getConstructors();
		System.out.println("ClassTest的全部public构造器如下:");
		for (Constructor c : publicCtors)
		{
			System.out.println(c);
		}
		//获取该Class对象所对应类的全部public方法
		Method[] mtds = clazz.getMethods();
		System.out.println("ClassTest的全部public方法如下:");
		for (Method md : mtds)
		{
			System.out.println(md);
		}
		//获取该Class对象所对应类的指定方法
		System.out.println("ClassTest里带一个字符串参数的info方法为:"
			+ clazz.getMethod("info" , String.class));
		//获取该Class对象所对应类的上的全部注释
		Annotation[] anns = clazz.getAnnotations();
		System.out.println("ClassTest的全部Annotattion如下:");
		for (Annotation an : anns)
		{
			System.out.println(an);
		}
		System.out.println("该Class元素上的@SuppressWarnings注释为:"
			+ clazz.getAnnotation(SuppressWarnings.class));
		//获取该Class对象所对应类的全部内部类
		Class[] inners = clazz.getDeclaredClasses();
		System.out.println("ClassTest的全部内部类如下:");
		for (Class c : inners)
		{
			System.out.println(c);
		}
		//使用Class.forName方法加载ClassTest的Inner内部类
		Class inClazz = Class.forName("com.zx.ClassTest$Inner");
		//通过getDeclaringClass()访问该类所在的外部类
		System.out.println("inClazz对应类的外部类为:" + 
			inClazz.getDeclaringClass());
		System.out.println("ClassTest的包为:" + clazz.getPackage());
		System.out.println("ClassTest的父类为:" + clazz.getSuperclass());
	}
}


 

 

 2 使用反射生成并操作对象:
 
 通过反射来生成对象有两种方式:

1>
使用Class对象newInstance()方法来创建,该中方式需要该类有默认的构造器
实际上newInstance()方法它是同过默认的构造器创建实例的
2>先使用Class对象获取指定的Constructor对象,在调用Contructor对象的newInstance()方法来创建实例的


下面程序就实现一个简单的   对象池  该对象池通过配置文件 读取name和value对,然后创建这些对象,这有点像spring容器,然后把这些创建的对象放进HashMap中.

配置文件如下:

public class ObjectPoolFactory
{
	//定义一个对象池,前面是对象名,后面是实际对象
	private Map objectPool = 
		new HashMap();
	//定义一个创建对象的方法,
	//该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象
	private Object createObject(String clazzName)
		throws InstantiationException , IllegalAccessException
		,ClassNotFoundException
	{
		//根据字符串来获取对应的Class对象
		Class clazz =Class.forName(clazzName);
		//使用clazz对应类的默认构造器创建实例
		return clazz.newInstance();		
	}
	//该方法根据指定文件来初始化对象池,
	//它会根据配置文件来创建对象
	public void initPool(String fileName)
		throws InstantiationException , IllegalAccessException
		,ClassNotFoundException
	{
		FileInputStream fis = null;
		try
		{
			fis = new FileInputStream(fileName);
			Properties props = new Properties();
			props.load(fis);
			for (String name : props.stringPropertyNames())
			{
				//每取出一对属性名-属性值对,就根据属性值创建一个对象
				//调用createObject创建对象,并将对象添加到对象池中
				objectPool.put(name , 
					createObject(props.getProperty(name))); 
			}
 
		}
		catch (IOException ex)
		{
			System.out.println("读取" + fileName + "异常");
		}
		finally
		{
			try
			{
				if (fis != null)
				{
					fis.close();
				}
			}
			catch (IOException ex)
			{
				ex.printStackTrace();
			}
		}
	}
	public Object getObject(String name)
	{
		//从objectPool中取出指定name对应的对象。
		return objectPool.get(name);
	}
	
	public static void main(String[] args)
		throws Exception
	{
		ObjectPoolFactory pf = new ObjectPoolFactory();
		pf.initPool("obj.txt");
		System.out.println(pf.getObject("a"));
	}
}


 

3 通过反射调用方法
通过invoke(Object obj,Object...args)

 

public class TestRef {
	public static void main(String args[]) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
	    Foo foo = new Foo("这个一个Foo对象!");
		Class clazz = foo.getClass();
		Method m1 = clazz.getDeclaredMethod("outInfo");
		Method m2 = clazz.getDeclaredMethod("setMsg", String.class);
		Method m3 = clazz.getDeclaredMethod("getMsg");
		m1.invoke(foo);
		m2.invoke(foo, "重新设置msg信息!");
		String msg = (String) m3.invoke(foo);
		System.out.println(msg);
	}
}


 

 

 

 4,  通过反射访问属性:
通过Class对象的getFields()和getField()方法可以获取该类所包括的全部属性
如:
class Person
{
	private String name;
	private int age;
	public String toString()
	{
		return "Person [ name:" + name + 
			" , age:" + age + " ]";
	}
}
public class FieldTest
{
	public static void main(String[] args) 
		throws Exception
	{
		//创建一个Person对象
		Person p = new Person();
		//获取Person类对应的Class对象
		Class personClazz = Person.class;
		//获取Person类名为name的属性
		//使用getDeclaredField,表明可获取各种访问控制符的field
		Field nameField = personClazz.getDeclaredField("name");
		//设置通过反射访问该Field时取消访问权限检查
		nameField.setAccessible(true);
		//调用set方法为p对象的指定Field设置值
		nameField.set(p , "Yeeku.H.Lee");
		//获取Person类名为age的属性
		Field ageField = personClazz.getDeclaredField("age");
		//设置通过反射访问该Field时取消访问权限检查
		ageField.setAccessible(true);
		//调用setInt方法为p对象的指定Field设置值
		ageField.setInt(p , 30);
		System.out.println(p);
	}
}


 

5 通过反射操作数组:

下面程序测试了,如果使用Array来生成数组,为指定数组元素复制,并获取指定数组元素的方式:


 

public class ArrayTest1 
{
	public static void main(String args[])
	{
		try
		{
			//创建一个元素类型为String ,长度为10的数组
			Object arr = Array.newInstance(String.class, 10);
			//依次为arr数组中index为5、6的元素赋值
			Array.set(arr, 5, "Struts2权威指南");
			Array.set(arr, 6, "ROR敏捷开发最佳实践");
			//依次取出arr数组中index为5、6的元素的值
			Object book1 = Array.get(arr , 5);
			Object book2 = Array.get(arr , 6);
			//输出arr数组中index为5、6的元素
			System.out.println(book1);
			System.out.println(book2);
		}
		catch (Throwable e)
		{
			System.err.println(e);
		}
	}
}

在上面的程序基础上,我们想生成一个三维数组怎么办呢?

public class ArrayTest2
{
	public static void main(String args[])
	{
		/*创建一个三维数组。
		  根据前面介绍数组时讲的:三维数组也是一维数组,是数组元素是
		  二维数组的一维数组,因此可以认为arr是长度为3的一维数组
		*/
		Object arr = Array.newInstance(String.class, 3, 4, 10);
		//获取arr数组中index为2的元素,应该是二维数组
		Object arrObj = Array.get(arr, 2);
		//使用Array为二维数组的数组元素赋值。
		//二维数组的数组元素是一维数组,所以传入Array set方法的第三个参数是
		//第三个参数是一维数组。
		Array.set(arrObj , 2 , new String[]
		{
			"Struts2权威指南",
			"轻量级J2EE企业应用实战"
		});
		//获取arrObj数组中index为3的元素,应该是一维数组。
		Object anArr  = Array.get(arrObj, 3);
		Array.set(anArr , 8  , "ROR敏捷开发最佳实践");
		//将arr强制类型转换为三维数组
		String[][][] cast = (String[][][])arr;
		//获取cast三维数组中指定元素的值
		System.out.println(cast[2][3][8]);
		System.out.println(cast[2][2][0]);
		System.out.println(cast[2][2][1]);
	}
}


 

 6, 通过反射来获取泛型的信息:
下面是一个完整的实例程序:详细介绍了怎么获取泛型的信息:

 

public class GenericTest
{
	private Map score;
	public static void main(String[] args)
		throws Exception
	{
		Class clazz = GenericTest.class;
		Field f = clazz.getDeclaredField("score");
		//直接使用getType()取出Field类型只对普通类型的Field有效
		Class a = f.getType();
		System.out.println("score的类型是:" + a);
		//获得Field实例f的泛型类型
		Type gType = f.getGenericType();
		//如果gType类型是ParameterizedType对象
		if(gType instanceof ParameterizedType)
		{
			//强制类型转换
			ParameterizedType pType = (ParameterizedType)gType;
			//获取原来类型
			Type rType = pType.getRawType();
			System.out.println("原始类型是:" + rType);
			//取得泛型类型的泛型参数
			Type[] tArgs = pType.getActualTypeArguments();
			System.out.println("泛型类型是:");
			for (int i = 0; i < tArgs.length; i++) 
			{
				System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
			}
		}
		else
		{
			System.out.println("获取泛型类型出错!");
		}
	}
}


 

总结:
我们通过代码的方式讲解了Class Method Field Contructor Type ParameterizedType等类和接口的用法.

 

 

 

                    ----------   期待与您的交流   Android培训 Java培训     期待与您的交流-----

 

你可能感兴趣的:(黑马程序员)