目录
1.2获取Constructor/Field/Method 调⽤对应⽅法
在运行时,知道一个类的名字可以动态获取类的方法和属性,知道一个对象的名字,可以动态的调用对象的属性和方法
1.API的使用
1.1 获取class对象
- 使用数据类型.class的方式获取对应类型的Class对象
- 使用引用/对象.getClass()的方式可以获取对应类型的Class对象
- 使⽤包装类.TYPE的⽅式可以获取对应基本数据类型的Class对象(Integer)
- 数组可以使⽤ '类型+''[].class',例如 int[].class
- 使用Class.forName()的方式来获取参数指定类型的Class对象
- 使用类加载器ClassLoader的⽅式获取指定类型的Class对象
Class<?> c1 = Class.forName("com.david.camp.code.Person");
Class<Person> c2 = Person.class;
Class<? extends Person> c3 = new Person().getClass();
Class<Integer> type = Integer.TYPE;
1.2获取Constructor/Field/Method 调⽤对应⽅法
public static void main(String[] args) throws Exception {
Class<Person> c2 = Person.class;
//实例化
Person person = c2.newInstance();
person.setName("⼩明");
person.setAge(22);
//调⽤构造⽅法
Constructor<Person> constructor = c2.getConstructor();
Person person2 = constructor.newInstance();
System.out.println("person2 " + person2);//person2
Person{id=null, name='null', age=null, salary=null}
//获取属性
Field age = c2.getDeclaredField("age");
//获取私有属性
age.setAccessible(true);
System.out.println("age:" + age.get(person));//age:22
//调⽤实例⽅法
Method hello = c2.getMethod("hello", Object.class);
Object invoke = hello.invoke(person, person.getName());//⼩明say
hello
}
1.3 反射实现的原理
Method.invoke反射调⽤执⾏过程
1. Method.invoke调⽤DelegatingMethodAccessorImpl.invoke 代理实现
2. 再调⽤本地native实现
3. 最终调⽤⽬标⽅法
当某个反射调用的调用次数在15之下时,采用的是本地实现,达到15时,便开始动态的实现。动态实现比本地实现效率要快上20倍
1.4 性能开销
1.getMethod等查找⽅法会返回结果数据的拷⻉,会占⽤堆空间,以及对GC产⽣压⼒。可以使⽤缓存优化。
2.Method.invoke参数传⼊必须使⽤对类型的的Object数组 public Object invoke(Object
obj, Object... args) ,因此会导致原始类型的装箱拆箱。
扩⼤Integer缓存范围 -Djava.lang.Integer.IntegerCache.high=128 减少包装类对象频繁
创建
3.内联瓶颈 。 JVM对同⼀个调⽤点的profile记录是有限制的(-XX:TypeProfileWidth 默认值为2),导
致后续⽅法⽆法被内联。
1.5 作用
1.可以绕过private的权限限制,final的不可变限制操作字段
2.通过框架开发Spring bean 工厂等
3.枚举动态化