java 反射

使用场景

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 load
Loading 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.MyClass
private  int  id
public  String  name

Accessible Fields for com.lxh.MyClass
public  String  name
public  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中反射已经讲完,欢迎各位交流指教!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值