• 类加载机制与反射(二)


    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包括原始类型、参数化类型、数组类型、类型变量和基本类型等

  • 相关阅读:
    台州 OJ 3847 Mowing the Lawn 线性DP 单调队列
    洛谷 OJ P1417 烹调方案 01背包
    快速幂取模
    台州 OJ 2649 More is better 并查集
    UVa 1640
    UVa 11971
    UVa 10900
    UVa 11346
    UVa 10288
    UVa 1639
  • 原文地址:https://www.cnblogs.com/ycyoes/p/6203834.html
Copyright © 2020-2023  润新知