Java笔记6:反射

作者:陆金龙    发表时间:2022-09-23 22:17   

关键词:  

6.3.1 反射获取类的信息

@Repeatable(Annos.class)
@interface Anno {}
@Retention(value=RetentionPolicy.RUNTIME)
@interface Annos {
    Anno[] value();
}
// 使用4个注解修饰该类
@SuppressWarnings(value="unchecked")
@Deprecated
// 使用重复注解修饰该类
@Anno
@Anno
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的全部Annotation如下:");
		for (Annotation an : anns)
		{
			System.out.println(an);
		}
		System.out.println("该Class元素上的@SuppressWarnings注解为:"
			+ Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
		System.out.println("该Class元素上的@Anno注解为:"
			+ Arrays.toString(clazz.getAnnotationsByType(Anno.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("ClassTest$Inner");
		// 通过getDeclaringClass()访问该类所在的外部类
		System.out.println("inClazz对应类的外部类为:" +
			inClazz.getDeclaringClass());
		System.out.println("ClassTest的包为:" + clazz.getPackage());
		System.out.println("ClassTest的父类为:" + clazz.getSuperclass());
	}
}
6.4 反射创建和操作对象
6.4.1 反射创建对象
// 定义一个对象池,前面是对象名,后面是实际对象
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
{
	try(
		FileInputStream fis = new FileInputStream(fileName))
	{
		Properties props = new Properties();
		props.load(fis);
		for (String name : props.stringPropertyNames())
		{
			// 每取出一对key-value对,就根据value创建一个对象
			// 调用createObject()创建对象,并将对象添加到对象池中
			objectPool.put(name ,createObject(props.getProperty(name)));
		}
	}
	catch (IOException ex)
	{
		System.out.println("读取" + fileName + "异常");
	}

}
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"));      // ①
	System.out.println(pf.getObject("b"));      // ②
}

obj.txt的内容:
a=java.util.Date
b=javax.swing.JFrame

// 获取JFrame对应的Class对象
Class jframeClazz = Class.forName("javax.swing.JFrame");
// 获取JFrame中带一个字符串参数的构造器
Constructor ctor = jframeClazz.getConstructor(String.class);
// 调用Constructor的newInstance方法创建对象
Object obj = ctor.newInstance("测试窗口");
// 输出JFrame对象
System.out.println(obj);

6.4.2 调用方法

// 定义一个对象池,前面是对象名,后面是实际对象
private Map objectPool = new HashMap<>();
private Properties config = new Properties();
// 从指定属性文件中初始化Properties对象
public void init(String fileName)
{
	try(
		FileInputStream fis = new FileInputStream(fileName))
	{
		config.load(fis);
	}
	catch (IOException ex)
	{
		System.out.println("读取" + fileName + "异常");
	}
}
// 定义一个创建对象的方法,
// 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象
private Object createObject(String clazzName)
	throws InstantiationException
	, IllegalAccessException , ClassNotFoundException
{
	// 根据字符串来获取对应的Class对象
	Class clazz =Class.forName(clazzName);
	// 使用clazz对应类的默认构造器创建实例
	return clazz.newInstance();
}
// 该方法根据指定文件来初始化对象池,
// 它会根据配置文件来创建对象
public void initPool()throws InstantiationException
	,IllegalAccessException , ClassNotFoundException
{
	for (String name : config.stringPropertyNames())
	{
		// 每取出一对key-value对,如果key中不包含百分号(%)
		// 这就标明是根据value来创建一个对象
		// 调用createObject创建对象,并将对象添加到对象池中
		if (!name.contains("%"))
		{
			objectPool.put(name ,
				createObject(config.getProperty(name)));
		}
	}
}
// 该方法将会根据属性文件来调用指定对象的setter方法
public void initProperty()throws InvocationTargetException
	,IllegalAccessException,NoSuchMethodException
{
	for (String name : config.stringPropertyNames())
	{
		// 每取出一对key-value对,如果key中包含百分号(%)
		// 即可认为该key用于控制调用对象的setter方法设置值,
		// %前半为对象名字,后半控制setter方法名
		if (name.contains("%"))
		{
			// 将配置文件中key按%分割
			String[] objAndProp = name.split("%");
			// 取出调用setter方法的参数值
			Object target = getObject(objAndProp[0]);//target 方法所在的类
			
			// 获取setter方法名:set + "首字母大写" + 剩下部分
			String mtdName = "set" +
			objAndProp[1].substring(0 , 1).toUpperCase()+ objAndProp[1].substring(1);
			// 通过target的getClass()获取它实现类所对应的Class对象
			Class targetClass = target.getClass();
			// 获取希望调用的setter方法
			Method mtd = targetClass.getMethod(mtdName , String.class);
			// 通过Method的invoke方法执行setter方法,
			// 将config.getProperty(name)的值作为调用setter的方法的参数
			mtd.invoke(target , config.getProperty(name));
		}
	}
}
public Object getObject(String name)
{
	// 从objectPool中取出指定name对应的对象。
	return objectPool.get(name);
}
public static void main(String[] args)
	throws Exception
{
	ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
	epf.init("extObj.txt");
	epf.initPool();
	epf.initProperty();
	System.out.println(epf.getObject("a"));
}


extObj.txt的内容:
a=java.util.Date
b=javax.swing.JFrame
a%title=Test Title
 
6.4.3 反射访问成员变量值
 
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 nameField = personClazz.getDeclaredField("name");
		// 设置通过反射访问该成员变量时取消访问权限检查
		nameField.setAccessible(true);
		// 调用set()方法为p对象的name成员变量设置值
		nameField.set(p , "Yeeku.H.Lee");
		
		// 获取Person类名为age的成员变量
		Field ageField = personClazz.getDeclaredField("age");
		// 设置通过反射访问该成员变量时取消访问权限检查
		ageField.setAccessible(true);	
		// 调用setInt()方法为p对象的age成员变量设置值
		ageField.setInt(p , 30);
		System.out.println(p);
	}
}
6.4.4 反射操作数组
// 创建一个元素类型为String ,长度为10的数组
Object arr = Array.newInstance(String.class, 10);
 
