• java 使用反射


    java程序中的对象在运行时会出现两种类型:编译时类型和运行时类型。例如List list  = new ArrayList()。其中变量list的编译时类型是List,运行时类型是ArrayList。还有更极端的类型,如通过网络通信传递过来一个对象,则这个对象的编译时类型是Object,但程序运行时又需要调用该对象运行时类型的方法。为了解决这种问题,程序需要在运行时发现对象和类的真实信息,为了解决这种问题有两种做法:

    • 第一种是假设在编译和运行时都知道类的具体信息。这种情况下,我们可以先使用instanceof运算符进行判断,再利用强制类型转换将之转换成运行时类型的变量即可;
    • 第二种是编译时根本无法预知该对象和类可能属于哪些类,程序只能依靠运行时信息来发现该对象和类的具体信息,这就必须使用反射。

     

    获得Class对象

    每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。Java程序中获得Class对象通常有如下三种方式:

    1. 使用Class类的forName()静态方法。该方法需要传入字符串参数。该字符串参数的值是某个类的全名。
    2. 调用类的class属性获取该类对应的Class对象。
    3. 调用某个对象的getClass()方法

    第一种方式和第二种方式都是直接根据类来获取该类的Class对象,相比之下,第二种方式有如下两种优势:

    • 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在;
    • 程序性能更高,因为这种方式无需调用方法,所以性能更好。

    也就是说,大部分情况下我们都该使用第二种方式来获取指定类的Class对象。但如果只有一个类名字符串,则只能使用第一种方式。一旦获取一个类对应的Class对象后,程序就可以调用Class对象的方法来获得该对象和该类的真实信息了。

    获得类的Class对象后,就可以通过Class对象来获取类的构造器、方法、属性、注释、匿名内部类等属性了。

    使用Class对象来创建类的对象

    通过反射来生对象有两种方式:

    • 使用Class对象的newInstance()方法来创建该Class对应类的实例,这种方式要求该Class对象的对应类有默认构造器,执行newInstance()方法实际上是使用类的默认构造器来创建类的实例。
    • 使用Class对象获取指定的Contructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象的对应类的实例。通过这种方法可以选择使用某个类的指定构造器来创建实例。
    package com.zhyea.test;
    
    import java.lang.reflect.Constructor;
    
    /**
     * 使用反射创建对象测试类
     * 
     * @author robin
     *
     */
    public class MyTest {
        /**
         * 使用默认构造器
         * 
         * @throws Exception
         */
        public void test1() throws Exception {
            Class<?> clazz = Class.forName("com.zhyea.test.Hello");
            System.out.println(clazz.newInstance());
        }
    
        /**
         * 使用指定构造器
         * 
         * @throws Exception
         */
        public void test2() throws Exception {
            Class<?> clazz = Class.forName("com.zhyea.test.Hello");
            Constructor<?> constructor = clazz.getConstructor(String.class);
            System.out.println(constructor.newInstance("robin"));
        }
    
        public static void main(String[] args) throws Exception {
            MyTest mt = new MyTest();
            mt.test1();
            mt.test2();
        }
    }
    
    class Hello {
    
        String name;
    
        public Hello() {}
    
        public Hello(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Hello " + name + "!";
        }
    }

    使用Class对象调用方法

    Class对象可以使用getMethods()方法或者getMethod()获取全部或指定方法。这两个方法返回的是Method对象数组或Method对象。

    获得Method对象后,就可以通过Method来调用对应方法。

    package com.zhyea.test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * 使用反射测试类
     * 
     * @author robin
     *
     */
    public class MyTest {
        /**
         * 使用默认构造器
         * 
         * @throws Exception
         */
        public void test1() throws Exception {
            Class<?> clazz = Class.forName("com.zhyea.test.Hello");
            Method mtd = clazz.getMethod("sayHello", String.class);
            mtd.invoke(clazz.newInstance(), "Robin");
        }
    
        /**
         * 使用指定构造器
         * 
         * @throws Exception
         */
        public void test2() throws Exception {
            Class<?> clazz = Class.forName("com.zhyea.test.Hello");
            Constructor<?> ctr = clazz.getConstructor(String.class);
            Method mtd = clazz.getMethod("sayHello", String.class);
            mtd.invoke(ctr.newInstance("zhangsan"), "Robin");
        }
    
        public static void main(String[] args) throws Exception {
            MyTest mt = new MyTest();
            mt.test1();
            mt.test2();
        }
    }
    
    class Hello {
    
        String name;
    
        public Hello() {}
    
        public Hello(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            if (null != this.name)
                return "Hello " + this.name + "!";
            else
                return "Hello!";
        }
    
        public void sayHello(String name) {
            if (null == this.name) {
                this.name = name;
            }
            System.out.println("Hello " + this.name + "!");
        }
    }

    对于私有方法,也是通过setAccessible方法进行访问,只不过获取Method对象的方法需要做下调整。

    package com.zhyea.test;
    
    import java.lang.reflect.Method;
    
    /**
     * 使用反射测试类
     * 
     * @author robin
     *
     */
    public class MyTest {
        
        public void test() throws Exception{
            Class<?> clazz = Class.forName("com.zhyea.test.Hello");
            Method mtd = clazz.getDeclaredMethod("testHello");
            mtd.setAccessible(true);
            mtd.invoke(clazz.newInstance());
        }
    
        public static void main(String[] args) throws Exception {
            MyTest mt = new MyTest();
            mt.test();
        }
    }
    
    class Hello {
    
        String name;
    
        public Hello() {}
    
        private void testHello(){
            System.out.println("This is a test for private method");
        }
    }

    使用Class对象访问成员变量

    通过Class对象的getFields()方法或getField()方法可以获取该类所包括的全部Field属性或指定Field。Field提供了如下两个方法来访问属性:

    getXxx(Object obj):获取obj对象该Field属性值。Xxx对应8个直接类型。引用类型直接使用get即可。

    setXxx(Object obj, Xxx val):将obj对象该Field的值设置成为val。Xxx对应8个直接类型。引用类型直接使用get即可。

    实例代码如下:

    package com.zhyea.test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    /**
     * 使用反射测试类
     * 
     * @author robin
     *
     */
    public class MyTest {
        
        public void test() throws Exception{
            Class<?> clazz = Class.forName("com.zhyea.test.Hello");
            Constructor<?> ctr = clazz.getConstructor(String.class);
            Object obj = ctr.newInstance("Robin");
            Field nameField = clazz.getDeclaredField("name");
            System.out.println(nameField.get(obj) );
            nameField.setAccessible(true);
            nameField.set(obj, "zhangsan");
            System.out.println((Hello)obj);
        }
    
        public static void main(String[] args) throws Exception {
            MyTest mt = new MyTest();
            mt.test();
        }
    }
    
    class Hello {
    
        String name;
    
        public Hello(String name) {
            this.name = name;
        }
        
        public String toString(){
            return "Hello " + this.name + "!";
        }
    }

    使用反射来操作数组

    在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态地创建操作数组。

    用代码来说明下:

    package com.zhyea.test;
    
    import java.lang.reflect.Array;
    
    /**
     * 使用反射测试类
     * 
     * @author robin
     *
     */
    public class ArrayTest {
        
        public static void main(String[] args) throws Exception {
            Object arr = Array.newInstance(String.class, 6); 
            Array.set(arr, 0, "Robin");
            Array.set(arr, 1, "Tom");
            
            System.out.println(Array.get(arr, 0));
            
            String[] strArr = (String[])arr;
            System.out.println(strArr[0]);
            System.out.println(strArr[1]);
        }
    }
  • 相关阅读:
    工作杠杆
    AngularJS 自定义指令directive 介绍
    CentOS卸载OpenJDK并安装Sun JDK
    jQuery Datatable 表格插件
    ZTree 使用范例
    jQuery UI 实例 – 切换(Toggle)
    curl 抓取页面信息
    报警平台
    PHP imagechar() 图形验证码 字体太小问题
    Discuz!在线中文分词服务
  • 原文地址:https://www.cnblogs.com/amunote/p/4183455.html
Copyright © 2020-2023  润新知