使用场景
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 | 程序运行时创建数组。 |
具体用途
- 获取对象类名;
- 获取包名,修饰符等等;
- 获取类中定义的方法,返回类型,修饰符,参数类型,参数名等等;
- 获取所有构造函数;
- 用它其中一个构造方法创建类;
- 通过方法名和方法参数类型调用方法;
- 在运行时动态创建数组,并动态操作此数组元素。
反射相关类的使用
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中反射已经讲完,欢迎各位交流指教!