java 反射

来源:互联网 发布:Java web 登录实现 编辑:程序博客网 时间:2024/05/16 10:19

使用场景

web中我不太清楚,android中,热修复时的原理,其实就是用的java反射,但不管咋样,学java的,了解java反射非常有必要!

什么是反射

反射定义

java官方文档给出了解释:

Reflection is the ability of a program to query and modify its state during the execution.

意为:反射是在程序运行时,能动态查看和修改它的状态。
在java中,我们能在程序运行时,获取它的属性,方法,修饰符,以及此类的父类。我们也能够在程序运行时,创建一个未知类的实例,并且设置或获取它的属性方法。
但是,在java中,我们不能在运行时改变它的数据结构,比如说,我们不能在程序运行时,往一个类或者对象里添加新的方法,也不能改变类中方法的代码。

反射API

反射中经常用到的几个类,如下所示:

类名 描述 java.lang.Class 表示在JVM中被类加载器加载的类。 java.lang.reflect.Field 表示一个类或者接口的属性。 java.lang.reflect.Constructor 表示一个类中的构造方法。 java.lang.reflect.Method 表示一个类或者接口中的方法。 java.lang.reflect.Modifier 解码一个类及其成员的访问修饰符。 java.lang.reflect.Array 程序运行时创建数组。

具体用途

  1. 获取对象类名;
  2. 获取包名,修饰符等等;
  3. 获取类中定义的方法,返回类型,修饰符,参数类型,参数名等等;
  4. 获取所有构造函数;
  5. 用它其中一个构造方法创建类;
  6. 通过方法名和方法参数类型调用方法;
  7. 在运行时动态创建数组,并动态操作此数组元素。

反射相关类的使用

java.lang.Class

先上代码:

