当前位置: 首页 > 科技观察

Java反射进阶——说说关于反射的几个问题

时间:2023-03-16 00:32:52 科技观察

前言有朋友反馈了很多昨天没讲到的反射知识,所以今天补篇,一起来看看反射的进阶知识点。反射可以修改final类型的成员变量吗?final大家应该都知道,在修改变量的时候,它代表的是一个常量,是不能被修改的。是否可以使用反射来达到修饰效果?我们先尝试修改一个用final修饰的String变量。publicclassUser{privatefinalStringname="Bob";privatefinalStudentstudent=newStudent();publicStringgetName(){returnname;}publicStudentgetStudent(){returnstudent;}}Useruser=newUser();Classclz=User.class;Fieldfield1=null;try{field1=clz.getDeclaredField("name");field1.setAccessible(true);field1.set(user,"xixi");System.out.println(user.getName());}catch(NoSuchFieldExceptione){e.printStackTrace();}catch(IllegalAccessExceptione){e.printStackTrace();}打印出来的结果还是Bob,也就是没有修改。让我们再次尝试修改学生变量:field1=clz.getDeclaredField("student");field1.setAccessible(true);field1.set(user,newStudent());打印:com.example.studynote.reflection修改前。通过Student@77459877com.example.studynote.reflection.Student@72ea2f77修改后可以看到普通对象变量即使是final修改也可以通过反射修改。为什么是这样?为什么String不能修改,普通对象变量可以修改?让我们从结论开始。其实String值也被修改了,只是我们无法通过这个对象获取到修改后的值。这就涉及到JVM的内联优化:内联函数,编译器插入指定的函数体,并替换每一个调用函数的地方(上下文),从而节省了每次函数调用所花费的额外时间。简单来说,JVM会在处理代码的时候帮助我们优化代码逻辑。比如上面提到的final变量,知道finalize之后就不会再修改了,所以在获取这个变量的时候,会在编译阶段直接帮你赋值。向上。所以上面的getName方法经过JVM编译和内联优化后会变成:publicStringgetName(){return"Bob";}所以无论怎么修改,都得不到修改后的值。有的朋友可能会提出直接取名字?例如://修改为publicpublicfinalStringname="Bob";//反射修改后,打印user.namefield1=clz.getDeclaredField("name");field1.setAccessible(true);field1.set(user,"xixi");System.out.println(用户名);抱歉,还是打印出Bob。这是因为System.out.println(user.name)这句话会写成:System.out.println(user.name)//内联优化System.out.println("Bob")所以:"反射可以修改final变量,但是如果是基本数据类型或者String类型,修改后的值是无法通过对象获取到的,因为JVM对它进行了内联优化。"那有没有办法获取修改后的值呢?有的,可以通过反射中的Field.get(Objectobj)方法获取://获取用户对象中字段对应的变量值System.out.println("ModifyAfter"+field.get(user));反射获取static静态变量说完final再说static,如何修改static修饰的变量?我们知道static变量是在实例化之前初始化的类(类初始化阶段),所以静态变量跟在类本身后面,与具体对象无关,所以我们获取变量不需要传入对象,直接传入null即可:publicclassUser{publicstaticStringname;}field2=clz.getDeclaredField("name");field2.setAccessible(true);//获取静态变量Objectgetname=field2.get(null);System.out.println("修改前"+getname);//修改静态变量field2.set(null,"xixi");System.out.println("修改后ation"+用户名);如上面代码:Field.get(null)可以获取静态变量。Field.set(null,object)可以修改静态变量。如何提高反射效率1.缓存重复使用的对象使用缓存,其实我不说大家知道,正常项目中使用多次的对象也会缓存,不会有人多次创建.不过这个在反射中尤为重要,比如Class.forName方法,我们做一个测试:longstartTime=System.currentTimeMillis();Classclz=Class.forName("com.example.studynote.reflection.User");Useruser;inti=0;while(i<1000000){i++;//方法一,直接实例化user=newUser();//方法二,每次通过反射获取class,再实例化user=(User)Class。forName("com.example.studynote.reflection.User").newInstance();//方法三,实例化之前反射得到的类user=(User)clz.newInstance();}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));打印结果:1.直接实例化耗时:152.每次通过反射获取类,再实例化耗时:6713.通过前面反射实例化获取的类耗时:31所以可以看出只要我们合理使用这些反射方法,比如Class.forName、Constructor、Method、Field等,尽量将实例缓存在循环外,提高反射效率,减少耗时。2.setAccessible(true)我们之前说过,遇到私有变量和方法时,我们会使用setAccessible(true)方法来关闭安全检查。这种安全检查实际上很耗时。所以我们可以尝试在反射过程中调用setAccessible(true)关闭安全检查,不管是私有的还是非私有的,这样也可以提高反射的效率。3.ReflectASMReflectASM是一个非常小的Java类库,通过代码生成提供高性能的反射处理,自动提供get/set字段的访问类。访问类使用字节码操作而不是Java反射技术,所以速度非常快。ASM是一个通用的Java字节码操作和分析框架。它可用于修改现有类或直接以二进制形式动态生成类。简单的说,这是一个类似于反射,又不同于反射的高性能库。他的原理是通过ASM库生成一个新类,然后相当于直接调用新类的方法完成反射功能。有兴趣的可以看看源码,实现原理比较简单——https://github.com/EsotericSoftware/reflectasm。”小总结:“经过上面三种方法,我觉得反射并没有那么可怕,会大大影响性能。如果你真的发现反射影响性能和实际使用,或许你可以研究一下是不是因为没有使用反射而不处理反射相关的缓存呢?反射原理如果我们尝试去查看这些反射方法的源码,就会发现它们最终都会走向native方法。比如getDeclaredField方法会去publicnativeFieldgetDeclaredField(Stringname)throwsNoSuchFieldException;那么在最底层,如何获取类的相关信息呢?首先回顾一下JVM加载Java文件的过程:在编译阶段,.java文件会被编译成.class文件。识别的机器码。.class文件依次存放class文件的各种信息,如:版本号、类名、字段描述和描述符、方法名和描述、是否public、类索引、字段表集合、方法集合等。数据。然后,JVM中的类加载器会读取字节码文件,取出二进制数据,加载到内存中,解析.class文件的信息。类加载器会获取该类的二进制字节流,并在内存中生成一个代表该类的java.lang.Class对象。最后会开始类的生命周期,比如连接,初始化等等。而反射就是操作这个java.lang.Class对象,它有整个类的结构,包括属性方法等等。综上所述,.class是一个顺序结构的文件,而Class对象就是这个文件的一个表示,所以我们可以从Class对象中得到类的所有信息,这就是反射原理。说一些与本文无关的事情,最近对分析文章的源码部分有了一些想法。以前一直想把源码原封不动的上传,方便大家离线找到相关的源码,通读一遍。但也许这是不现实的?而且也给很多朋友阅读文章造成了障碍。很可能他们当时只是一知半解,忘得一干二净。至少我是这样的哈哈。所以在文章的写作中可能会涉及到源码分析部分。尽量写的简洁一点,或者直接贴伪代码,方便大家看懂~以后试试看。再见Android架构(连载文章、脑图、面试题):https://github.com/JiMuzz/Android-ArchitectureReferencehttps://juejin.cn/post/6844903905483030536https://www.zhihu.com/提问/46883050https://juejin.cn/post/6917984253360177159https://blog.csdn.net/PiaoMiaoXiaodao/article/details/79871313https://www.cnblogs.com/coding-night/p/10772631.html本文转载自微信公众号《代码积木》,可通过以下二维码关注。转载本文请联系代码上的积木公众号。