简介有一本关于诺贝尔物理学奖得主、物理学家费曼的书,名叫《发现的乐趣》。书中有一段关于费曼童年的故事:“我们家有《大不列颠百科全书》,我小时候。我父亲过去常常让我坐在他的腿上给我读一些东西《大不列颠百科全书》。例如,我们读了关于恐龙的部分,这本书可能会谈到雷龙或其他一些龙,这本书会说,“这家伙身高25英尺,头长6英尺。这时,父亲停下来,说:“让我们看看这句话是什么意思。”意思是:如果它站在我们的前院,它会很高,高到可以把头伸出窗户。但是,他可能会遇到麻烦,因为他的头是比窗户宽一点,如果他把头伸进去,他会把窗户砸碎。费曼说:“我们读到的一切,我们都试图把它变成某种现实。从这里我学会了一项技能——我读到的所有东西,我总是试图通过某种转化来理解它。这是什么意思,它到底在说什么。Feynmantechnique费曼技巧,或费曼学习法,是一种以教促学的方法。总共有四个步骤(你可以忽略你已经知道的并跳过它):(1)选择新概念/新知识,自己学习先去学习它。(2)假装老师教别人想象你面对的是一群新手。如何向他们解释这个概念,使他们能够理解?把你解释的想法写在纸上。如果你真的不想写,你可以说出来。很重要!!!不要让你的思绪停留在你的大脑中,因为你的大脑中会对知识点之间的联系产生一些想当然的、错误的假设。你可以通过口头表达或写出来找到这些“盲点”!!(3)如果你教的有困难卡住了,回去学习。回到书上,搜索相关资料,问问别人,强迫自己搞清楚概念,然后回到第二步,继续给小白讲解。(4)简化你的语言。目标是用你自己的话,非技术词汇来解释这个概念。尽量简单明了,或者找个比喻来表达。这是一个非常简单的过程,对吧?下面我们用一个例子来练习一下实战练习。请第一主角张大发出场。张胖子正在学习Java。这一天,他遇到了一个新概念:“DynamicProxy”(注意这个概念是学习用的,不是实际实现的),非常抽象,在日常编程中几乎没有直接用到,所以很难理解。第一步自学张大发,看动态代理介绍。书中列出了一堆烦人的代码来展示这个东西是如何使用的。比如有一个接口(IHelloWorld)和它的实现类(HelloWorld),然后有一个InvocationHandler的实现,最后用Proxy.newProxyInstance(...)创建一个新的类,这些都是什么鬼?第二步,张大胖尝试教小白(当然这里的小白至少要懂一点Java)。Proxy该技术可以在运行时创建一个新的代理类。小白:张老师,新的代理类有什么用?张胖子:比如有一个接口叫IHelloWorld,它的实现类HelloWorld,有一个方法叫sayHello()。您可以在sayHello()前后添加一些额外的日志输出。(在解释一个概念的时候,例子和类比很重要,人类习惯于通过例子来学习,从具体到抽象)小白:那我就直接写一个新的类,比如HelloWorldEx,把日志输出加进去。现在,为什么要使用Proxy.newProxyInstance(...)这个麻烦的方法?publicclassHelloWorldExamplementsIHelloWorld{IHelloWorldhw;publicHelloWorldEx(IHelloWorldhw){this.hw=hw;}publicvoidsayHello(){Logger.startLog();hw。sayHello();Logger.endLog();}}这道题张胖子答不上来,卡住了!第三步,回去看书学习。书上没有解释,唉!仔细想想,手动写一个类HelloWorldEx和用Proxy.newProxyInstance创建有什么区别?实现的功能是一样的,但是HelloWorldEx需要提前写好,编译后不能更改,相当于写死!如果我要在Order类、Employee类、Department类中添加一些日志,就得写OrderEx、EmployeeEx、DepartmentEx类,太麻烦了!而Proxy.newProxyInstance方法,可以在运行时为任意类动态创建增强类。预编码的叫静态代理,Proxy.newProxyInstance的方法叫动态代理,更灵活。张胖子觉得这个解释有道理。小白:为什么要新建一个代理类?Proxy.newProxyInstance不能直接修改旧的HelloWorld类吗?张大发又卡壳了,上网一搜,果然找到了答案。与Python、Ruby等方法不同,Java本质上是一种静态类型语言,一旦类被加载到JVM中,就无法修改、添加或删除方法。由于不能修改旧类,只能通过代理创建新类。小白:明白了,这个技术主要用在什么地方?难道只是为了添加日志?张胖子第三次卡住了,只好重新寻找。事实证明,AOP是使用最广泛的动态代理。在AOP中,往往以声明的方式提出这样的要求:某个包下所有以add开头的方法在执行前必须调用Logger.startLog()方法,执行后要调用Logger.endLog()方法。或者对于所有以Service结尾的类,所有方法在执行前必须调用tx.begin(),执行后调用tx.commit(),抛出异常则调用tx.rollback()。至此,张胖子可以这样告诉小白:你没用过SpringAOP吗?AOP里面经常有这样的需求。。。spring想加上这些日志和事务的功能,但是没有办法修改用户的类,它是一个框架,一个是用户类的源码不得而知,另一个是Java不允许修改加载到JVM中的类。没办法,Spring不得不在运行时找到用户的类,然后操纵字节码动态创建新的类。新类将增强原有类,增加日志和交易功能。请注意,这些都是在内存中动态创建的。这个技术就是Java的动态代理,但是它有一个前提,就是用户的类需要实现接口。我用一个简单的例子告诉你,你就会明白其中的细节……第四步,化简。”,因为我明白为什么了,剩下的就是细节。如果你理解透彻,动态代理的技术细节会在你的脑海中产生这样的画面:$HelloWorld100是代理类,它和HelloWorld都实现了IHelloWorld接口。如果一定要打个比喻的话,两者是“兄弟”。CgLib提供了另一种增强现有类的方法。动态生成的类继承现有类,两者是“父子关系”。总结一下呢?用这种(假装)教别人的方法很有效,循序渐进,然后问自己问题?好处是大吗?用这种方法实际上是迫使你把大脑中的盲点和一些理所当然的假设结合起来暴露了,效果比单纯的看书背诵好很多,学习中赶快试试吧!授权】点这里查看作者更多好文
