类加载机制与反射
Posted 清心雅筑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类加载机制与反射相关的知识,希望对你有一定的参考价值。
1、使用反射生成并操作对象
通过反射来生成对象有如下两种方式:
使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求改Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例;
先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
import java.io.FileInputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class ObjectPoolFactory { //定义一个对象池,前面是对象名,后面是实际对象 private Map<String, Object> objectPool = new HashMap<>(); public static void main(String[] args) { ObjectPoolFactory pf = new ObjectPoolFactory(); pf.initPool("obj.txt"); System.out.println(pf.getObject("a")); System.out.println(pf.getObject("b")); } //定义一个创建对象的方法 //该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象 private Object createObject(String clazzName) throws Exception { //根据字符串来获取对应的class对象 Class<?> clazz = Class.forName(clazzName); //使用clazz对应类的默认构造器创建实例 return clazz.newInstance(); } //根据指定文件来初始化对象池 //它会根据配置文件来创建对象 public void initPool(String fileName) { try { FileInputStream fis = new FileInputStream(fileName); Properties props = new Properties(); props.load(fis); for(String name : props.stringPropertyNames()) { //每取出一对key-value对,就根据value创建一个对象 //调用createOjbect()创建对象,并将对象添加到对象池中 objectPool.put(name, createObject(props.getProperty(name))); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Object getObject(String name) { //从objectPool中取出指定name对应的对象 return objectPool.get(name); } }
obj.txt:
a=java.util.Date
b=javax.swing.JFrame
调用方法
当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法--这两个方法的返回值是Method数组,或者Method对象。
每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它对应的方法。在Method里包含一个invoke()方法,该方法的签名如下:
Object invoke(Object obj, Object... args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class ExtendedObjectPoolFactory { //定义一个对象池,前面是对象名,后面是实际对象 private Map<String, Object> objectPool = new HashMap<>(); private Properties config = new Properties(); 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")); } //根据指定文件来初始化对象池 //根据配置文件来创建对象 public void initPool() throws Exception { for(String name : config.stringPropertyNames()) { System.out.println("name: " + name); //每取出一个key-value对,如果key中不包含百分号(%) //表明是根据value来创建一个对象 //调用createObject创建对象,并将对象添加到对象池中 if (!name.contains("%")) { objectPool.put(name, createObject(config.getProperty(name))); } } } //根据属性文件来调用指定对象的setter方法 public void initProperty() throws Exception { 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]); //获取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); System.out.println(mtd); //通过Method的invoke方法执行setter方法 //将config.getProperty(name)的值作为调用setter方法的参数 mtd.invoke(target, config.getProperty(name)); } } } public Object getObject(String name) { return objectPool.get(name); } //定义一个创建对象的方法 //该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象 private Object createObject(String clazzName) throws Exception { //根据字符串来获取对应的Class对象 Class<?> clazz = Class.forName(clazzName); //使用clazz对应类的默认构造器创建实例 return clazz.newInstance(); } //从指定属性文件中初始化Properties对象 public void init(String fileName) { try (FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { System.out.println("读取" + fileName + "异常"); } } }
访问成员变量值
import java.lang.reflect.Field; public class FieldTest { public static void main(String[] args) throws Exception { //创建一个Person对象 Person p = new Person(); //获取Person类对应的Class对象 Class<Person> personClazz = Person.class; //获取Person的名为name的成员变量 //使用getDeclaredField()方法声明可获取各种访问控制符的成员变量 Field nameField = personClazz.getDeclaredField("name"); //设置通过反射访问该成员变量时取消访问权限检查 nameField.setAccessible(true); //调用set()方法为p对象的name成员变量设置值 nameField.set(p, "Jason"); //获取Person类名为age的成员变量 Field ageField = personClazz.getDeclaredField("age"); //设置通过反射访问该成员变量时取消访问权限检查 ageField.setAccessible(true); //调用setInt()方法为p对象的age成员变量设置值 ageField.setInt(p, 30); System.out.println(p); } } class Person{ private String name; private int age; public String toString() { return "Person[name:" + name + " ,age: " + age + " ]"; } }
getDeclaredField()方法获取了名为name的成员变量,注意此处不是使用getField()方法,因为getField()方法只能获取public访问控制的成员变量,而getDeclaredField()方法则可以获取所有的成员变量;ageField.setAccessible(true);通过反射访问该成员变量时不受访问权限的控制。
2、使用反射生成JDK动态代理
在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类或动态代理对象。
使用Proxy和InvocationHandler创建动态代理
Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态的生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态的创建实例,也可以使用Proxy来创建动态代理实例。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest { public static void main(String[] args) { //创建一个InvocationHandler对象 InvocationHandler handler = new MyInvocationHandler(); //使用指定的InvocationHandler来生成一个动态代理对象 Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] {Person.class}, handler); //调用动态代理对象的walk()和sayHello()方法 p.walk(); p.sayHello("zhangsan"); } } class MyInvocationHandler implements InvocationHandler { /** * 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法 * 其中: * proxy: 代表动态代理对象 * method: 代表正在执行的方法 * args: 代表调用目标方法时传入的实参 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("----正在执行的方法: " + method); if (args != null) { System.out.println("下面是执行该方法时传入的实参为:"); for(Object val : args) { System.out.println(val); } } else { System.out.println("调用该方法没有实参!"); } return null; } } interface Person { void walk(); void sayHello(String name); }
不管程序是执行代理对象的walk()方法,还是执行代理对象的sayHello()方法,实际上都是执行InvocationHandler对象的invoke()方法。
实际上,在普通编程过程中,确实无须使用动态代理,但在编写框架或底层基础代码时,动态代理的作用就非常大。
动态代理和AOP
反射和泛型
从JDK5以后,Java的Class类增加了泛型功能,从而允许使用泛型来限制Class类,例如,String.class的类型实际上是Class<String>。如果Class对应的类暂时未知,则使用Class<?>。通过在反射中使用泛型,可以避免使用反射生成的对象需要强制类型转换。
import java.util.Date; import javax.swing.JFrame; public class ObjectFactory2 { public static void main(String[] args) { try { //获取实例后无需类型转换 Date d = ObjectFactory2.GetInstance(Date.class); JFrame f = ObjectFactory2.GetInstance(JFrame.class); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static <T> T GetInstance(Class<T> cls) throws InstantiationException, IllegalAccessException { return cls.newInstance(); } }
使用反射来获取泛型信息
通过指定类对应的Class对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用private修饰,还是使用public修饰。获得了成员变量对应的Field对象后,就可以很容易的获得该成员变量的数据类型,即使用如下代码即可获得指定成员变量的类型。
//获取成员变量f的类型 Class<?> a = f.getType();
但这种方式只对普通类型的成员变量有效。如果该成员变量的类型是有泛型类型的类型,如Map<String, Integer>类型,则不能准确的得到该成员变量的泛型参数。
为了获得指定成员变量的泛型类型,应先使用如下方法来获取该成员变量的泛型类型。
//获取成员变量f的泛型类型 Class<?> a = f.getGenericType();
然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType类提供了如下两个方法:
getRawType(): 返回没有泛型信息的原始类型
getActualTypeArguments(): 返回泛型参数的类型。
import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Map; public class GenericTest { private Map<String, Integer> score; public static void main(String[] args) { Class<GenericTest> clazz = GenericTest.class; try { Field f = clazz.getDeclaredField("score"); //直接使用getType()取出类型只对普通类型的成员变量有效 Class<?> a = f.getType(); 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(); System.out.println("泛型信息是: "); for (int i = 0; i < tArgs.length; i++) { System.out.println("第" + i + "个泛型类型是: " + tArgs[i]); } } else { System.out.println("泛型类型出错!"); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Outputs:
score的类型是: interface java.util.Map 原始类型是: interface java.util.Map 泛型信息是: 第0个泛型类型是: class java.lang.String 第1个泛型类型是: class java.lang.Integer
Type也是java.lang.reflect包下的一个接口,该接口代表所有类型的公共高级接口,Class是Type接口的实现类。Type包括原始类型、参数化类型、数组类型、类型变量和基本类型等。
以上是关于类加载机制与反射的主要内容,如果未能解决你的问题,请参考以下文章