java 反射机制

来源:互联网 发布:js 数组存在值 编辑:程序博客网 时间:2024/06/05 20:55

0、前言及思路

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

–摘自:百度百科-JAVA反射机制

  java的反射机制是java SE的内容,但是却在许多框架中出现使用,java反射机制可以接触到一些程序不允许你接触的地方,对于java反射机制的这一特性是存在一些争议的,有人支持,有人反对。

  对于java的反射机制,核心是Class类,这个Class类也在java虚拟机JVM很始终要的,因为一个类的信息会包装成一个Class类,同时这个Class类也是java反射机制的入口,从我的学习中,我发现,许多操作都是围绕着Class展开的。

  从Class类中我们可以获得一些信息,从一个类的上到下,可列出信息如下:
Class ↓
- 1、包信息
- 2、类修饰符、类名、父类、多接口
- 3、成员变量 -> 获取(get)和设置(set)成员变量
- 4、构造方法 -> 执行构造方法
- 5、成员方法 -> 调用方法

其中可以发现一些规律:

  • 这些信息都是在Class类中获得的,那么写java反射的时候第一时间应该是拿到需要的Class类实例
  • 接着获取一些如上1、2、3、4、5的信息
    • 获得完信息就完毕的,比如1、2的信息,我们已经不能再干什么了
    • 获取完还是一个信息类,而这个类可以执行方法,完成一些功能的,比如3、4、5

下面开始实践。注意Constructor、Field、Method类。
如果获取Constructor、Field、Method这些类的是私有信息,记得用setAccessible方法设置Accessible为true,再去使用方法这些是有属性或者方法。

1、构造一个被反射的类

先构建如下一个提供调用的类:

public class Reflection extends Thread implements Serializable {    // 静态常量属性    private static final long serialVersionUID = 1L;    // 静态属性    private static Reflection instance;    // 普通私有属性    private String threadName;    // 这个属性是是了验证get方法的,ide自动生成的形式    private boolean isRunning;    // 私有构造方法    private Reflection() {        this.threadName = currentThread().getName();    }    // 这个构造方法仅代码需要    public Reflection(final String threadName, boolean isRunning) {        super();        this.threadName = threadName;        this.isRunning = isRunning;    }    // 类方法    public static Reflection getInstance() {        if (instance == null) {            synchronized (Reflection.class) {                if (instance == null) {                    instance = new Reflection();                }            }        }        return instance;    }    // 普通方法    @Override    public void run() {        // TODO Auto-generated method stub        System.out.println("run");    }    // 下面是自动生成的getter和setter方法,用于属性的取值和赋值    public boolean isRunning() {        return isRunning;    }    public void setRunning(boolean isRunning) {        this.isRunning = isRunning;    }    public String getThreadName() {        return threadName;    }    public void setThreadName(String threadName) {        this.threadName = threadName;    }    public static long getSerialversionuid() {        return serialVersionUID;    }    // toString方法在在调用构造方法的时候容易看到属性值    @Override    public String toString() {        return "Reflection [threadName=" + threadName + ", isRunning=" + isRunning + "]";    }    //私有方法    private String printMSG(String who){        return who+" print Reflection [threadName=" + threadName + ", isRunning=" + isRunning + "]";    }}

2、构造Class类

构造Class类的方法有三个:

  • Class.forName(“包名.类名”) -> Class.forName(“space.xxhui.Reflection”);
  • 类名.class -> Reflection.class
  • 对象名.getClass() -> Reflection.getInstance().getClass();

下面是举例子,代码如下:

        Class cForName = null;        Class cForClass = Reflection.class;        Reflection r = Reflection.getInstance();        Class cForRuntime = r.getClass();        try {            cForName = Class.forName("space.xxhui.Reflection");        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }

3、获取包信息

其实很简单就getPackage().getName(),即可

            // 1、先获得Package类,再获得包名            System.out.println("***** start print packageName *****");            String p1 = cForName.getPackage().getName();            String p2 = cForClass.getPackage().getName();            String p3 = cForRuntime.getPackage().getName();            System.out.println(p1);            System.out.println(p2);            System.out.println(p3);            System.out.println("***** stop print packageName *****");

结果:


这里写图片描述


这里写图片描述

4、获取类修饰符、类名、父类、多接口

4.1、类修饰符和类名

