1.什么样的代码可能用到闭包

image.png
前言参考https://www.yuque.com/bravo1988/java/gilh34#G1dPE
里面的这个章节<Lambda表达式拓展:this与闭包> 相当于引入个闭包这个概念

闭包:内部函数凭啥能访问外部变量

上面这三个代码相当于加强了下这个概念,说明了一个问题
1.lambda函数 —>可以访问外部的变量
2.匿名内部类形态1(类的重写) —>可以访问外部的变量
3.匿名内部类形态2(接口实现) —>可以访问外部的变量
这个是符合人类的基本认识(常识)的,里面的类(里面的方法)可以访问外部的,外部的不一定可以访问内部的
具体实现方式要看语言

this为啥打印出来是不同的

回答:说明了具体java底层(jvm)为了实现这个效果,可能用的实现方式是不同的,就好比可以通过走路去北京,也可以通过坐飞机去北京,只要能到北京就行,匿名内部类是一种实现方式,lambda是另外一种实现方式
可以先看看编译以后是啥样的
image.png
匿名内部类不管是形态1还是形态2,编译以后会有2个class文件,可以简单理解是java认为其实1个类等同2个类(自己和内部类)
lambda编译以后只有1个class文件,可以认为java认为,就是一个类,相当于多了一个方法

2.这两种代码有啥不同呢

其实在java层面是等同的,比如用idea就可以自动的把匿名内部类转换为lambda表达式,
但是其实二者还是有区别的,底层是不一样的,比如编译以后文件数不同,使用的字节码指令不同
可以理解成通过不同方式实现相同的效果!

3.final是什么鬼

代码1,代码2,代码3的这个局部变量String str,不管是否声明为final,函数都是可以正常执行的,不信可以试一试
我们可以简单理解成内部类可以用外部的变量,但是不能改的,类似是只读的属性
我们平常写代码很少用final作为方法参数的修饰,那就写一个试试吧
image.png
我们一般用final就是用在全局常量
比如
public static final String URL=”www.baidu.com”;
通过上面代码例子我们可以知道,如果方法参数用final修饰,那么在方法中是不能改变这个参数的值的

语法糖

jdk1.5中引入了自动装箱,拆箱
就是一种语法糖,让程序自动化,减轻程序猿的工作量
这里,jdk1.8相当于自动帮我们把final来修饰变量

为啥要final修饰呢

因为生命周期不同,为了内部类引用外部类的局部变量,强行加final续命,具体看挺哥的文章
50,通过jvm理解 匿名内部类和lambda中的闭包实现方式 - 图4
所以需要加上final,让这个局部变量的寿命 +1 +1 +1

4.jvm解读

解读匿名内部类

image.png
image.png
再看看整体
image.png
总结:
属性里面有闭包内部类
内部类构造器(init)方法:把外部类和局部变量(自动加上final)传进来,赋值给field
打印的时候用field

解读lambda

image.png
总结:
属性里面有内部类启动方法
调用invokedynamic指令的时候,相当于会跑到一个新的自动生成的方法,这个方法里面就有this和str,而且jvm自动加上了final,因此是不可修改的

代码调戏final?

image.png
image.png
由于jvm自动帮我们加了final,哪怕你不是final,也不允许更改,调戏失败

总结

这篇文章主要说了匿名内部类通过自动加上2个final实例字段,并通过构造器赋值(final外部类引用,final外部类局部变量),然后再通过实例字段进行使用
没说的地方是lambda是通过jvm自动生成lambda方法的字节码,然后通过局部变量表this和final局部变量的方式,达到同样的效果
总结:殊途同归 jvm实现方式不同