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


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

  • 相关阅读:
    为什么少有人在Windows电脑上安OS X?
    Xamarin.iOS开发初体验
    MySQL MyISAM/InnoDB高并发优化经验
    windows系统上安装与使用Android NDK r8d(二)
    windows系统上安装与使用Android NDK r8d(一)
    Windows平台下如何使用Android NDK
    Xamarin 手动安装步骤+破解(最新版Xamarin V3)
    MONO,原来你是水中月
    剑客vs刀客 Java vs .NET
    终于理解了什么是LGPL
  • 原文地址:https://www.cnblogs.com/ycyoes/p/6203834.html
Copyright © 2020-2023  润新知