【Java面试】为什么匿名内部类只能访问外部类的final类型局部变量?

简介: 【Java面试】为什么匿名内部类只能访问外部类的final类型局部变量?

先来看一下下面一段代码

public class InnerClassTest {
    public static void main(String[] args) {
        int a = 10;
        new Service() {
            @Override
            public void method() {
                System.out.println("a=" + a);
            }
        }.method();
        a = 11;
    }
}
interface Service {
    public void method();
}

这段代码并不能通过编译,因为他会抱有如下异常:

这里发现,我的匿名内部类调用外部的局部变量的时候发生了报错,那么这个报错的原因是什么?

我们先来看解决这个报错的方法:

1:删掉下面的对a=11的修改,这意味着a这个值并没有被修改,是只读的

2:将a变量设定为final类

两种方法都能解决上面的问题,但是为什么我们使用外部的局部变量的时候我们需要它是未被修改的或者说为什么必须是final的?

答:

匿名内部类无法直接访问外部类方法中的局部变量,除非该变量被声明为final类型,是因为匿名内部类在实例化时会隐式地持有对外部类方法中的局部变量的引用。为了确保引用的可用性和一致性,Java编译器要求局部变量必须是final类型的。

当一个局部变量被声明为final时,Java编译器会在内存中创建一个拷贝,而不是直接引用原始变量。这样做的目的是为了避免匿名内部类中对外部局部变量的修改导致不一致的情况发生。

通过将局部变量声明为final,Java编译器确保了匿名内部类在获取局部变量的值时,能够获取到该变量的固定值。这样,即使外部方法调用已经结束,局部变量仍然可以正确地被匿名内部类所访问和使用。

需要注意的是,在Java 8之后,如果局部变量被显式声明为final,即使没有使用final关键字,同样可以在局部类或匿名内部类中访问。这是因为Java 8引入了"effectively final"的概念,即在变量被赋值后,没有再发生修改。在这种情况下,编译器会将其视为final类型的变量,从而允许在局部类或匿名内部类中访问该变量。这也就是为什么上面我们只要把a=11这一行代码删掉也可以通过运行的原因。

当然,如果你的JDK版本是7或者更早,那么就依旧会报错,如下:

面试官:为什么匿名内部类只能访问外部类的final类型局部变量?

我对这个问题的完整解释是这样子回答的

我:其实对于为什么需要使用final类型的外部局部变量,我的解释应该会倾向于生命周期的概念。

我们知道,匿名内部类的调用发生在方法中,方法创建时会创建一个栈帧,栈帧中保存的是我们的局部变量等信息,这个时候如果我们使用了匿名内部类,还会再堆中创建一个类,然后如果我们的这个匿名内部类使用了外部变量,而外部变量的创建是跟随方法的,如果方法结束,那么外部变量就要被回收消失,此时会出现生命周期的问题,也就是我们的匿名内部类还指向这个方法,并且内部类还没有被回收,因为堆内对象的回收需要的是垃圾回收器的工作而不是跟随方法,即使这个对象是通过这个方法才创建的。

因此此时就会出现外部变量消失的情况,而匿名内部类依旧存在于堆内存中并且对外部变量存有引用,为了解决这种生命周期不一致的问题,可以使用final关键字修改局部变量的生命周期,我们知道如果对局部变量使用final修饰,他就会在内存中留有一份数据。

当局部变量被声明为final时,它们在内存中会保留其值。在使用final修饰的局部变量时,其值在声明时被确定,并且不能再被修改。这样做的目的是为了确保在匿名内部类或其他类的方法中使用这些final变量时,它们的值保持不变。

在编译过程中,如果一个局部变量被匿名内部类或其他闭包引用,编译器会创建一个新的内部类,并将这些被引用的final局部变量的值传递给内部类的构造函数。因此,这些final局部变量的值将在内存中一直存在,直到内部类对象不再被引用,并由垃圾回收器回收。

值得注意的是,如果局部变量没有被匿名内部类或其他闭包引用,即使将其声明为final,它们在方法执行完毕后仍然会被销毁,不会一直保存在内存中。只有在有需要的情况下才将局部变量声明为final。


目录
打赏
0
0
0
0
5
分享
相关文章
java面试-基础语法与面向对象
本文介绍了 Java 编程中的几个核心概念。首先,详细区分了方法重载与重写的定义、发生阶段及规则;其次,分析了 `==` 与 `equals` 的区别,强调了基本类型和引用类型的比较方式;接着,对比了 `String`、`StringBuilder` 和 `StringBuffer` 的特性,包括线程安全性和性能差异;最后,讲解了 Java 异常机制,包括自定义异常的实现以及常见非检查异常的类型。这些内容对理解 Java 面向对象编程和实际开发问题解决具有重要意义。
57 15
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
190 14
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
80 13
Java 高级面试技巧:yield() 与 sleep() 方法的使用场景和区别
本文详细解析了 Java 中 `Thread` 类的 `yield()` 和 `sleep()` 方法,解释了它们的作用、区别及为什么是静态方法。`yield()` 让当前线程释放 CPU 时间片,给其他同等优先级线程运行机会,但不保证暂停;`sleep()` 则让线程进入休眠状态,指定时间后继续执行。两者都是静态方法,因为它们影响线程调度机制而非单一线程行为。这些知识点在面试中常被提及,掌握它们有助于更好地应对多线程编程问题。
156 9
写出Java中局部变量的概念及特点
写出Java中局部变量的概念及特点
148 0
|
2月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
213 60
【Java并发】【线程池】带你从0-1入门线程池
|
21天前
|
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
56 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
100 23
|
1月前
|
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
161 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码

热门文章

最新文章

下一篇
oss创建bucket
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等