美文网首页
Java 反射机制

Java 反射机制

作者: zone7_ | 来源:发表于2017-06-21 00:59 被阅读16次

    Java 反射机制也是进阶的知识点,在 Android 中有挺多地方用到,虽然我们自己写代码会比较少用到,但是这对阅读一些源码是挺有帮助的。就比如:RemoteView 中的一系列 set 方法,其最终就是通过反射机制去实现的。

    什么是 Java 反射机制

    Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
    或许看完上面这段话之后还有点蒙蔽,我举个栗子:

    User user = new User();
    

    假如我已经有个 User 类,那么我可以利用反射机制,来知道 User 类里面有什么成员变量和方法,并且我能够调用这些变量和方法。

    反射机制的简单运用

    首先,大概浏览一下下面的代码。然后,思考一个问题:
    我们写 Java 代码这么久了,我们新建的类(例如上面的:Reflect 类,User 类)都是谁的对象?
    初次看到这个问题,我是蒙蔽的。搜寻答案之后,是 java.lang.Class 的对象,而我们的 Reflect 类和 User 类也有另外一种称呼,就是类的类型(class type)。而这就跟我们要说的反射机制跟这个 class type 有关系。下面这段代码就是反射的简单运用,有注释在旁边,应该也挺容易理解的。

    package com.zone;
    public class Reflect {
        public static void main(String[] args) {
    //      类是 java.lang.Class 的对象
            User user = new User();
    //      方法一
            Class userAlias1 = User.class;//获取类的类型
            User user1;
            try {
                user1 = (User)userAlias1.newInstance();//通过类的类型获取实例
                user1.printName("create by userAlias1");//通过实例调用方法
            } catch (InstantiationException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IllegalAccessException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
    //      方法二
            Class userAlias2 = user.getClass();//获取类的类型
            try {
                User user2 = (User)userAlias2.newInstance();//通过类的类型获取实例
                user2.printName("create by userAlias2");//通过实例调用方法
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    //      方法三
            Class userAlias3 = null;
            try {
                userAlias3 = Class.forName("com.zone.User");//获取类的类型
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                User user3 = (User)userAlias3.newInstance();//通过类的类型获取实例
                user3.printName("create by userAlias3");//通过实例调用方法
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    class User{
        public void printName(String message){
            System.out.println("My name is zone. "+ message);
        }
    }
    

    那么我们稍微解释一下方法一:

    //      方法一
            Class userAlias1 = User.class;//获取类的类型
            User user1;
            try {
                user1 = (User)userAlias1.newInstance();//通过类的类型获取实例
                user1.printName("create by userAlias1");
            } catch (InstantiationException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IllegalAccessException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
    

    userAlias1 相当于 User 类的别名,userAlias1 还是指向 User 类的,因此我们可以通过它来获取实例。当然看成为别名是我个人的看法,只是为了更好理解。

    user1 = (User)userAlias1.newInstance();//通过类的类型获取
    

    获取方法的名称,参数类型,返回类型

        public static void printClassMessage(Object obj){
            Class c = obj.getClass();
            System.out.println("类的名称是:"+c.getName());
    //      获取父类和本类的方法
            Method[] ms=c.getMethods();
    //      获取本类的方法
    //      c.getDeclaredMethods();
            for (int i = 0; i < ms.length; i++) {
    //          获取方法的返回值的类的类型(class type)
                Class returnType = ms[i].getReturnType();
                System.out.print(returnType.getSimpleName()+" ");
    //          获取方法的名字
                System.out.print(ms[i].getName()+"(");//这一句是为了打印出来更美观
    //          获取参数的类的类型
                Class[] paramTypes = ms[i].getParameterTypes();
                for (int j = 0; j < paramTypes.length; j++) {
                    if (j==paramTypes.length-1) {
                        System.out.print(paramTypes[j].getName());
                    }else {
                        System.out.print(paramTypes[j].getName()+",");
                    }
                }
                System.out.println(");");//这一句是为了打印出来更美观
                
            }
        }
    

    这里写了一个方法,通过传入一个实例,来获取类的名字,类的方法,方法的参数和返回值。来看看将 user 实例传进去,会有什么打印出来:

    User user = new User();
    printClassMessage(user);
    

    以下为控制台打印出的数据:

    类的名称是:com.zone.User
    void printName(java.lang.String);
    void wait();
    void wait(long,int);
    void wait(long);
    boolean equals(java.lang.Object);
    String toString();
    int hashCode();
    Class getClass();
    void notify();
    void notifyAll();
    

    可以看到第一个方法是我们自己写的方法 void printName(java.lang.String);

    获取成员变量的类型和名称

        private static void printFieldsMessage(Object obj) {
            Class c = obj.getClass();
            Field[] fs = c.getFields();//获取 public 变量
    //      Field[] fs = c.getDeclaredFields();//获取所有成员变量
            for (Field field : fs) {
                Class fieldType = field.getType();
                String typeName  = fieldType.getName();//获取成员变量的类型属性的 class type
                String fieldName = field.getName();//获取成员变量的名称
                System.out.println(typeName+fieldName);
            }
        }
    

    下面为 User 类添加如下代码:

    public String name = "zone";
    

    然后传入 user 实例运行,运行结果如下:

    java.lang.String  name
    

    获取构造函数信息

        private static void printConstructorMessage(Object obj){
            Class c  = obj.getClass();
    //      Constructor[] cs = c.getConstructors();//获取 public 修饰的构造方法
            Constructor[] cs = c.getDeclaredConstructors();//获取所有的构造方法
            for (Constructor constructor : cs) {
                System.out.print(constructor.getName()+"(");//输出构造函数名
                Class[] paramTypes = constructor.getParameterTypes();//获取构造函数参数类型
                for (Class class1 : paramTypes) {
                    System.out.print(class1.getName()+",");//输出参数名
                }
                System.out.println(")");
            }
        }
    

    通过反射调用方法

    接下来为 User 类添加如下方法:

        public void printStr(String str1,String str2){
            System.out.println(str1+" && "+str2);
        }
    

    然后通过反射来调用方法:

            User user = new User();
            Class c1 = user.getClass();
            try {
                Method m = c1.getDeclaredMethod("printStr", String.class,String.class);//获取 public 方法
    //          Method m2 = c1.getMethod("printStr", String.class,String.class);
    //          Method m3 = c1.getDeclaredMethod("printStr", new Class[]{String.class,String.class});//获取自己声明的方法
                m.invoke(user, "first","second");//通过反射调用方法
            } catch (NoSuchMethodException e2) {
                e2.printStackTrace();
            } catch (SecurityException e2) {
                e2.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    

    浏览上面代码之后,先来说说 getDeclaredMethod() 的参数,第一个参数是要调用的方法的名字。从第二个参数开始,是要放反射方法的参数的类的类型,这有两种方式。第一种: new 一个类的类型(class type) 的数组,数组里面放反射的方法的参数的类的类型(class type)。第二种: 直接开始放反射方法的参数的类的类型(class type)。文字描述起来有点绕,拿上面的例子解说一下:

    Method m3 = c1.getDeclaredMethod("printStr", new Class[]{String.class,String.class});
    

    "printStr"是我要反射的方法。String.class,String.class,是 printStr(String str1,String str2) 方法的参数的类的类型。

    java 反射学到这里就告一段落了。等待发现更多知识点的你一起分享。

    我的公众号

    公众号内容描述

    zone7 公众号,与您一起学习分享后端知识。本公众号涉及的知识点将会有:nodejs,python,docker,kubernetes,后端架构等


    image

    相关文章

      网友评论

          本文标题:Java 反射机制

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