美文网首页
【转】Java编程学习之反射技术及其应用

【转】Java编程学习之反射技术及其应用

作者: FoxLayla | 来源:发表于2019-03-12 20:29 被阅读0次

    【转】Java编程学习之反射技术及其应用

    (原文链接:java编程学习之反射技术及其应用)

    说明:原文中给出的代码示例由于格式问题不易阅读,本文进行了整理,并修改了部分例子。

    另,本文未整理“泛型的本质”和“动态代理和AOP”这两节,这些内容请参考原文。


    翻阅了很多资料想查看一个比较通俗易懂的关于Java反射机制的理解,但是想要从概念中去理解一项技术,可能有点困难,所以先将理论型知识贴出来,后面,慢慢来理解。

    反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

    注意:要理解Java的反射机制,先要了解以下基本概念:运行时,编译时,编译型,解释型,类加载器,动态加载类。

    Class类的使用

    概念理解

    在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。

    获取Class实例的方式

    不能直接创建Class的实例对象,因为Class类的构造方法是私有的,只有jvm可以去创建,因此获取Class实例的几种方式为:

    • 利用对象调用getClass()方法获取该对象的Class实例;

    • 使用Class类的静态方法forName(),用类的名字获取一个Class实例;

    • 运用.class的方式获取Class实例,对于基本数据类型的封装类,还可以采用TYPE来获取对应的基本数据类型的Class实例;

    • 使用类的加载器ClassLoader的loadClass()方法获取一个Class实例;

    综上所述,其实我们代码中创建的每一个类都是一个对象,只不过它是Class类的实例对象,这个对象我们称为该类的类类型。并且一个类只可能是Class类的一个实例对象,即获取的类类型是相同的

    那么,如何去创建Class的实例呢?

    首先,过程要理解,源文件经过编译(javac.exe)以后,得到一个或多个.class文件。.class文件经过运行(java.exe)这步,就需要进行类的加载(通过JVM的类的加载器),记载到内存中的缓存。每一个放入缓存中的.class文件就是一个Class的实例!下面是创建Class实例的三种方法。

    实例如下:

    public class ReflectTest {     
        public static void main(String[] args){ 
            ReflectTest reflectTest = new ReflectTest();
            
            // 类实例的getClass()方法
            Class clazz1 = reflectTest.getClass();
            
            // 类的.class
            Class clazz2 = ReflectTest.class; 
            
            // Class类的forName()方法
            Class clazz3 = null; 
            try { 
                clazz3 = Class.forName("com.william.test.ReflectTest"); 
            } catch (ClassNotFoundException e) { 
                e.printStackTrace(); 
            } 
            
            // 类加载器
            ClassLoader loader = this.getClass().getClassLoader(); 
            Class clazz4 = null; 
            try { 
                clazz4 = loader.loadClass("com.william.test.ReflectTest"); 
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    根据类类型去创建类的实例

    ReflectTest reflectTest1 = null;
    try { 
        reflectTest1 = (ReflectTest) clazz1.newInstance();//需要类有无参的构造方法
    } catch (InstantiationException e | IllegalAccessException e) { 
        e.printStackTrace();
    }
    

    动态加载类

    Class.forName("类的全称")表示类的类类型,还代表了动态加载类

    区分上面所说的“编译”和“运行”,编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

    关于动态加载类的实例代码请参考:2-1 Java动态加载类

    获取方法信息

    获取方法的信息,主要通过Method类数组来接受getMethods()方法的返回值,然后进行遍历解析。

    /*打印类的信息,包括类的成员函数,成员变量*/
    public class ClassUtil {
        public static void printMethodMessage(Object object) { 
            //获取类的类类型 
            Class clazz = object.getClass();
            System.out.println("类的名称是:" + clazz.getName());
            
            /* Method类,方法对象 
             * 一个成员方法就是一个Method对象 
             * getMethods()方法获取的是所有的public的函数,包括父类继承而来的 
             * getDeclaredMethods()获取的是所有该类自己声明的方法,不论访问权限 */ 
            Method[] methods = clazz.getDeclaredMethods(); 
            for (int i = 0; i < methods.length; i++) { 
                //1. 获取注解 
                Annotation[] ann = methods[i].getAnnotations(); 
                for (Annotation a : ann) { 
                    System.out.println(a); 
                } 
                //2. 获取权限修饰符 
                String str = Modifier.toString(methods[i].getModifiers()); 
                System.out.print(str + " "); 
                //3. 得到方法的返回值类型的类类型 
                Class returnType = methods[i].getReturnType(); 
                System.out.print(returnType.getName() + " "); 
                //4. 得到方法的名称 
                System.out.print(methods[i].getName() + "("); 
                //5.获取参数类型-->得到的是参数列表的类型的类类型 
                Class[] paramTypes = methods[i].getParameterTypes(); 
                //解析数组 
                for (int j = 0; j < paramTypes.length; j++) { 
                    if (j == 1 || j == paramTypes.length - 1) {
                        System.out.print(paramTypes[j].getName() + " args" + j); 
                    } else { 
                        System.out.print(paramTypes[j].getName() + " args" + j + ","); 
                    } 
                } 
                System.out.print(")"); 
                //6.获取异常类型 
                Class[] exps = methods[i].getExceptionTypes(); 
                if (exps.length != 0) { 
                    System.out.print(" throws "); 
                } 
                for (int k = 0; k < exps.length; k++) {
                    System.out.print(exps[k].getName() + " "); 
                } 
                System.out.println(); 
            } 
        } 
    }
    
    public class Person {
        private final String name;
        private final int age;
        private List<Person> friends;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
            this.friends = new ArrayList<>();
        }
    
        @Override
        public String toString() {
            return "[name: " + this.name + ", age: " + this.age + "]";
        }
    
        public void beFriend(Person person) {
            this.friends.add(person);
        }
    }
    
    //测试
    public class Main {
    
        public static void main(String[] args) {
            Person person = new Person("zhang", 23);
    
            ClassUtil classUtil = new ClassUtil();
            ClassUtil.printMethodMessage(person);
        }
    }
    
    //output
    类的名称是:main.java.company.model.Person
    public java.lang.String toString()
    public void beFriend(main.java.company.model.Person args0)
    

    获取成员变量信息

    获取成员变量的信息,同获取方法的信息类似,只不过使用Field类数组来接收getFieldss()方法的返回值,然后进行解析。

    /*获取成员变量*/
    public static void printFieldMessage(Object object) {
        //获取类的类类型 
        Class clazz = object.getClass();
        System.out.println("类的名称是:" + clazz.getName()); 
        
        /* 成员变量也是对象 
         * java.lang.reflect.Field 
         * Field类封装了关于成员变量的操作 
         * getFields()方法获取的是所有的public的成员变量的信息 
         * getDeclaredFields获取的是该类自己声明的成员变量的信息 */ 
        Field[] fields = clazz.getDeclaredFields(); 
        for (Field field : fields) { 
            //获取每个属性的权限修饰符 
            int i = field.getModifiers(); 
            String modifier = Modifier.toString(i); 
            //得到成员变量的类型的类类型 
            Class fieldType = field.getType(); 
            String typeName = fieldType.getName(); 
            //得到成员变量的名称 
            String fieldName = field.getName(); 
            System.out.println(modifier + " "+ typeName + " " + fieldName); 
        }
    }
    
    //测试
    public class Main {
    
        public static void main(String[] args) {
            Person person = new Person("zhang", 23);
    
            ClassUtil classUtil = new ClassUtil();
            ClassUtil.printFieldMessage(person);
        }
    }
    
    //output
    类的名称是:main.java.company.model.Person
    private final java.lang.String name
    private final int age
    private java.util.List friends
    

    获取构造函数

    /*获取对象的构造函数的信息*/
    public static void printConMessage(Object object) { 
        //获取类的类类型 
        Class clazz = object.getClass();
        System.out.println("类的名称是:" + clazz.getName());
        
        /* 构造函数也是对象 
         * java.lang.Constructor中封装了构造函数的信息 
         * getConstructors获取所有的public的构造函数 
         * getDeclaredConstructors得到所有的构造函数 */ 
        Constructor[] constructors = clazz.getConstructors(); 
        for (Constructor constructor : constructors) {
            System.out.print(constructor.getName() + "("); 
            //获取构造函数的参数列表--->得到的是参数列表的类类型 
            Class[] paramTypes = constructor.getParameterTypes(); 
            for (Class clazz1 : paramTypes) { 
                System.out.print(clazz1.getName() + ","); 
            } 
            System.out.println(")"); 
        }
    }
    
    //测试
    public class Main {
    
        public static void main(String[] args) {
            Person person = new Person("zhang", 23);
    
            ClassUtil classUtil = new ClassUtil();
            ClassUtil.printConMessage(person);
        }
    }
    
    //output
    类的名称是:main.java.company.model.Person
    main.java.company.model.Person(java.lang.String,int,)
    

    方法的反射

    方法的名称和方法的参数列表才能唯一决定某个方法

    方法反射的操作

    method.invoke(对象,参数列表)

    示例:

    public class Main {
    
        public static void main(String[] args) {
            Person person1 = new Person("zhang", 23);
            Person person2 = new Person("wang", 23);
    
            Class clazz = person1.getClass();
            try {
                Method method = clazz.getMethod("beFriend", Person.class);
                Object object = method.invoke(person1, person2);
            } catch (NoSuchMethodException | 
                     IllegalAccessException | 
                     InvocationTargetException e) {
                e.printStackTrace();
            }
    
            System.out.println(person1.getFriends());
    
        }
    }
    

    成员变量的反射

    try {
        Class personClass = Class.forName("main.java.company.model.Person");
        Object personObject = personClass.newInstance();
        Person person = (Person) personObject;
    
        Field name = personClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(person, "li");
    
        Field age = personClass.getDeclaredField("age");
        age.setAccessible(true);
        age.set(person, 20);
    
        System.out.println(person);
    } catch (ClassNotFoundException |
             IllegalAccessException |
             InstantiationException |
             NoSuchFieldException e) {
        e.printStackTrace();
    }
    

    构造函数的反射

    Class clazz2 = Person.class;
    try {
        Constructor constructor = clazz2.getConstructor(String.class, int.class);
        Person person3 = (Person) constructor.newInstance("zhao", 19);
        System.out.println(person3);
    } catch (NoSuchMethodException |
             IllegalAccessException |
             InstantiationException |
             InvocationTargetException e) {
        e.printStackTrace();
    }
    

    反射应用之动态代理

    动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

    interface Subject {
        void action();
    }
    
    //被代理类
    class SubjectImpl implements Subject {
        @Override 
        public void action() {
            System.out.println("我是被代理类"); 
        }
    }
    
    class MyInvocationHandler implements InvocationHandler {
        Object object;
        
        //给被代理的对象实例化,并返回一个代理类的对象
        public Object blind(Object object) {
            this.object = object; 
            return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this); 
        } 
        
        //当通过代理类的对象发起对被重写的方法的调用时,都会转化为对如下的invoke方法的调用 
        @Override 
        public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
            //method方法的返回值是returnVal 
            return method.invoke(object, args); 
        }
    }
    
    public class ProxyTest { 
        public static void main(String[] args){ 
            //1. 创建被代理类对象 
            SubjectImpl realSubject = new SubjectImpl(); 
            //2. 创建一个实现了InvocationHandler接口的类的对象
            MyInvocationHandler handler = new MyInvocationHandler(); 
            //3. 调用blind()方法,返回一个同样实现了Subject接口的代理类的对象
            Object object = handler.blind(realSubject); 
            Subject subject = (Subject) object;
            subject.action();
        }
    }
    

    说明,了解动态代理和静态代理的区别,所谓的静态代理,其代理类和目标对象的类在编译期间就确定下来,不利于程序的扩展。即,每一个代理类只能为一个接口服务,也就是说程序开发中会产生很多代理类。

    相关概念

    什么是编译?

    答:将原程序翻译成计算机语言,就是二进制代码。在Java中是将.java文件翻译成.class的字节码

    什么是编译时?

    答:将原程序翻译成计算机语言的过程中。将.java翻译为.class文件的过程

    什么是运行时?

    答:就是在启动这个程序的时候。在Java中是,类加载器加载.class文件,并交给JVM处理

    什么是编译型语言?

    答:将原程序一次性全部转换为二进制代码,然后执行程序

    什么是解释型语言?

    答:转换一句,执行一句,Java是既编译又解释的语言

    编译型语言和解释型语言的区别:

    答:编译型语言效率高,依赖于编译器,但是跨平台差,解释型的效率低,依赖于解释器,但跨平台强

    什么是类加载器?

    答:类加载器就是JVM中的类装载器,作用就是将编译好的.class字节码运到检查器进行安全检查的,检查通过后开始解释执行

    什么是运行时动态加载类?

    答:反射就是可以将一个程序(类)在运行的时候获得该程序(类)的信息的机制,也就是获得在编译期不可能获得的类的信息,因为这些信息是保存在Class对象中的,而这个Class对象是在程序运行时动态加载的

    它就是可以在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成对象。类在运行的时候,可以得到该类的信息,并且可以动态的修改这些信息,自己能看到自己,跟照镜子一样,class对象是在运行的时候产生的,通过class对象操作类的信息是在运行时进行的,当运行程序的时候,类加载器会加载真正需要的类,什么是真正需要的呢?就是该类真正起作用,如:有该类的对象实例,或该类调用了静态方法属性等。

    相关文章

      网友评论

          本文标题:【转】Java编程学习之反射技术及其应用

          本文链接:https://www.haomeiwen.com/subject/zvropqtx.html