前言:反射是java语言的重要一部分,反射可以在运行期间动态执行访问类,方法及字段,会大大提高框架的灵活性,所以像很多开源框架大量使用反射动态生成对象,例如spring的bean,mybatis的mapper。但反射的性能一直是被诟病的。 java中创建对象有几种方式? 1.使用new关键字 2.使用clone方法 3.使用反序列化4.使用反射 5.使用Unsafe
1.一个小demo,使用反射实例化对象并且调用该对象里的方法
1.1 定义一个Student对象
/**
* @Author feng ye
* @Date 2021/6/30 0:48
* @Description
*/
public class Student {
public void toSleep(){
System.out.println("toSleep方法被执行了...");
}
}
复制代码
1.2定义一个属性文件内容是该类的全限定名以及类中方法
1.3 读取配置文件种的信息,使用反射实例化Student对象以及调用toSleep方法
/**
* @Author feng ye
* @Date 2021/6/30 0:41
* @Description
*/
public class ReadSource {
public static void main(String[] args) throws Exception{
// 可以创建任意类的对象,可以执行任意方法
// 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
//1.加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReadSource.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
Method[] methods = cls.getMethods();
System.out.println("methods.length :"+ methods.length);
for (Method m : methods){
System.out.println("类名:"+cls.getSimpleName()+" ,方法名"+m.getName());
}
//6.执行方法
method.invoke(obj);
}
}
复制代码
执行结果:
上面就是使用反射结合spi的方式可以实现动态生成对象,我们只需要在配置文件替换类名和方法名称就行
2.反射之Class对象讲解
获取一个class对象有三种方式,虽然方式不同,但获取的都是同一个class对象。因为class文件只会被jvm加载一次
* Class对象功能:
* 获取功能:
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
2. 获取构造方法们
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
3. 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4. 获取全类名
* String getName()
复制代码
3.反射之对Filed对象操作
3.1定义一个Person类
```
public class Person {
public String a;
protected String b;
String c;
String d = "我是d";
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
private Person(String name) {
this.name = name;
}
public void show() {
System.out.println("show方法被执行");
}
private void show2() {
System.out.println("私有的 show2方法被执行");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"a='" + a + ''' +
", b='" + b + ''' +
", c='" + c + ''' +
", d='" + d + ''' +
", age=" + age +
", name='" + name + ''' +
'}';
}
public void eat() {
System.out.println("eat...");
}
public void eat(String food) {
System.out.println("eat..." + food);
}
private void sum(Integer num) {
System.out.println("sum方法被执行了..." + num);
}
}
```
复制代码
3.2 使用反射访问类中的不用权限的属性以及对属性赋值
/**
* @Author feng ye
* @Date 2021/6/30 0:23
* @Description 使用反射获取Class对象的成员变量【属性】
* 可以对其进行赋值,取值;包括不同的修饰可以对齐进行操作【public,protected,private】
*/
public class ReflectDemo02 {
/**
* Class对象功能:
* 获取功能:
* 获取成员变量们
* Field[] getFields()
* Field getField(String name)
* Field[] getDeclaredFields()
* Field getDeclaredField(String name)
*/
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
/*
1. 获取成员变量们
* Field[] getFields()
* Field getField(String name)
* Field[] getDeclaredFields()
* Field getDeclaredField(String name)
*/
//1.Field[] getFields()获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println("遍历获取所有public修饰的成员变量field: " + field);
}
//2.Field getField(String name)
Field a = personClass.getField("a");
//获取成员变量a 的值
Person p = new Person();
Object value = a.get(p);
System.out.println("获取person的属性a名称: "+value);
//设置a的值
a.set(p, "张三");
System.out.println("设置person的属性a的值: " + p);
System.out.println("#############分隔符############");
//Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
//开启暴力访问,可以访问类的私有属性
declaredField.setAccessible(true);
Object a1 = declaredField.get(p);
System.out.println("--------------->a1: " + a1);
System.out.println("--------------->declaredField: " + declaredField);
}
//Field getDeclaredField(String name)
Field d = personClass.getDeclaredField("d");
//忽略访问权限修饰符的安全检查
d.setAccessible(true);//暴力反射
Object value2 = d.get(p);
System.out.println("获取person类中属性d的值: "+value2);
}
}
复制代码
执行结果
4.发射访问类中的方法
public class ReflectDemo04 {
/**
* Class对象功能:
* 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, Class<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, Class<?>... parameterTypes)
*
* //获取类名
* String getName()
*/
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
//获取指定名称的方法
Method eat_method = personClass.getMethod("eat");
Person p = new Person();
//执行方法
eat_method.invoke(p);
Method eat_method2 = personClass.getMethod("eat", String.class);
//执行方法
eat_method2.invoke(p, "饭");
System.out.println("---------------分隔符1---------------");
//获取所有public修饰的方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println("获取所有public修饰的方法,methodName: "+method);
}
System.out.println("---------------分隔符2---------------");
//获取private修饰的sum方法
Method sum = personClass.getDeclaredMethod("sum",Integer.class);
sum.setAccessible(true);
sum.invoke(p,1);
}
复制代码
执行结果:
5.反射为什么会变慢
以下是oracle官网原话:
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
大意是说:反射的类型是动态解析的,这将导致JVM无法实施某些特定的优化(具体来说,就是我们常说的JIT优化),在性能敏感和频繁调用的方法上,应该尽量避免使用反射。
5.1静态加载和动态加载:下面是我们用notePad++编写的一个ClssLoad_.java文件,然后尝试用cmd去 javac去(分开截图了)
静态加载:就是在编译的时候将我们编写的java文件编译成class字节码文件,因为Dog是静态加载,如果该类不存在则会抛出异常[classNofoundException]
动态加载:Person就是动态加载,所以即使当该Person不存在的时候,编译阶段也不会报错。 只有在执行到case "2",动态加载该Person类时才会报错。
结论:可以得出一个结论(为什么反射慢,影响性能)。因为反射是在动态加载的时候才会将对象的字节码文件加载到jvm。而且访问反射相关的属性,会有安全检查等耗时判断。【可以将动态加载对比的理解为java对象的懒加载,因为在动态加载的过程需要做一些事情,所以慢】
反射的优化:
作者:初心不忘976
链接:https://juejin.cn/post/7007410196330840071
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。