<small id='NqpBj7b'></small> <noframes id='hyG7HY4'>

  • <tfoot id='HVUqyr'></tfoot>

      <legend id='KQUiwloT8L'><style id='04N6l2sbR'><dir id='nrSzoPR'><q id='FkYdhf'></q></dir></style></legend>
      <i id='GjCvxI'><tr id='coAuflrIO'><dt id='VzSY'><q id='OyxMGQH'><span id='PSTqh5Vk'><b id='bNxJI'><form id='3UAq'><ins id='p0ZYmw3S4'></ins><ul id='5hvdU'></ul><sub id='HSkdJ3Bob'></sub></form><legend id='Ym7IHa'></legend><bdo id='yRF2D1KsGp'><pre id='SfrkWD'><center id='eEMq'></center></pre></bdo></b><th id='VQO5YAUha'></th></span></q></dt></tr></i><div id='YdGi9kKD7'><tfoot id='2eFBCV'></tfoot><dl id='qaO0Ri'><fieldset id='n6rRT'></fieldset></dl></div>

          <bdo id='9Evc36aIyV'></bdo><ul id='zcVNmjSL9w'></ul>

          1. <li id='xPdUIt'></li>
            登陆

            Java 根底篇之反射

            admin 2019-11-20 312人围观 ,发现0个评论

            反射

            使用反射获取程序运行时的对象和类的真实信息。

            获取 Class 对象

            每个类被加载之后,系统会为该类生成一个对应的 Class 对象,通过该 Class 对象可以访问到 JVM 中的这个类。

            • 使用 Class 类的 forName(String clazzName) 静态方法。字符串参数的值是某个类的全限定类名,必须包含完整的包名
            • 调用某个类的 class 属性
            • 调用某个对象的 getClass() 方法。该方法是 java.lang.Object 类中的一个方法,所有的 Java 对象都可以调用,返回该对象所属类对应的 Class 对象

            获取 Class 对象中信息

            Class 类提供了大量的实例方法来获取该 class 对象所对应的类的详细信息。更多请参考 API。

            import jJava 根底篇之反射ava.lang.reflect.*;
            import java.lang.annotation.*;
            public class ClassTest {
            private ClassTest() {
            }
            public ClassTest(String name) {
            System.out.println("执行有参数的构造器");
            }
            public void info() {
            System.out.println("执行无参数的info方法");
            }
            public void info(String str) {
            System.out.println("执行有参数的info方法" + ",其 str 参数值: " + str);
            }
            class Inner {
            }
            public static void main(String[] args) throws Exception {
            Class clazz = ClassTest.class;
            // 获取 clazz 对象所对应类的全部构造器
            Constructor
            System.out.println("ClassTest 的全部构造器如下: ");
            for (Constructor c : ctros) {
            System.out.println(c);
            }
            // 获取 clazz 对象所对应类的全部 public 构造器
            Constructor
            System.out.println("ClassTest的全部public构造器如下:");
            for (Constructor c : publicCtors) {
            System.out.println(c);
            }
            // 获取 clazz 对象所对应类的全部 public 方法
            Method[] mtds = clazz.getMethods();
            System.out.println("ClassTest 的全部 public 方法如下: ");
            for (Method md : mtds) {
            System.out.println(md);
            }
            // 获取 clazz 对象所对应类的指定方法
            System.out.println("ClassTest 里带一个字符串参数的 info 方法为:" + clazz.getMethod("info", String.class));
            // 获取 clazz 对象所对应类的全部注解
            Annotation[] anns = clazz.getAnnotations();
            System.out.println("ClassTest 的全部 Annotation 如下: ");
            for (Annotation an : anns) {
            System.out.println(an);
            }
            // 获取 clazz 对象所对应类的全部内部类
            Class
            System.out.println("ClassTest 的全部内部类如下: ");
            for (Class c : inners) {
            System.out.println(c);
            }
            // 使用 Class.forName() 方法加载 ClassTest 的 Inner 内部类
            Class inClazz = Class.forName("ClassTest$Inner");
            // 访问该类所在的外部类
            System.out.println("inClazz 对应类的外部类为: " + inClazz.getDeclaringClass());
            System.out.println("ClassTest 的包为:" + clazz.getPackage());
            System.out.println("ClassTest 的父类为:" + clazz.getSuperclass());
            }
            }

            应用

            Class 对象可以获得对应类的方法(由 Method 表示)、构造器(由 Constructor 表示)、成员变量(由 Field 对象表示),且这个三个类都实现了 java.lang.reflect.Member 接口。程序可以通过 Method 对象来执行对应的方法,通过 Constructor 对象来调用对应的构造器创建实例,Java 根底篇之反射通过 Field 对象直接访问并修改对象的成员变量值。

            创建对象

            • 使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例。要求该 Class 对象的对应类有默认构造器
            • 先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建该 Class 对象对应类的实例。这种方式可以选择使用指定的构造器来创建实例

            方式一

            实现了一个简单的对象池,该对象池会根据配置文件读取 key-value 对,然后创建这些对象并放入 HashMap 中

            import java.io.FileInputStream;
            import java.io.IOException;
            import java.util.HashMap;
            import java.util.Map;
            import java.util.Properties;
            public class ObjectPoolFactory {
            private Map objectPool = new HashMap<>();
            private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            Class
            // 使用 Class 对象对应的类的默认构造器
            return clazz.newInstance();
            }
            public void iniJava 根底篇之反射tPool(String fileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            try (
            FileInputStream fis = new FileInputStream(fileName)
            ) {
            Properties props = new Properties();
            props.load(fis);
            for ( String name: props.stringPropertyNames()) {
            objectPool.put(name, createObject(props.getProperty(name)));
            }
            } catch (IOException ex) {
            System.out.println("读取" + fileName + "异常");
            }
            }
            public Object getObject(String 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
            */

            方式二

            import java.lang.reflect.Constructor;
            public class CreateJFrame {
            public static void main(String[] args) throws Exception {
            Class
            // 选择使用指定的构造器
            Constructor ctor = jframeClazz.getConstructor(String.class3a);
            Object obj = ctor.newInstance("测试窗口");
            System.out.println(obj);
            }
            }

            调用方法

            每个 Method 对象对应一个方法,获得 Method 对象后,就可以通过该 Method 来调用它对应的方法。

            Method 包含一个 invoke() 方法,该方法的签名如下:

            • Object invoke(Object obj, Object… args):该方法中的 obj 是执行方法的主调(即类的实例对象),后面的 args 是执行该方法的实参

            下面是对之前的对象工厂池进行增强,允许在配置文件中增加配置对象的成员变量值,对象池工厂会读取该对象配置的成员变量值,并利用该对象对应的 setter 方法设置成员变量的值:

            import java.io.FileInputStream;
            import java.io.IOException;
            import java.lang.reflect.InvocationTargetException;
            import java.lang.reflect.Method;
            import java.util.HashMap;
            import java.util.Map;
            import java.util.Properties;
            public class ExtendedObjectPoolFactory {
            // 定义一个对象池,前面是对象名,后面是实际的对象
            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 + "异常");
            }
            }
            // 定义创建对象的方法
            private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            // 根据字符串来获取对应的 Class 对象
            Class
            // 使用 clazz 对应类的默认构造器创建实例
            return clazz.newInstance();
            }
            // 初始化对象池
            public void initPool() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            for (String name : config.stringPropertyNames()) {
            if (!name.contains("%")) {
            objectPool.put(name, createObject(config.getProperty(name)));
            }
            }
            }
            // 根据属性文件来调用指定对象的 setter 方法
            public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
            for (String name : config.stringPropertyNames()) {
            if (name.contains("%")) {
            String[] objAndProp = name.split("%");
            Object target = getObject(objAndProp[0]);
            String mtdName = "set" + objAndProp[1].substring(1);
            // 通过 target 的 getClass() 获取它的实现类所对应的 Class 对象
            Class
            // 获取希望调用的 setter 方法
            Method mtd = targetClass.getMethod(mtdName, String.class);
            // 通过 Method 的 invoke 方法执行 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
            # set the title of a
            a%title=Test Title
            */

            PS:当通过 Method 的 invoke() 方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果需要调用某个对象的 private 方法,则可以先调用 Method 对象的如下方法:

            • setAccessible(boolean flag):值为 true,表示该 Method 在使用时取消访问权限检查

            访问成员变量值

            Filed 提供如下两组方法来读取Java 根底篇之反射或设置成员变量值:

            • getXxx(Object obj):获取 obj 对象的该成员变量的值。此处的 Xxx 对应 8 中基本类型。如果成员变量的类型是引用类型,则直接使用 get
            • setXxx(Object obj, Xxx val):将 obj 对象的成员变量值设为 val 值。此处的 Xxx 对应 8 中基本类型。如果成员变量的类型是引用类型,则直接使用 set
            public class Person {
            private String name;
            private int age;
            public String toString() {
            return "Person[name:" + name + ", age:" + age + "]";
            }
            }
            import java.lang.reflect.Field;
            public class FieldTest {
            public static void main(String[] args) throws Exception {
            Person p = new Person();
            Class personClazz = Person.class;
            Field nameField = personClazz.getDeclaredField("name");
            nameField.setAccessible(true);
            nameField.set(p, "crazy");
            Field ageField = personClazz.getDeclaredField("age");
            ageField.setAccessible(true);
            ageField.setInt(p, 30);
            System.out.println(p);
            }
            }

            泛型在反射中的应用

            在反射中使用泛型,反射生成的对象就不需要进行强制类型转换。

            import java.util.Date;
            public class CrazyitObjectFactory {
            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 = CrazyitObjectFactory.getinstance(Date.class);
            }
            }

            欢迎关注我的公众号

            请关注微信公众号
            微信二维码
            不容错过
            Powered By Z-BlogPHP