• 四大神器之反射


    Java反射机制,是一个基础的内容章节,在对于之后的框架学习中起到了至关重要的作用,现在比较流行的是spring

    框架 ,其中的IOC(自动注入)以及AOP(动态代理),在AOC中代理模式又分为动态代理和byteCode

    instrument(插桩)或者是CGLIB 。

    在学习Java反射机制前,首先问一个问题:在Java运行时,对于任意一个类,能否知道这个类有那些属性和方法?对

     

    于任意一个对象,能否调用它任意一个方法?

     

    答案是肯定的。可以!

     

    在这里要去区别一个事情:如果说在自己写的类中去改一个数据类型或者说属性,那只是在编译时,并不是在运行

     

    时。

     

    反射机制的几大功能:

     

    • 在运行时,判断任意一个对象所属类。
    • 在运行时,构造任意一个对象。
    • 在运行时,判断任意一个类所具有的成员变量和方法
    • 在运行时,调用任意一个对象的方法。

     

    一般而言:程序运行时,允许改变程序结构和变量类型成为动态语言。由此可见Java并不是动态语言,但是Java有一

     

    个动态相关的机制Reflection,可以运行时加载、探知、使用编译期间完全未知的classes(但是methods定义不知

     

    道)。所以Reflection是Java视为动态语言的关键性质,允许在程序运行时通过Reflection APIs 取得任何一个已知名

     

    称的class的内部信息。(包括父类,接口,变量,方法等)。

     

    接下来我们用代码来举一个例子。

    public class DumpMethods {
        //从命令行接受一个字符串(该字符串是某个类的全名)
        //打印该类的所有方法
    
        public static void main(String[] args) throws ClassNotFoundException {
            //class类是所有反射的入口
            Class<?> classType = Class.forName(args[0]);//编译时不知道args是什么?
    
            Method[] methods = classType.getDeclaredMethods();
    
            for (Method method : methods) {
                System.out.println(method);
            }
    
        }
    }

     

    在这里,我们可以看到,Java虚拟机并不知道你所要传入的参数是什么,这个时候我们类的全类型名

     

    称 Java.lang.String输入进去,我们可以看到如下图片

    结果.jpg

     

    其中,标红的那一行可以看到,直接将私有的方法也可显示出来。这一个是非常有用的。那么这些代码就可以很明显

     

    的让我们知道,在Java运行时,就可以判断任意一个对象所属类。

     

    接下来,我们来看一下反射的相关代码:

     

    public class InvokeTester {
        public int add(int param1, int param2) {
            return param1 + param2;
        }
    
        public String echo(String msg) {
            return msg + "hello";
        }
    
        public static void main(String[] args) throws Exception {
            Class<?> classType = InvokeTester.class; //获取类对象
    
            Object invokeTester = classType.newInstance();//创建此 Class 对象所表示的类的一个新实例。
            //调用前提是 必须有一个不带参数的构造器
    
            //以上两行代码就相当于new invokeTester
    
            Method addmethod = classType.getMethod("add", int.class, int.class);//确定这个方法需要输入 方法名 参数
    
            Object result = addmethod.invoke(invokeTester, new Object[]{100, 200});
            //以上两行相当于i.add(100,200)
            System.out.println((Integer) result);
    
            Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
            Object result2 = echoMethod.invoke(invokeTester, "hello world");
            System.out.println((String) result2);
        }
    }

    在这里,着重说获取类对象时,创建的class对象新的实例时,必须有一个不带参数的构造器,否则会报错,那么怎

     

    没有应该怎么办呢?下面会讲到。确认一个方法,我们通过重载机制就可以知道,需要通过这个方法的名字和他的参

     

    数来确认。上一个我们通过classType.getDeclareMethods()来获取了所有方法,而现在我们可以通过

     

    classType.getMethods()方法来获得这个我们需要已知方法的名字,在这里参数类型是int.class,String.class.在之

     

    后,我们通过revoke方法,来获得我们调用方法的结果,invoke方法中的两个参数,第一个参数是我们需要调用的对

     

    象,第二个参数就是调用方法的参数,这个参数应该与getmethod方法的参数所写的类型一致,下面我们来看一下结

     

    果:

    Snipaste_2020-01-10_11-55-11.jpg

    我们通过这个例子,就可以发现,通过反射机制我们完全可以调用一个方法。

     

    接下来,整一个复杂点的!

    public class ReflectTester {
    
    
        public Object copy(Object object) throws Exception {
            Class<?> classType = object.getClass();
    
            System.out.println(classType.getName());
    
            Object objectCopy = classType.getConstructor(new Class[]{})
                    .newInstance(new Object[]{});
            //相当于是 object object2 = classType.newInstance();
    
            //获得对象的所有属性
            Field[] fields = classType.getDeclaredFields();
    
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
    
                String fieldName = field.getName();
    
                //获得属性的首字母并转换为大写
                String firstLetter = fieldName.substring(0, 1).toUpperCase();
                //获得和属性对应的getxxx()方法
                String getMethodName = "get" + firstLetter + fieldName.substring(1);
                //获得个属性对应的setxxx()方法
                String setMethodName = "set" + firstLetter + fieldName.substring(1);
    
                //获得get方法
                Method getMethod = classType.getMethod(getMethodName, new Class[]{});
    
                //获得set方法
                Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});
    
    
                Object value = getMethod.invoke(object, new Object[]{});
    
                System.out.println(fieldName + ":" + value);
    
                setMethod.invoke(objectCopy, new Object[]{value});
            }
            return objectCopy;
        }
    
        public static void main(String[] args) throws Exception {
    
            Customer customer = new Customer();
    
            customer.setId(new Long(1));
            customer.setName("zhangsan");
            customer.setAge(20);
    
            Customer customerCopy = (Customer) new ReflectTester().copy(customer);
    
            System.out.println(customerCopy.getId() + "," + customerCopy.getName() + "," + customerCopy.getAge());
        }
    
    }
    
    
    class Customer {
        private long id;
    
        private String name;
    
        private int age;
    
        public Customer() {
        }
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
    this.age = age;
        }
    }

    以上代码创造了一个Customer类,并通过反射进行了一次copy,调用getter,setter方法,成功。此处一定要在

     

    Customer类中写入构造器,否则容易造成权限不通过导致没有办法读取。我们可以看到  Object object copy =

     

    classType.getConstructor(new Class[]{}) .newInstance(new Object[]{});这一行代码就相当于是object object2 =

     

    classType.newInstance();如果没有无参构造器时,则可运用这个方法

    Snipaste_2020-01-10_11-59-09.jpg

     
     
  • 相关阅读:
    9.1、PHP 中的函数
    7.2.2、二维数组中包含自定义键数组的打印
    Windows 8 VHD 概述与使用
    8.2、磁盘、目录和文件计算
    7.2.6、随机取出数组中的某个下标
    7.2.3、数组排序
    7.2.7、数组指针的操作
    CentOS6 下 JDK7 + jBoss AS 7 环境搭建
    How to iterate HashMap using JSTL forEach loop
    windows 8 非内置系统管理员获得完整权限的方法
  • 原文地址:https://www.cnblogs.com/xiaobaoa/p/12178508.html
Copyright © 2020-2023  润新知