好久不见,在疫情的控制下,我急需一杯奶茶续续命!
作者:王炸 |【坚持1000篇原创】
2020.2.21 王炸的第60篇原创
☝️先赞后看是技术人的传统美德☝️
有小朋友问我,我刚刚学Java,没接触过项目,也没机会用Spring之类的框架,看书也知道反射都有什么内容,哪本书都说反射很有用,但到现在都不知道它用在啥上面,所有,反射有啥用?
关于反射,我必须要吐槽一下,初学的时候如果靠看xxx从入门到精通系列的书,理解反射可太TM困难了,看的我怀疑人生,那个时候我一度怀疑,我该不是个智障吧!
今天来分析下:围绕Java反射,BAT的面试官可以问出多少花样。
反射关系到Java的语言特性,jvm的内存细节
JVM:只能跑Java代码的CPU
刚刚开始我也不理解Java的虚拟机到底算个什么东西?
第一节接触这东西一脸困惑,字节码代码是在CPU上执行?在虚拟机上执行?虚拟机又是个什么东西?
后来我慢慢试着理解:JVM就是跑才CPU上的一个虚拟CPU,但是这个CPU只能跑Java代码
Java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。
上图是java的内存模型,我们关注的点,一个方法区,一个栈,一个堆,初学的时候老师不深入的话只告诉你java的内存分为堆和栈
假如你写了一段代码:String s=new String();
运行了起来!这里面发生了什么?
首先JVM会启动,你的代码会编译成一个xxx.class文件。
然后被类加载器加载进jvm的内存中(虚拟CPU),你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。
⚠️注意:jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。
上面的流程就是你自己写好的代码扔给jvm去跑,跑完就over了,jvm关闭,你的程序也停止了。
为什么会发明反射这种技术?
想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑,程序员都是聪明的动物,还有没有更好的办吧不用写死,不用new。
假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,哦启动一下服务器,(脑残)!
这个时候反射技术诞生了,这在当时,是一个非常先进的理念,Java也因此得到更多人认可。
反射是什么呢?
当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻。
Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。
有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类,这时第一个程序员不能通过编译,但是利用反射就可以通过。原因是java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。
举个例子:
我们的项目底层有时是用Mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了。
假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用。
这时候我们的程序就写得比较动态化,通过Class tc = Class.forName("com.java.dbtest.TestConnection");
通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。
这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!
举多个例子,大家如果接触过spring,会发现当你配置各种各样的bean时,是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。
好吧,围绕Java反射,BAT的面试官可以问出多少花样
面试:Java反射机制在Spring IOC中的应用
IOC:即“控制反转”,不是什么技术,而是一种思想。
使用IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
IOC底层实现的原理(反射),Bean容器的实现,就不对IOC的概念进行详述了。
在Spring的配置文件中,经常看到如下配置:
那么通过这样配置,Spring是怎么帮我们实例化对象,并且放到容器中去了了?对,就是通过反射!!!
面试:Java反射在动态代理中的应用
JDK动态代理
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
Interface InvocationHandler:该接口中仅定义了一个方法Object,invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
Proxy:该类即为动态代理类。
Cglib动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。JDK代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制性要求。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。
Javassist代理
一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler;使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑。
vx搜索:转行程序员