package com.lxh;public class MyClass {    static {        System.out.println("Loading class MyClass...");    }}
package com.lxh;public class Main {    public static void main(String[] args) {        try {            String className = "com.lxh.MyClass";            boolean initialize = false;            ClassLoader cLoader = Main.class.getClassLoader();            Class c = Class.forName(className, initialize, cLoader);            className = "com.lxh.MyClass";            System.out.println("about to load");            // Will load and initialize the class            c = Class.forName(className, true, cLoader);        } catch (ClassNotFoundException e) {            System.out.println(e.getMessage());        }    }}

log如下:

about to loadLoading class MyClass...

Class.forName(String name, boolean initialize, ClassLoader loader)如果布尔值为false,表示不初始化名为name的类,当为true时,就会初始化。Class.forName(String name)其实也是调用的Class.forName(String name, boolean initialize, ClassLoader loader),只不过它中间默认的布尔值为true。当调用名为name的类时,name类中的静态代码块被加载。
注意:这里第一个参数name,必须是类的全名,包含包名,否则将调用不到该类。

java.lang.reflect.Field

反射类Field表示类中的属性,Field中有如下几种方法:

Field[] getFields()Field[] getDeclaredFields()Field getField(String name)Field getDeclaredField(String name)

getFields()表示所有该类所有修饰符为public的属性,以及该类所继承的父类所有修饰符为public的属性。
getDeclaredFields()表示该类任何修饰符的所有属性,但不包括父类中属性。
上代码:

class MySuperClass {    public int super_id = -1;    private String super_name = "Unknown";}class MyClass extends MySuperClass{    private int id = -1;    public String name = "Unknown";}public class Main {    public static void main(String[] args) {        Class<MyClass> c = MyClass.class;        // 获取MyClass类中所有的属性        ArrayList<String> fieldsDesciption = getDeclaredFieldsList(c);        System.out.println("Declared Fields for " + c.getName());        for (String desc : fieldsDesciption) {            System.out.println(desc);        }        //获取MyClass类中修饰符为public,及其父类中属性        fieldsDesciption = getFieldsList(c);        System.out.println("\nAccessible Fields for " + c.getName());        for (String desc : fieldsDesciption) {            System.out.println(desc);        }    }    public static ArrayList<String> getFieldsList(Class c) {        Field[] fields = c.getFields();        ArrayList<String> fieldsList = getFieldsDesciption(fields);        return fieldsList;    }    public static ArrayList<String> getDeclaredFieldsList(Class c) {        Field[] fields = c.getDeclaredFields();        ArrayList<String> fieldsList = getFieldsDesciption(fields);        return fieldsList;    }    public static ArrayList<String> getFieldsDesciption(Field[] fields) {        ArrayList<String> fieldList = new ArrayList<>();        for (Field f : fields) {            int mod = f.getModifiers() & Modifier.fieldModifiers();            //获取该属性的修饰符 public,int等等            String modifiers = Modifier.toString(mod);            //获取该属性的类型 int,String等等            Class<?> type = f.getType();            String typeName = type.getSimpleName();            // 获取该属性名            String fieldName = f.getName();            //拼接后加到集合中            fieldList.add(modifiers + "  " + typeName + "  " + fieldName);        }        return fieldList;    }}

打印的log如下:

Declared Fields for com.lxh.MyClassprivate  int  idpublic  String  nameAccessible Fields for com.lxh.MyClasspublic  String  namepublic  int  super_id

说明getDeclaredFields()
获取到了指定类中所有属性,而getFields()获取的是指定类及其父类中修饰符为public的属性。而我们在具体情境下,一般是用getDeclaredFields()方法比较多~

java.lang.reflect.Method

Method中有如下方法:

Method[]  getMethods()Method[]  getDeclaredMethods()Method getMethod(String name,  Class...  parameterTypes)Method getDeclaredMethod(String name,  Class...  parameterTypes)

跟Field类似,getMethods()返回该类中任何修饰符的方法,getDeclaredMethods()返回该类及其父类中修饰符为public的方法
上代码:

class MyClass<T> {  public MyClass(int i, int j, String s) {  }  public MyClass(T t) {  }  public int getInt(String a) {    return 0;  }}public class Main {  public static void main(String[] args) {    Class<MyClass> c = MyClass.class;    ArrayList<String> methodsDesciption = getDeclaredMethodsList(c);    System.out.println("Declared Methods  for " + c.getName());    for (String desc : methodsDesciption) {      System.out.println(desc);    }    methodsDesciption = getMethodsList(c);    System.out.println("\nMethods for  " + c.getName());    for (String desc : methodsDesciption) {      System.out.println(desc);    }  }  public static ArrayList<String> getMethodsList(Class c) {    Method[] methods = c.getMethods();    ArrayList<String> methodsList = getMethodsDesciption(methods);    return methodsList;  }  public static ArrayList<String> getDeclaredMethodsList(Class c) {    Method[] methods = c.getDeclaredMethods();    ArrayList<String> methodsList = getMethodsDesciption(methods);    return methodsList;  }  public static ArrayList<String> getMethodsDesciption(Method[] methods) {    ArrayList<String> methodList = new ArrayList<>();    for (Method m : methods) {      String modifiers = getModifiers(m);      Class returnType = m.getReturnType();      String returnTypeName = returnType.getSimpleName();      String methodName = m.getName();      String params = getParameters(m).toString();      String throwsClause = getExceptionList(m).toString();      methodList.add(modifiers + "  " + returnTypeName + "  " + methodName          + "(" + params + ") " + throwsClause);    }    return methodList;  }  public static ArrayList<String> getParameters(Executable exec) {    Parameter[] parms = exec.getParameters();    ArrayList<String> parmList = new ArrayList<>();    for (int i = 0; i < parms.length; i++) {      int mod = parms[i].getModifiers() & Modifier.parameterModifiers();      String modifiers = Modifier.toString(mod);      String parmType = parms[i].getType().getSimpleName();      String parmName = parms[i].getName();      String temp = modifiers + "  " + parmType + "  " + parmName;      if (temp.trim().length() == 0) {        continue;      }      parmList.add(temp.trim());    }    return parmList;  }  public static ArrayList<String> getExceptionList(Executable exec) {    ArrayList<String> exceptionList = new ArrayList<>();    for (Class<?> c : exec.getExceptionTypes()) {      exceptionList.add(c.getSimpleName());    }    return exceptionList;  }  public static String getModifiers(Executable exec) {    int mod = exec.getModifiers();    if (exec instanceof Method) {      mod = mod & Modifier.methodModifiers();    } else if (exec instanceof Constructor) {      mod = mod & Modifier.constructorModifiers();    }    return Modifier.toString(mod);  }}

log打印如下:
这里写图片描述

java.lang.reflect.Constructor

跟Method和Field类似~

通过反射给方法赋值

直接上代码:

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;class MyClass {    public MyClass() {    }    private String name;    public void setName(String name) {        this.name = name;        System.out.println("setName():"+name);    }    public String getName(){        return name;    }}public class Main {    public static void main(String[] args) {        Class<MyClass> myClass = MyClass.class;        try {            //通过class.newInstance()实例化对象            MyClass p = myClass.newInstance();            //给对象中方法赋值            Method setName = myClass.getMethod("setName", String.class);            setName.invoke(p, "luoxiaohui");            //获取赋值后getName()中的返回值,不需要参数.            Method getName = myClass.getMethod("getName");            System.out.println(getName.invoke(p));        } catch (InstantiationException | IllegalAccessException                | NoSuchMethodException | SecurityException | IllegalArgumentException                | InvocationTargetException e) {            System.out.println(e.getMessage());        }    }}

注意:getMethod(String methodName,Class… parameterType),第一个参数表示方法名,后面范型表示方法的需要传的值,这里一定要跟原来方法相匹配,有参数就传参数。比如setName()方法需要传字符串型,参数则为String.class,而getName()不需要传。如果多传或者少传参数,将对应不上类中的方法。

通过反射给属性赋值

前面我们提到的Field的方法getField()和getDeclaredField(),只是获取属性的修饰符,属性类型,属性名,这里来说说给属性具体赋值。
上代码:

import java.lang.reflect.Field;class MyClass {  private String name = "Unknown";  public MyClass() {  }  public String toString() {    return "name=" + this.name;  }}public class Main {  public static void main(String[] args) {    Class<MyClass> my = MyClass.class;    try {      MyClass p = my.newInstance();      Field nameField = my.getDeclaredField("name");      //Field.setAccessible()方法要注意      nameField.setAccessible(true);      String nameValue = (String) nameField.get(p);      System.out.println("Current name is " + nameValue);      nameField.set(p, "luoxiaohui");      nameValue = (String) nameField.get(p);      System.out.println("New name is " + nameValue);    } catch (InstantiationException | IllegalAccessException        | NoSuchFieldException | SecurityException | IllegalArgumentException e) {      System.out.println(e.getMessage());    }  }}

注意:在获取到Class的Field属性后,记得调用方法Field.setAccessible(true),这样能保证不管该属性是public,private,还是缺省,都能修改它,如果没调用此方法,则只能修改修饰符为public的属性。

通过反射扩展数组

java.lang.reflect.Array中有两个方法

Object newInstance(Class<?> componentType,  int  arrayLength)Object newInstance(Class<?> componentType,  int... dimensions)

前者表示实例化一个长度为arrayLength的数组,后者表示实例化一个数组矩阵,后者此篇暂时不讲。以前在书上看到说数组实例化后,是不能改变起长度的,不过,通过反射,还是可以滴~
上代码:

import java.lang.reflect.Array;import java.util.Arrays;public class Main {    private static int[] ids = new int[2];    public static void main(String[] args) {        ids = new int[2];        System.out.println(ids.length);        System.out.println(Arrays.toString(ids));        //将已经实例化的数组ids扩展两个单位长度        ids = (int[]) expandBy(2);        //给数组中某个对象赋值        ids[2] = 3;        ids[0] = 5;        System.out.println(ids.length);        System.out.println(Arrays.toString(ids));    }    public static Object expandBy(int increment) {        Object newArray = null;        //获取数组ids的长度        int oldLength = Array.getLength(ids);        //定义新的长度        int newLength = oldLength + increment;        Class<?> c = ids.getClass();        //此方法很重要,对实例化数组通过反射扩展其长度        newArray = Array.newInstance(c.getComponentType(), newLength);        return newArray;    }}

log打印如下:

2[0, 0]4[5, 0, 3, 0]

数组成功扩展!

关于java中反射已经讲完,欢迎各位交流指教!

2 0