            System.out.println("***** start print classModify and class name ****");            // 2.1、返回这个类或接口的Java语言修饰符返回整数编码            int modifyCode = cForClass.getModifiers();            // 2.2、使用Modifier类的方法进行解码。            String modifyType = Modifier.toString(modifyCode);            // 3、获取当前类名            String name = cForName.getName();            System.out.println("\n"+modifyType +" - class - "+ name +"\n");            System.out.println("***** stop print classModify and class name ****");

结果:


这里写图片描述


这里写图片描述

4.2、获取父类和多接口

            System.out.println("***** start print super class and interfaces ****");            // 4、获得当前父类名字(java是单继承,所以方法只返回单值)            String super1 = cForName.getSuperclass().getName();            System.out.print("\n extends - "+super1+" - implements - ");            // 5、获得所有接口,并打印它们的名字(java可以实现多个接口,所以返回值是数组)            for (Class cInterface : cForName.getInterfaces()) {                System.out.print(cInterface.getName()+" - ");            }            System.out.println("\n");            System.out.println("****** stop print super class and interfaces *****");

结果:


这里写图片描述


这里写图片描述

4、获取成员变量

            System.out.println("****** 打印成员变量  *****");            System.out.println(">>>>>> 打印全部声明的成员变量");            // 6.1、获得属性的声明信息            // 注意:用的是cForRunTime            // 获取属性类型的方法用的是:cField.getGenericType().getTypeName()            for (Field cField : cForRuntime.getDeclaredFields()) {                System.out.print(Modifier.toString(cField.getModifiers()) + " - "                        + cField.getGenericType().getTypeName() + " _ " + cField.getName());                // 对于 Field、Method 或 Constructor 对象,取消默认 Java 语言访问控制检查的能力                cField.setAccessible(true);                try {                    System.out.print(" = " + cField.get(r)+"\n");                } catch (IllegalArgumentException | IllegalAccessException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }            System.out.println();            // 6.2获取(get)和设置(set)特定的属性信息            try {                // instance为static属性                System.out.println(">>>>>> 打印static成员变量");                Field cField = cForName.getDeclaredField("instance");                if (cField != null) {                    System.out.println(Modifier.toString(cField.getModifiers()) + " _ " + cField.getType().getName()                            + " - " + " _ " + cField.getName());                }                // threadName为普通属性                System.out.println(">>>>>> 打印要get和set的成员变量");                cField = cForRuntime.getDeclaredField("threadName");                if (cField != null) {                    System.out.println(Modifier.toString(cField.getModifiers()) + " _ " + cField.getType().getName()                            + " _ " + " _ " + cField.getName());                    cField.setAccessible(true);                    System.out.println("查看设置前的值:threaName :"+cField.get(r));//获取属性                    cField.set(r, "sub thread");//设置属性                    System.out.println("查看设置后的值:threadName :"+cField.get(r));//查看是否设置成功                }            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println("****** 结束打印成员变量 *****");

结果:


这里写图片描述


这里写图片描述

5、获取构造方法

