1、什么是反射机制
Java反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有的属性和方法;对于任何一个对象,
都能够调用它的任何一个方法和属性;这样动态获取信息以及动态调用对象方法的功能就叫做反射。
简单来说反射就是在运行状态解剖一个未知的.class文件,然后获取这个类中的属性和方法,前提是要获取这个类的Class对象
2、反射机制相关的类
| 类名 | 说明 |
|---|---|
| Class | 代表类的实体,在运行的Java应用程序中表示类和接口 |
| Field | 代表类的成员变量(成员变量也称为类的属性) |
| Method | 方法 |
| Constructor | 构造方法 |
3、获取Class对象的三种方式
- 通过类名.class获取Class对象。
- 通过对象.getClass()方法获取Class对象。
- 通过Class.forName(全类名)获取Class对象。
4、使用反射获取类信息
先写两个类备用SupperClass父类 子类SubClass
package top.soft1010.java.knowledge.point.reflect;
/**
* Created by zhangjifu on 19/10/8.
*/
public class SupperClass extends Object {
private Integer id;
private String name;
public static final String super_default_value = "123456";
public SupperClass() {
}
public SupperClass(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//public 内部类
public class InnerSuperClass {
}
//protected 内部类
protected class InnerSuperClass2 {
}
//private 内部类
private class InnerSuperClass3 {
}
}
package top.soft1010.java.knowledge.point.reflect;
/**
* Created by zhangjifu on 19/10/8.
*/
public class SubClass extends SupperClass {
public static final String sub_default_value = "123456";
private final String finalString = "final String";
private final int finalInt = 1;
private final Integer finalInteger = 11;
public SubClass() {
}
public SubClass(Integer id, String subName) {
this.id = id;
this.subName = subName;
}
public SubClass(Integer id, String name, Integer id1, String subName) {
super(id, name);
this.id = id1;
this.subName = subName;
}
private Integer id;
private String subName;
public void publicMethod(String... args) throws NullPointerException, ClassCastException {
StringBuffer stringBuffer = new StringBuffer("===");
for (Object obj : args
) {
stringBuffer.append(obj.toString()).append("===");
}
privateMethod(stringBuffer.toString());
}
private void privateMethod(String str) {
System.out.println("===name:" + getName() + " id:" + getId() + "===" + str == null ? "" : str);
}
private void privateMethod() throws NullPointerException, ClassCastException {
privateMethod("");
}
public String getFinalString() {
return finalString;
}
public int getFinalInt() {
return finalInt;
}
public Integer getFinalInteger() {
return finalInteger;
}
@Override
public Integer getId() {
return id;
}
@Override
public void setId(Integer id) {
this.id = id;
}
public String getSubName() {
return subName;
}
public void setSubName(String subName) {
this.subName = subName;
}
//内部类
public class InnerSubClass {
}
}
获取构造函数 getDeclaredConstructors()&getConstructors()
public static void parseConstructors(Class cls, boolean declared) {
Constructor[] constructors;
if (declared) {
constructors = cls.getDeclaredConstructors();
} else {
constructors = cls.getConstructors();
}
for (Constructor constructor : constructors) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(Modifier.toString(constructor.getModifiers())).append(" ")
.append(constructor.getName()).append("(");
//参数
Parameter[] parameters = constructor.getParameters();
if (parameters != null && parameters.length > 0) {
for (Parameter parameter : parameters) {
stringBuffer.append(parameter.getType().getName()).append(" ").append(parameter.getName()).append(",");
}
stringBuffer.delete(stringBuffer.length() - 1, stringBuffer.length());
}
stringBuffer.append(")");
//异常信息
Class[] exceptionClss = constructor.getExceptionTypes();
if (exceptionClss != null && exceptionClss.length > 0) {
stringBuffer.append(" throws ");
StringBuffer exceptionSb = new StringBuffer();
for (Class exceptionCls : exceptionClss) {
exceptionSb.append(exceptionCls.getName()).append(",");
}
stringBuffer.append(exceptionSb.substring(0, exceptionSb.length() - 1));
}
System.out.println(stringBuffer.toString());
}
}
输出结果:
===获取构造函数===
public top.soft1010.java.knowledge.point.reflect.SubClass(java.lang.Integer arg0,java.lang.String arg1,java.lang.Integer arg2,java.lang.String arg3)
public top.soft1010.java.knowledge.point.reflect.SubClass(java.lang.Integer arg0,java.lang.String arg1)
public top.soft1010.java.knowledge.point.reflect.SubClass()
===获取declared构造函数===
public top.soft1010.java.knowledge.point.reflect.SubClass(java.lang.Integer arg0,java.lang.String arg1,java.lang.Integer arg2,java.lang.String arg3)
public top.soft1010.java.knowledge.point.reflect.SubClass(java.lang.Integer arg0,java.lang.String arg1)
public top.soft1010.java.knowledge.point.reflect.SubClass()
两个方法获取的结果一致,看看下面获取变量和方法是否一致?
获取变量信息 getFields&getDeclaredFields
- getFields获取所有public的成员变量 包含本类和从子类继承来的
- getDeclaredFields获取本类所有变量,所有权限
public static void parseFileds(Class cls, boolean declared) {
Field[] fields;
if (declared) {
//获取本类所有变量,所有权限
fields = cls.getDeclaredFields();
} else {
//获取所有public的成员变量 包含本类和从子类继承来的
fields = cls.getFields();
}
for (Field field : fields) {
System.out.print(Modifier.toString(field.getModifiers()) + " "
+ field.getType().getName() + field.getName());
System.out.println("");
}
}
输出结果:
===获取fields===
public static final java.lang.Stringsub_default_value
public static final java.lang.Stringsuper_default_value
===获取declaredFields===
public static final java.lang.Stringsub_default_value
private final java.lang.StringfinalString
private final intfinalInt
private final java.lang.IntegerfinalInteger
private java.lang.Integerid
private java.lang.StringsubName
获取类的方法信息
- getMethods获取自己及父类public的方法
- getDeclaredMethods获取本类所有权限的方法
- method.getReturnType()获取方法的返回值信息
- method.getParameters()获取方法的入参信息
- method.getExceptionTypes()获取方法抛出的异常信息
public static void parseMethods(Class cls, boolean declared) {
Method[] methods = null;
if (!declared) {
//获取自己及父类public的方法
methods = cls.getMethods();
} else {
//获取本类所有权限的方法
methods = cls.getDeclaredMethods();
}
for (Method method : methods) {
//获取方法的入参信息
Parameter[] parameters = method.getParameters();
System.out.print(Modifier.toString(method.getModifiers()) + " " +
method.getReturnType().getName() + " " + method.getName());
System.out.print("(");
//入参
if (parameters != null && parameters.length > 0) {
StringBuffer argsStr = new StringBuffer();
for (Parameter parameter : parameters) {
argsStr.append(parameter.getType().getName()).append(" ").append(parameter.getName()).append(",");
}
System.out.print(argsStr.substring(0, argsStr.length() - 1));
}
System.out.print(")");
//异常信息
Class[] exceptionClss = method.getExceptionTypes();
if (exceptionClss != null && exceptionClss.length > 0) {
System.out.print("throws ");
StringBuffer exceptionSb = new StringBuffer();
for (Class exceptionCls : exceptionClss) {
exceptionSb.append(exceptionCls.getName()).append(",");
}
System.out.print(exceptionSb.substring(0, exceptionSb.length() - 1));
}
System.out.println();
}
}
输出结果:
===获取方法签名===
public java.lang.Integer getId()
public java.lang.String getFinalString()
public int getFinalInt()
public java.lang.Integer getFinalInteger()
public void setId(java.lang.Integer arg0)
public java.lang.String getSubName()
public void setSubName(java.lang.String arg0)
public transient void publicMethod([Ljava.lang.String; arg0)throws java.lang.NullPointerException,java.lang.ClassCastException
public java.lang.String getName()
public void setName(java.lang.String arg0)
public final void wait(long arg0,int arg1)throws java.lang.InterruptedException
public final native void wait(long arg0)throws java.lang.InterruptedException
public final void wait()throws java.lang.InterruptedException
public boolean equals(java.lang.Object arg0)
public java.lang.String toString()
public native int hashCode()
public final native java.lang.Class getClass()
public final native void notify()
public final native void notifyAll()
===获取declared方法签名===
public java.lang.Integer getId()
private void privateMethod(java.lang.String arg0)
private void privateMethod()throws java.lang.NullPointerException,java.lang.ClassCastException
public java.lang.String getFinalString()
public int getFinalInt()
public java.lang.Integer getFinalInteger()
public void setId(java.lang.Integer arg0)
public java.lang.String getSubName()
public void setSubName(java.lang.String arg0)
public transient void publicMethod([Ljava.lang.String; arg0)throws java.lang.NullPointerException,java.lang.ClassCastException
获取类的内部类信息
- getDeclaredClasses 得到该类所有的内部类,除去父类的
- getClasses 得到该类及其父类所有的public的内部类
public static void parseInnerClass(Class cls, boolean declared) {
Class[] classes;
if (declared) {
//getDeclaredClasses 得到该类所有的内部类,除去父类的
classes = cls.getDeclaredClasses();
} else {
//getClasses 得到该类及其父类所有的public的内部类。
classes = cls.getClasses();
}
for (Class clz : classes) {
System.out.println(clz.getName());
}
}
输出结果:
===获取内部类===
top.soft1010.java.knowledge.point.reflect.SubClass$InnerSubClass
top.soft1010.java.knowledge.point.reflect.SupperClass$InnerSuperClass
===获取declared内部类===
top.soft1010.java.knowledge.point.reflect.SubClass$InnerSubClass
5、访问或者操作类的变量和方法
上面反映的是动态获取信息,下面动态调用方法
调用方法
私有方法调用需要设置访问权限 privateMethod.setAccessible(true);
public static void invokePrivateMethod(Class cls) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//获取指定方法
System.out.println("调用私有方法");
//方法名 + 所有的参数类型(无参则不写)可变参数使用数组 如:可变参数为String的可变参数,则这里String[].class
Method privateMethod = cls.getDeclaredMethod("privateMethod", String.class);
//对于private方法,需要设置访问权限 对于public方法,不需要设置访问权限
privateMethod.setAccessible(true);
privateMethod.invoke(cls.newInstance(), "zhang");
}
执行:
ReflectUtils.invokePrivateMethod(SubClass.class);
输出结果:
===name:null id:null===zhang
调用可变参数的方法
public static void invokeMethod(Object obj, String methodName, String... args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
System.out.println("调用可变参数公有方法");
//获取指定方法 可变参数使用数组 如:可变参数为String的可变参数,则这里String[].class
Method publicMethod = obj.getClass().getDeclaredMethod(methodName, String[].class);
//对于public方法,不需要设置访问权限
// publicMethod.setAccessible(true);
//注意
//error java.lang.IllegalArgumentException: wrong number of arguments
//publicMethod.invoke(obj, new Object[]{"111", "222"});
//done 这里的参数对于可变参数一定是new Object[]{可变参数} 可变参数作为一个整体
//数组是可协变的,new String[]{"111", "222"}协变为Object
//publicMethod.invoke(obj, new Object[]{new String[]{"111", "222"}});
publicMethod.invoke(obj, new Object[]{args});
}
执行:
ReflectUtils.invokeMethod(subClass, "publicMethod", "1", "2", "3");
输出结果
===name:null id:null======1===2===3===
修改变量
直接上代码 注意私有变量修改值也需要先field.setAccessible(true);
public static void modifyFiled() throws NoSuchFieldException, IllegalAccessException {
SubClass subClass = new SubClass();
subClass.setSubName("subname");
Class cls = subClass.getClass();
Field field = cls.getDeclaredField("subName");
System.out.println();
//设置访问权限
field.setAccessible(true);
System.out.println("修改前 subname:" + subClass.getSubName());
//field是私有变量 subClass是要修改的对象 subNameNew是修改成的目标值
field.set(subClass, "subNameNew");
System.out.println("修改后 subname:" + subClass.getSubName());
}
输出结果:
修改前 subname:subname
修改后 subname:subNameNew
修改私有常量
还是先上一段代码
public static void modifyFinalFiled() throws NoSuchFieldException, IllegalAccessException {
SubClass subClass = new SubClass();
Class cls = subClass.getClass();
Field field = cls.getDeclaredField("finalInt");
System.out.println("私有变量finalInt 通过反射修改 值 1--> 2");
//设置访问权限
field.setAccessible(true);
//直接获取对象指定私有常量的值
System.out.println("反射前 finalInt:" + field.get(subClass));
//通过get方法获取私有常量的值
System.out.println("反射前 getfinalInt():" + subClass.getFinalInt());
//field是私有变量 subClass是要修改的对象 subNameNew是修改成的目标值
field.set(subClass, 2);
//直接获取修改后对象指定私有常量的值
System.out.println("反射后 finalInt:" + field.get(subClass));
//通过get方法获取修改后私有常量的值
System.out.println("反射后 getfinalInt():" + subClass.getFinalInt());
System.out.println("=============");
System.out.println("私有变量finalInteger 通过反射修改 11-->12");
Field field1 = cls.getDeclaredField("finalInteger");
//设置访问权限
field1.setAccessible(true);
//直接获取对象指定私有常量的值
System.out.println("反射前 finalInteger:" + field1.get(subClass));
//通过get方法获取私有常量的值
System.out.println("反射前 getfinalInteger():" + subClass.getFinalInteger());
//field是私有变量 subClass是要修改的对象 subNameNew是修改成的目标值
field1.set(subClass, 12);
//直接获取修改后对象指定私有常量的值
System.out.println("反射后 finalInteger:" + field1.get(subClass));
//通过get方法获取修改后私有常量的值
System.out.println("反射后 getfinalInteger():" + subClass.getFinalInteger());
}
输出结果:
===修改常量===
私有变量finalInt 通过反射修改 值 1--> 2
反射前 finalInt:1
反射前 getfinalInt():1
反射后 finalInt:2
反射后 getfinalInt():1
=============
私有变量finalInteger 通过反射修改 11-->12
反射前 finalInteger:11
反射前 getfinalInteger():11
反射后 finalInteger:12
反射后 getfinalInteger():12
-
1、通过反射修改私有常量值肯定是可行的。 field.get(subClass)这个方法得到修改后的值,无论是基本类型int还是包装类型Integer都是可以的
-
2、为什么常量类型分别为int和Integer的值,通过get方法得到的结果不一致呢? 下面我们看看编制之后的字节码文件反编译出来的java类
package top.soft1010.java.knowledge.point.reflect;
import top.soft1010.java.knowledge.point.reflect.SupperClass;
public class SubClass extends SupperClass {
public static final String sub_default_value = "123456";
private final String finalString = "final String";
private final int finalInt = 1;
private final Integer finalInteger = Integer.valueOf(11);
private Integer id;
private String subName;
public SubClass() {
}
public void publicMethod(String name, Integer id) throws NullPointerException, ClassCastException {
System.out.println("===name:" + name + " id:" + id + "===");
}
private void privateMethod(String name, Integer id) throws NullPointerException, ClassCastException {
System.out.println("===name:" + name + " id:" + id + "===");
}
//1、String类型的常量值,编译之后引用常量的位置直接被常量值替换了
public String getFinalString() {
return "final String";
}
//2、int基本类型的常量值,编译之后引用常量的位置也直接被产量值替换了
public int getFinalInt() {
return 1;
}
//3、Integer包装类型的的常量值,还是引用原来的常量,如果常量变化了,
//这个get方法返回值也会跟着变化
public Integer getFinalInteger() {
return this.finalInteger;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSubName() {
return this.subName;
}
public void setSubName(String subName) {
this.subName = subName;
}
public class InnerSubClass {
public InnerSubClass() {
}
}
我们在程序运行时刻依然可以使用反射修改常量的值, 但是 JVM 在编译阶段得到的 .class 文件已经将常量优化为具体的值, 在运行阶段就直接使用具体的值了,所以即使修改了常量的值也已经毫无意义了 get方法获取到的还是替换之后的具体常量值,但是非基本类型jvm并没有做这个优化操作
6、java反射性能问题
有各种关于java性能慢的论述,随之带来的是不建议使用,今天我们就自己动手测试研究一下反射性能几何? 环境:mac系统 jdk 1.8.0_221
public static void main(String[] args) {
//执行次数
int sum = 1000;
long start = System.currentTimeMillis();
SubClass subClass = new SubClass();
subClass.setName("zhang");
subClass.setId(123);
for (int i = 0; i < sum; i++) {
subClass.publicMethod("zhang", "123");
}
long time = System.currentTimeMillis();
try {
for (int i = 0; i < sum; i++) {
Method method = subClass.getClass().getDeclaredMethod("publicMethod", String[].class);
//对于private方法,需要设置访问权限
method.setAccessible(true);
method.invoke(subClass, new Object[]{new String[]{"zhang", "123"}});
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("直接调用方法:" + (time - start));
System.out.println("反射调用:" + (System.currentTimeMillis() - time));
}
具体执行方法1:
public void publicMethod(String... args) throws NullPointerException, ClassCastException {
//没有任何逻辑
}
结果对比:
| 执行次数 | 正常执行时间 | 反射执行时间 |
|---|---|---|
| 1000 | 4 | 13 |
| 10000 | 8 | 54 |
| 100000 | 31 | 67 |
| 1000000 | 34 | 692 |
可以看到反射执行时间是正常执行时间的3~20倍左右,反射真的性能很差。 下面我们修改 具体执行方法如下
public void publicMethod(String... args) throws NullPointerException, ClassCastException {
StringBuffer stringBuffer = new StringBuffer("===");
for (Object obj : args
) {
stringBuffer.append(obj.toString()).append("===");
}
privateMethod(stringBuffer.toString());
}
同样的测试条件,结果如下
| 执行次数 | 正常执行时间 | 反射执行时间 |
|---|---|---|
| 1000 | 29 | 41 |
| 10000 | 118 | 160 |
| 100000 | 793 | 845 |
| 1000000 | 5468 | 5967 |
| 可以看到,这个性能几乎是没有太大差距的,如果多执行几次,少许情况下,可能反射用时比正常还少。 |
为什么会出现上面两种情况呢?java反射到底对性能影响大不大? 我们重新修改执行方法的实现 方法1:
public void publicMethod(String... args) throws NullPointerException, ClassCastException {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
方法2:
public void publicMethod(String... args) throws NullPointerException, ClassCastException {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
方法3:
public void publicMethod(String... args) throws NullPointerException, ClassCastException {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行结果: |执行次数|方法1正常执行时间|方法1反射执行时间|方法2正常执行时间|方法2反射执行时间| |---|---|---|---|---|---|---| |1000|1397|1440|11341|11442| |10000|13799|14145|103049|102865|
下面看看反射执行的过程对比正常执行过程有哪些不同 首先是通过反射获取方法,第二个是invoke()方法
- 通过反射获取方法,在特定环境下每次耗时应该是一个相对固定的值
- invoke()方法 是在执行正常方法之前执行了一个相对固定的逻辑代码,具体过程可以自行查看。
如果这样,是不是我们就可以假想反射确实比正常执行耗时更多,但是这个耗时在具体环境下是一个相对稳定的值, 所以反射对性能的影响取决于具体执行方法的耗时,如果方法耗时约等于反射总共耗时,那就是一倍的差距。 根据上面方法1&方法2测试发现,反射共耗时是一个极小的数值(远小于1ms),如果一个方法的业务逻辑耗时是ms级以上 则反射造成的性能问题几乎可以忽略不计
7、java反射应用
jOOR
jOOR是对原生reflect的封装,屏蔽了一些无关的细节。 pom.xml jdk9+
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joor</artifactId>
<version>0.9.7</version>
</dependency>
jdk8+
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joor-java-8</artifactId>
<version>0.9.7</version>
</dependency>
看几个简单的应用
package top.soft1010.java.knowledge.point.reflect.joor;
import org.joor.Reflect;
/**
* Created by zhangjifu on 19/10/23.
*/
public class Test {
private String name;
public Test(String name) {
this.name = name;
}
public static void main(String[] args) {
String name =
//构造reflect对象
Reflect.on("top.soft1010.java.knowledge.point.reflect.joor.Test")
//调用构造方法构造对象
.create("hello world")
//调用方法
.call("test")
//获取field
.get("name");
System.out.println(name);
}
private Test test() {
System.out.println(name);
return this;
}
}
reflections框架
引入pom.xml 依赖 Google 的 Guava 库和 Javassist 库。
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
reflections的使用
/**
* 如果没有配置scanner,默认使用SubTypesScanner和TypeAnnotationsScanner
*
* @param basePackage 包路径
*/
public static Reflections getFullReflections(String basePackage) {
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addUrls(ClasspathHelper.forPackage(basePackage));
builder.setScanners(new TypeAnnotationsScanner(), new SubTypesScanner(),
new MethodAnnotationsScanner(), new FieldAnnotationsScanner());
builder.filterInputsBy(new FilterBuilder().includePackage(basePackage));
Reflections reflections = new Reflections(builder);
return reflections;
}
//找出被指定注解类标注的所有接口、类
public static void getTypesAnnotatedWith() {
Set<Class<?>> annotationSet = getFullReflections(BASE_PACKAGE).getTypesAnnotatedWith(Handler.class);
for (Class<?> clazz : annotationSet) {
System.out.println(clazz.getName());
Handler annotation = clazz.getAnnotation(Handler.class);
System.out.println("name:" + annotation.name() + " value" + annotation.value());
}
}
//找出指定接口的所有子类
public static void getSubTypesOf() {
Set<Class<? extends TestService>> classes = getFullReflections(BASE_PACKAGE).getSubTypesOf(TestService.class);
for (Class<?> clazz : classes) {
System.out.println(clazz.getName());
}
}
//查找有某个注解类的方法
public static void getMethodsAnnotatedWith() {
Set<Method> methodSet = getFullReflections(BASE_PACKAGE).getMethodsAnnotatedWith(MethodHandler.class);
for (Method m : methodSet) {
System.out.println(m.getDeclaringClass().getName());
System.out.println(m.getName());
}
}
//查找有某个注解类的字段
public static void getFieldsAnnotatedWith() {
Set<Field> fieldSet = getFullReflections(BASE_PACKAGE).getFieldsAnnotatedWith(FiledAnno.class);
for (Field f : fieldSet) {
System.out.println(f.getName());
}
}
简单列举上面几个,详细的reflections的使用参考官网
java反射的几个应用
- spring中bean实例化并且添加到spring容器
<bean id="userDao" class="top.soft1010.user.dao.userDao"></bean>
//1、读取xml配合文件,获取idStr = userDao classStr=top.soft1010.user.dao.userDao
//利用反射,通过classStr获取Class类对象
Class<?> cls = Class.forName(classStr);
//实例化对象
Object obj = cls.newInstance();
//container表示Spring容器
container.put(idStr, obj);
- spring中带有属性的bean实例化
<bean id="userService" class="top.soft1010.user.service.UserService">
<!-- 控制调用setUserDao()方法,将容器中的userDao bean作为传入参数 -->
<property name="userDao" ref="userDao"></property>
</bean>
//解析<property .../>元素的name属性得到该字符串值为“userDao”
String nameStr = "userDao";
//解析<property .../>元素的ref属性得到该字符串值为“userDao”
String refStr = "userDao";
//生成将要调用setter方法名
String setterName = "set" + nameStr.substring(0, 1).toUpperCase()
+ nameStr.substring(1);
//获取spring容器中名为refStr的Bean,该Bean将会作为传入参数
Object paramBean = container.get(refStr);
//利用反射,通过classStr获取Class类对象
Class<?> cls = Class.forName("top.soft1010.user.service.UserService");
//获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象
Method setter = cls.getMethod(setterName, paramBean.getClass());
//调用invoke()方法,此处的obj是刚才反射代码得到的Object对象
setter.invoke(obj, paramBean);
3、动态代理 动态代理的demo参考这里
8、思考
1、在反射中为什么调用方法要使用Method.invoke(Object obj,Object . . . arrs)去调用,既然传了对象给invoke()为什么不直接去调用相对应的方法?
这里的Method对象是在运行期间通过类的字节码对象获取到的,对于编码期间的时候,我们并不知道具体的对象是什么类型;因此我们并不知道要转成什么类型,然后在直接调用该方法。 使用Method.invoke(Object obj,Object . . .arr)方法调用,因为我们传了具体的对象和方法的参数,以及方法对象。在底层jvm会获取具体对象的class字节码对象,又有了方法对象和参数,就可以动态的调用该方法了。
2、编译阶段对常量的优化问题
对于常量编译器会做优化,基本类型会使用常量值替换常量,反射能修改常量值,但是对替换成常量值的.class文件无能为力,因为运行期阶段使用的字节码文件都是直接使用的常量值
3、可变参数的方法的反射问题
//获取指定方法 可变参数使用数组 如:可变参数为String的可变参数,则这里String[].class
Method publicMethod = obj.getClass().getDeclaredMethod(methodName, String[].class);
//error java.lang.IllegalArgumentException: wrong number of arguments
//publicMethod.invoke(obj, new Object[]{"111", "222"});
//done 这里的参数对于可变参数一定是new Object[]{可变参数} 可变参数作为一个整体
//数组是可协变的,new String[]{"111", "222"}协变为Object
//publicMethod.invoke(obj, new Object[]{new String[]{"111", "222"}});
publicMethod.invoke(obj, new Object[]{args});
- 获取指定方法 可变参数使用数组 如:可变参数为String的可变参数,则这里String[].class
- method.invoke()的参数对于可变参数一定是new Object[]{可变参数} 可变参数作为一个整体
- 数组是可协变的 Fruit[] apples=new Apple[size];下面这些都是正确的
Object o = new int[]{};
Object o1 = new Integer[]{};
Object[] o2 = new Integer[]{};
那么问题来了,jOOR框架下可变参数的方法调动,参数该如何写?
public static void main(String[] args) {
String name =
//构造reflect对象
Reflect.on("top.soft1010.java.knowledge.point.reflect.joor.Test")
//调用构造方法构造对象
.create("zhang")
//可变参数的方法调用 这里可变参数的类型是new Object[] new String[]{}才是具体参数
.call("test1", new Object[]{new String[]{"1", "2", "3"}})
//调用方法
.call("test")
//获取field
.get("name");
System.out.println(name);
}
private void test1(String... args) {
for (String arg : args) {
System.out.println(arg.toString());
}
}
java反射的性能问题
java反射相对于直接的方法调用确实有性能损耗,但是这个损耗是依据具体反射的方法耗时决定的。 具体需要根据具体情况判断。对于一个耗时ms级以上的方法,反射的性能基本就可以忽略了。
--------------------------end-------------------------
对你有帮助的话,记得点个赞👍
- [六十七点五 ]
--------------------------end-------------------------