![Java无难事:详解Java编程核心思想与技术](https://wfqqreader-1252317822.image.myqcloud.com/cover/59/35011059/b_35011059.jpg)
7.4 局部内部类
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_17.jpg?sign=1738884969-pR2uKU63NyMfkPVeMbKpc3Ux5gMWYy1T-0-aa5127580d650f88ce81e82d66d54f3e)
扫码看视频
还可以在方法中定义内部类,甚至在语句块中也可以定义内部类,这种情况通常是某个方法需要创建一个类来辅助完成该方法的功能,而这个类只用在该方法中。局部内部类需要配合接口来一起使用。我们看一个例子,如代码7.8所示。
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_18.jpg?sign=1738884969-xWykDa2t3yOs5wiyQNQ4YGy86cjY3sTc-0-0ad56df03a0d057b6f6c84314fdab088)
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_19.jpg?sign=1738884969-0bvfDHpLYnQt7Jnt6bDeZAQugizAomKr-0-a48151b0e132ae68703174e8f323d9d5)
局部内部类限定了该类只能在局部作用域内被访问,因此是无法直接返回该类型的对象的,需要向上转型为其实现的接口类型。此外,局部内部类不能使用public和private访问说明符进行声明。
程序的运行结果是:
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_20.jpg?sign=1738884969-0zqlnLuqh7xL8beK2u5FIoQTpMnRRt2m-0-6cf722c9875a2268922d894478d159be)
在本例中,局部内部类MySpeaker有一个实例变量str,其值是方法getSpeaker的形参str的值,在构造内部类对象时,通过new MySpeaker(str)传入进去。实际上,在局部内部类中可以直接访问方法的参数,这样就可以减少内部类的实例变量数。修改代码7.8,删除内部类MySpeaker的实例变量str,改为直接访问getSpeaker方法的参数,如代码7.9所示。
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_21.jpg?sign=1738884969-4ZVTrxCLZGW26IlmTFlB9CITSENWOS6l-0-23fad9c6747d5ca6d225e7f4f8fd7e9e)
可以看到,代码变得更加简洁了。
我们来分析一下main方法中的调用过程:
(1)创建LocalInnerClass类的对象lic。
(2)调用lic的getSpeaker方法,传入实参“Local inner class”,方法形参str有值了。
(3)getSpeaker方法返回Speaker接口类型的对象,该方法结束,清理方法所在的栈空间,形参str被清理。
(4)调用Speaker对象的speak方法,输出getSpeaker方法的形参str的值。
嗯?形参str不是被清理了,不复存在了吗?
实际上,早期版本的JDK要求局部内部类在访问本地变量时(方法形参或方法内部定义的局部变量),该本地变量必须声明为final,而我们知道final代表的是“最终的”“不可修改的”,这样该变量就变成了常量,并被保存到常量池中,简单来说,就是改变了本地变量的生存时间。背后的实现就是在编译后的内部类中自动生成了一个名字为“val$变量名”的final实例变量,然后在内部类对象创建时,将方法的参数传递给构造方法,用于初始化这个final常量。之后内部类对象访问的都是自己的这个final常量。
从Java 8开始,已经不要求将本地变量必须声明为final,不过其背后的实现原理是一样的,这些工作都是由编译器来完成的。