            System.out.println("****** 打印构造方法 *****");            System.out.println("获取公共构造方法:");            // 7.1获取构造方法(public的            // getConstructors方法返回所有pulic声明的构造方法            for (Constructor cConstructor : cForName.getConstructors()) {                System.out.print(Modifier.toString(cConstructor.getModifiers()) + " - " + cConstructor.getName());                System.out.print(" ( ");                for (Parameter cParameter : cConstructor.getParameters()) {                    System.out.print(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getType().getName() + " - " + cParameter.getName()+" _ ");                }                System.out.print(" ) \n");            }            System.out.println("打印所有声明的构造方法");            // 7.2获取构造方法(所有的            // getDeclaredConstructors方法返回类中声明的全部方法            for (Constructor cConstructor : cForName.getDeclaredConstructors()) {                System.out.print(Modifier.toString(cConstructor.getModifiers()) + " - " + cConstructor.getName());                System.out.print(" ( ");                for (Parameter cParameter : cConstructor.getParameters()) {                    System.out.print(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName()+" _ ");                }                System.out.print(" ) \n");            }            System.out.println();            // 7.3获取单个构造方法,并调用构造函数            try {                // 获取声明的无参构造函数                Constructor cConstructor = cForName.getDeclaredConstructor();                System.out.print(Modifier.toString(cConstructor.getModifiers()) + " - " + cConstructor.getName());                System.out.print(" ( ");                for (Parameter cParameter : cConstructor.getParameters()) {                    System.out.print(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName());                }                System.out.print(" ) \n");                // 无参的构造方法是私有的,先取消访问控制的能力                // 对于 Field、Method 或 Constructor 对象,取消默认 Java 语言访问控制检查的能力                cConstructor.setAccessible(true);                System.out.println("调用私有构造方法:"+cConstructor.newInstance().toString());                // 获取声明的有两个参数的构造函数                cConstructor = cForName.getDeclaredConstructor(new Class[] { String.class, boolean.class });                System.out.print(Modifier.toString(cConstructor.getModifiers()) + " - " + cConstructor.getName());                System.out.print(" ( ");                for (Parameter cParameter : cConstructor.getParameters()) {                    System.out.print(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName());                }                System.out.print(" ) \n");                System.out.println("调用公有构造方法:"+cConstructor.newInstance("sub Thread", false).toString());            } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException                    | IllegalArgumentException | InvocationTargetException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println("****** 构造方法打印结束 *****");

结果:


这里写图片描述


这里写图片描述

6、获取成员方法