// 依次为arr数组中index为5、6的元素赋值
Array.set(arr, 5, "疯狂Java讲义");
Array.set(arr, 6, "轻量级Java EE企业应用实战");
 
// 依次取出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);
 
/*
  创建一个三维数组。
  根据前面介绍数组时讲的:三维数组也是一维数组,
  是数组元素是二维数组的一维数组,
  因此可以认为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[]
{
"疯狂Java讲义",
 
"轻量级Java EE企业应用实战"
});
// 获取arrObj数组中index为3的元素,该元素应该是一维数组。
Object anArr  = Array.get(arrObj, 3);
Array.set(anArr , 8  , "疯狂Android讲义");
 
// 将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.5 创建动态代理

public static void main(String[] args)
		throws Exception
{
	// 创建一个原始的GunDog对象,作为target
	Dog target = new GunDog();
	// 以指定的target来创建动态代理
	Dog dog = (Dog)MyProxyFactory.getProxy(target);
	dog.info();
	dog.run();
}

public class MyProxyFactory
{
	// 为指定target生成动态代理对象
	public static Object getProxy(Object target)throws Exception
	{
		// 创建一个MyInvokationHandler对象
		MyInvokationHandler handler = new MyInvokationHandler();
		// 为MyInvokationHandler设置target对象
		handler.setTarget(target);
		// 创建、并返回一个动态代理
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces() , handler);
	}
}

public class MyInvokationHandler implements InvocationHandler
{
	// 需要被代理的对象
	private Object target;
	public void setTarget(Object target)
	{
		this.target = target;
	}
	// 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
	public Object invoke(Object proxy, Method method, Object[] args)
		throws Exception
	{
		DogUtil du = new DogUtil();
		// 执行DogUtil对象中的method1。
		du.method1();
		
		// 以target作为主调来执行method方法
		Object result = method.invoke(target , args);
		
		// 执行DogUtil对象中的method2。
		du.method2();
		return result;
	}
}
6.6 反射中使用泛型
 
6.6.1 泛型和Class类
public class CrazyitObjectFactory2
{
	public static  T getInstance(Class cls)
	{
		try
		{
			return cls.newInstance();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}
	public static void main(String[] args)
	{
		// 获取实例后无须类型转换
		Date d = CrazyitObjectFactory2.getInstance(Date.class);
		JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
	}
}


public class CrazyitArray
{
	// 对Array的newInstance方法进行包装
	@SuppressWarnings("unchecked")
	public static  T[] newInstance(Class componentType, int length)
	{
		return (T[])Array.newInstance(componentType , length);  //①
	}
	public static void main(String[] args)
	{
		// 使用CrazyitArray的newInstance()创建一维数组
		String[] arr = CrazyitArray.newInstance(String.class , 10);
		
		// 使用CrazyitArray的newInstance()创建二维数组
		// 在这种情况下,只要设置数组元素的类型是int[]即可。
		int[][] intArr = CrazyitArray.newInstance(int[].class , 5);
		arr[5] = "疯狂Java讲义";
		// intArr是二维数组,初始化该数组的第二个数组元素
		// 二维数组的元素必须是一维数组
		intArr[1] = new int[]{23, 12};
		System.out.println(arr[5]);
		System.out.println(intArr[1][1]);
	}
}
6.6.2 获取泛型信息
 
public class GenericTest
{
	private Map score;
	public static void main(String[] args)
		throws Exception
	{
		Class clazz = GenericTest.class;
		Field f = clazz.getDeclaredField("score");
		// 直接使用getType()取出的类型只对普通类型的成员变量有效
		Class a = f.getType();
		
		// 下面将看到仅输出java.util.Map
		System.out.println("score的类型是:" + a);
		// 获得成员变量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();
			for (int i = 0; i < tArgs.length; i++)
			{
				System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
			}
		}
		else
		{
			System.out.println("获取泛型类型出错!");
		}
	}
}