恕我孤陋寡闻,因为这次面试才第一次听说Java中的函数闭包

​最近接到了一个公司的视频面试,在面试过程中被问到在Java中匿名内部类和lambda表达式中如果使用外部的变量,需要注意什么,当然,如果在开发中使用过lambda表达式或匿名内部类,并且需要使用外部的变量的话,即使自己没有这个意识,idea也会给出提示必须使用final进行修饰,否则,就报出受检异常了,在程序编译器就不能通过。然后我就直接说必须使用final进行修饰了,否则报错,程序不能通过编译,不料对方随即就问,为什么要这样做呢,为什么非要使用final进行修饰呢?emmm(表情开始逐渐凝固),然后陷入沉思,自己想lambda是匿名内部类的优化方式,肯定也是基于匿名内部类进行实现的,然后从内部类上分析,发现没有找到答案,从实现类和接口的结构上想想,仍然是没有头绪,然后自己想难道是在编译成class文件中就是这样要求的,但是我也没有反编译过lambda之后的class文件,根本不知道啊,捉鸡,慌了,awsl,面试官似乎看出了我的尴尬,最后指给我说了下这涉及到函数闭包的概念,然后就下个问题了。

然后自己私下里查了些资料,大众的说法函数闭包的概念就是:

闭包是ECMAScript (JavaScript)最强大的特性之一,Java并不能显式地支持闭包,但对于非静态内部类而言(注意是非静态内部类,稍后会有各种内部类的区别对比),它不仅记录了其外部类的详细信息,还保留了一个创建非静态内部类对象的引用,并且可以直接调用外部类的private成员,因此可以把非静态内部类当成面向对象领域的闭包。

其实形象化地理解下来就是lambda本质还是调用的方法,如果使用的变量是基本类型就要final修饰,相当于值传递,如果是引用类型就不用final修饰,引用传递。值传递是使用其他方法栈里面的变量,不应该修改其他方法栈里面的基本类型变量。

我随便找了个例子看一下区别:

这里基本类型threadCount在lambda中进行++操作,但是threadCount并没有被final进行修饰,当然这里就报错了。

然后这种情况下我测试的是latch这个引用类型变量,因为引用类型传递的是引用类型的指针,所以其指针指向的内容可以随意修改,并没有什么问题。演示效果这里就不再执行了。

然后还有人给出的解决方法是对于基本类型可以使用包装类型进行代替,其实本质还是将其替换成引用类型。

当然对于函数闭包应该谨慎使用,因为泛滥使用函数闭包会使大量的内存得不到释放导致内存泄漏。对于函数闭包这是个人的理解,若有出入还望指正。

作为记录,在这里罗列一下这次面试中能够回忆到的大致一些点吧:

Redis相关的:

①、zSet的使用(怎么实现类似MySQ Llimit功能)

②、incr是先加后获取值还是先获取值然后执行加1

③、Redis数据结构实现原理

④、Redis单线程模型分析

MySQL相关的:

①、B+树基本存储结构

②、B+树为什么非叶子结点不存储数据

③、联合索引的最左匹配原则(各种场景举例判断是否走索引)

④、MySQL8使用索引的index merge特性

⑤、SQL一般优化方法(索引的使用、索引失效、explain的使用-哪些场景下看哪些字段判断需要进行SQL优化和SQL优化的入手点)

⑥、MySQL锁相关-间隙锁

Java SE相关:

①、forkJoinPool的原理

②、forkJoinPool的思想(分而治之)以及分而治之适合、不适合哪些场景

③、函数闭包

④、Automic包下类实现原理(CAS、UNSAFE、缓存一致性协议)

源码相关:

①、mybatis Executor除了BATCH还有哪些类型,BATCH有没有其他替代方式

②、mybatis怎么控制事务回滚、提交

③、spring事务传播机制原理

④、IOC、AOP原理

能回忆到的大致这些吧,还有一些忘了,幸运的是这次面试过了,继续第二次线上面试,其实这次面试还是挺贴近基础的,至于分布式、并发场景、大数据量处理其实没有涉及太多,Java技术栈各种框架源码还需要学习,加油吧,学习力才是竞争力。

附:

Java中的各种内部类对比区分:

①、成员内部类

class  Outer1{

      class Inner1{

      }

 }

(1)、成员内部类与实例变量/实例方法一样, 都是属于某个 Outer1对象的

(2)、在成员内部类中可以定义实例变量/实例方法, 不能定义静态成员

(3)、成员内部类可以直接使用外部类的成员

(4)、一般在Outer1外部类的实例方法中会创建成员内部类对象

(5)、所有的类编译后都会生成单独的字节码文件: Outer1$Inner1.class

②、静态内部类

class  Outer2{

           static  class Inner2{

          }

}

(1)、静态内部类与静态变量/静态方法一样, 不属于某个外部类对象

(2)、在静态内部类中不仅可以定义实例成员还可以定义静态成员

(3)、在静态内部类中,不能直接使用外部类的实例成员

(4)、一般在外部类Outer2的静态方法中, 创建静态内部类inner2的对象

(5)、编译为单独的字节码文件

③、局部内部类

class  Outer3{

       public  void   method  ()  {

            class  Inner3{

             }

       }

}

(1)、在方法体中定义的内部类就是局部内部类

(2)、它的使用域 是从定义的位置开始直到包含它的大括弧结束

(3)、编译为单独的字节码文件

④、匿名内部类

之前的内部类都有一个类名, 通过类名创建对象。

匿名内部类是指没有类名的内部类,  内部类的定义与内部类对象的创建是一起的。

接口/抽象类的引用可以赋值匿名内部类对象。

一般情况下, 当接口的实现类,或者抽象类的子类只使用一次的时候,  不需要单独的定义一个类, 直接使用匿名内部类即可。

 

更多内容持续更新中,感兴趣的朋友请移步至个人公众号,谢谢支持😜😜......

公众号:wenyixicodedog

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值