            System.out.println("****** 打印成员方法 *****");            System.out.println("打印所有公有方法:   太长了不打印,知道即可");            // 8.1获取方法(public的,并且包括接口、父类本类的,打印出来好长啊//          for (Method cMethod : cForName.getMethods()) {//              System.out.print(Modifier.toString(cMethod.getModifiers()) + " - " + cMethod.getReturnType().getName()//                      + " - " + cMethod.getName());//              System.out.print(" ( ");//              for (Parameter cParameter : cMethod.getParameters()) {//                  System.out.print(Modifier.toString(cParameter.getModifiers()) + " - "//                          + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName());//              }//              System.out.print(" ) \n");//          }            System.out.println("打印所有声明的方法");            //8.2获取方法(所有声明的方法,包括private            for (Method cMethod : cForName.getDeclaredMethods()) {                System.out.println(Modifier.toString(cMethod.getModifiers()) + " - " + cMethod.getReturnType().getName()                        + " - " + cMethod.getName());                for (Parameter cParameter : cMethod.getParameters()) {                    System.out.println(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName());                }            }            System.out.println("打印单个方法,并调用:");            //8.3获取单个方法,并调用方法            try {                //获取一个public类型的无参方法                Method cMethod = cForName.getMethod("run", new Class[]{});                System.out.println(Modifier.toString(cMethod.getModifiers()) + " - " + cMethod.getReturnType().getName()                        + " - " + cMethod.getName()+" (  ) ");                //调用方法                Reflection reflection =(Reflection) cForName.getConstructor(new Class[] { String.class, boolean.class }).newInstance("sub Thread",false);                System.out.println("普通方法调用:");                cMethod.invoke(reflection, null);//这里参数为null代表无参数                System.out.println("//--------------------------------------------------");                //获取一个private类型的有参方法                cMethod = cForName.getDeclaredMethod("printMSG", new Class[]{String.class});                System.out.print(Modifier.toString(cMethod.getModifiers()) + " - " + cMethod.getReturnType().getName()                        + " - " + cMethod.getName());                System.out.print(" ( ");                for (Parameter cParameter : cMethod.getParameters()) {                    System.out.print(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName());                }                System.out.print(" ) \n");                // 由于是私有的,取消访问控制的能力                // 对于 Field、Method 或 Constructor 对象,取消默认 Java 语言访问控制检查的能力                cMethod.setAccessible(true);                System.out.println("普通方法调用:");                String result = (String) cMethod.invoke(reflection, "Hitvz");                System.out.println(result);                System.out.println("//--------------------------------------------------");                //调用一个类方法(注意:反射调用方法是区分大小写的,因为我用getinstance报错了,呵呵呵                cMethod = cForName.getMethod("getInstance", new Class[]{});                System.out.println(Modifier.toString(cMethod.getModifiers()) + " - " + cMethod.getReturnType().getName()                        + " - " + cMethod.getName());                for (Parameter cParameter : cMethod.getParameters()) {                    System.out.println(Modifier.toString(cParameter.getModifiers()) + " - "                            + cParameter.getParameterizedType().getTypeName() + " - " + cParameter.getName());                }                //调用静态方法,与普通方法的区别就是:invoke方法的第一个参数,普通方法是要一个具体的实例对象,而静态方法的话直接置null即可                System.out.println("静态方法调用:");                Reflection reflection2 = (Reflection) cMethod.invoke(null,null);                System.out.println(reflection2.toString());            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println("****** 成员方法结束打印 *****");


这里写图片描述
这里写图片描述


这里写图片描述

7、通过方法来设置成员变量

我自己写的方法:

public static void buildSetter(Object target, String name,Class[] clazzs ,Object[] objects){        if(clazzs[0].getName().equals("boolean")||clazzs[0].getName().equals("java.lang.Boolean")){            char letter = name.substring(2, 3).toCharArray()[0];            if(letter<'Z'&&letter>'A'){                name = name.substring(2);            }            name ="set"+ name.substring(0, 1).toUpperCase() + name.substring(1);        }else{            name ="set"+ name.substring(0, 1).toUpperCase() + name.substring(1);            //System.out.println(name);        }        //System.out.println(name);        Method m = null;        try {            m = target.getClass().getDeclaredMethod(name, clazzs);            m.invoke(target, objects);        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    // boolean修饰的属性,getter方法有is开头,不添加is,没有的话,添加is    // setter方法却是有is开头,去掉is,没有is,如常不增删    public static Object buildGetter(Object target ,String name){//由于不能获取返回类型,所以只能在方法中找了        String temp =name;        List<Method> list = new ArrayList<Method>();        for (Method method : target.getClass().getMethods()) {//找出每个方法            if(method.getParameterCount()==0){//找出方法中参数为0的方法                if(method.getReturnType().getName().equals("void")){//排除返回值为void的方法                    continue;                }                if(method.getName().startsWith("get")||method.getName().startsWith("is")){//选择出get和is开头的方法                    temp = name;                    if(method.getReturnType().getName().equals("boolean")||method.getReturnType().getName().equals("java.lang.Boolean")){//找出返回值为布尔类型的方法,再判断                        char letter = temp.substring(2, 3).toCharArray()[0];                        if(letter<'Z'&&letter>'A'){//第三个字母是大写                            if(method.getName().length()==temp.length()){                                temp = temp.substring(2);                                if(method.getName().contains(temp)){                                    list.add(method);                                }                            }                        }else{//不是大写                            temp = temp.substring(0, 1).toUpperCase() + temp.substring(1);                            if(method.getName().contains(temp)){                                list.add(method);                            }                        }                    }else {//找出返回值为非布尔类型的方法,再处理                        temp ="get"+ temp.substring(0, 1).toUpperCase() + temp.substring(1);                        if(method.getName().contains(temp)){                            if(method.getName().length()==temp.length()){                                list.add(method);                            }                        }                    }                }            }        }        if(list.size()==0){            System.out.println("no such getter!");            return null;        }        //System.out.println(list.size());        Method m = null;        Object o = null;        try {            m = target.getClass().getDeclaredMethod(list.get(0).getName(), new Class[]{});            o = m.invoke(target);        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return o;    }

调用:

            System.out.println("修改threadName");            try {                Reflection reflection =(Reflection) cForName.getConstructor(new Class[] { String.class, boolean.class }).newInstance("sub Thread",false);                buildSetter(reflection, "threadName", new Class[]{String.class}, new Object[]{"sub Thread"});                System.out.println("更改后的信息:"+reflection.toString());                Object o= buildGetter(reflection, "threadName");                System.out.println("获取threadName信息:"+o);            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }


这里写图片描述