Java内部类final语义实现
本文描述在java内部类中,经常会引用外部类的变量信息。但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索。本文从字节码层描述在内部类中是如何实现这些语义的。
本地临时变量 基本类型
final int x = 10;
new Runnable() {
@Override
public void run() {
System.out.println(x);
}
}.run();
当输出内部类字节码(javap -p -s -c -v)时,如下所示:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: bipush 10 5: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 8: return
可以看出,此常量值直接被写在内部类的临时变量中,即相当于进行了一次变量copy。
本地临时变量 引用类型
final T t = new T();
new Runnable() {
@Override
public void run() {
System.out.println(t);
}
}.run();
字节码变为如下所示:
final T val$t;
flags: ACC_FINAL, ACC_SYNTHETIC
T$1(T);
Signature: (LT;)V
//构建函数的字节码
0: aload_0
1: aload_1
2: putfield #1 // Field val$t:LT;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
//main函数字节码
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field val$t:LT;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
可以看出,这时自动生成了一个带有1个参数的构造函数,并且将相应的t值作为参数传递到内部类当中,同时设定final语义,即不能被内部类修改。
上面的是无参构造函数,如果是一个有参数的内部类呢,如下所示:
Thread thread = new Thread("thread-1") {
@Override
public void run() {
System.out.println(t);
}
};
生成的字节码如下:
T$1(java.lang.String, T); Signature: (Ljava/lang/String;LT;)V
可以看出,编译器将自动对原来调用的构造函数进行了修改,将原来只需要1个参数的构造函数 修改为传2个参数,并且同时将相应的t传递进去。
引用字段,基本类型
int t = 3;
private void xx() {
new Runnable() {
@Override
public void run() {
System.out.println(t);
}
}.run();
}
生成的字节码如下:
T$1(T);
Signature: (LT;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LT;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
这里并没有如临时变量那样,直接在内部类中进行常量定义。为什么?因为这里的t对象随时可能被修改。
引用字段,引用类型
final String t = new String("abc");
private void xx() {
new Runnable() {
@Override
public void run() {
System.out.println(t);
}
}.run();
}
生成字节码如下:
final T this$0;
Signature: LT;
T$1(T);
//内部类构造函数
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LT;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
这里,在内部类的构造函数中,直接将外部类的this传递进来了,因此在内部类的run方法中,对于t,将直接两层getField进行调用,即可以拿到相应的信息。如下所示:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$0:LT; 7: getfield #4 // Field T.t:Ljava/lang/String; 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: return
引用类型,引用类型,static字段
static String t = new String("abc");
private void xx() {
new Runnable() {
@Override
public void run() {
System.out.println(t);
}
}.run();
}
字节码如下:
final T this$0;
Signature: LT;
flags: ACC_FINAL, ACC_SYNTHETIC
T$1(T);
Signature: (LT;)V
//构造函数字节码
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LT;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
//run方法字节码
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #4 // Field T.t:Ljava/lang/String;
6: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: return
可以看出,即使是引用static字段,在内部类中仍然会保留外部类的引用,即达到引用目的。同时,在run方法内部,因为是static字段,因此将不再使用getField,而是使用getStatic来进行相应字段的引用。
总结
在整个内部类字节码的生成规则中,主要采用了修改构造函数的方式来将需要在整个内部类中引用的变量进行参数传递。并且,因为是内部类,构造函数是已知的,可以随意的修改。针对特定的场景,可以进行一定的优化,如常量化(临时变量基本类型)。
因为在整个JVM层,并没有针对内部类作特殊的处理,因此这些处理手法都是在编译层进行处理的。同时,在语言层,针对这些生成的信息进行指定的说明。如SYNTHETIC语义。
在反射字段Member层,定义了如下方法:
/**
* Returns {@code true} if this member was introduced by
* the compiler; returns {@code false} otherwise.
*
* @return true if and only if this member was introduced by
* the compiler.
* @jls 13.1 The Form of a Binary
* @since 1.5
*/
public boolean isSynthetic();
即此信息是由编译器引入的。
了解这些对于整个语言层有一定的理解意义,但并不代表将来这些不会会改变,了解一些实现细节有助于自己在代码实现层有进一步的思考空间,并不局限于之前所了解的信息。
学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:454297367 我们一起学Java!
Java内部类final语义实现的更多相关文章
- 为什么java内部类访问局部变量必须声明为final?
https://blog.csdn.net/z55887/article/details/49229491 先抛出让我疑惑了很久的一个问题 编程时,在线程中使用局部变量时候经常编译器会提示:局部变量必 ...
- Java内部类详解
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- 黑马----JAVA内部类
黑马程序员:Java培训.Android培训.iOS培训..Net培训 黑马程序员--JAVA内部类 一.内部类分为显式内部类和匿名内部类. 二.显式内部类 1.即显式声明的内部类,它有类名. 2.显 ...
- java 内部类 *** 最爱那水货
注: 转载于http://blog.csdn.net/jiangxinyu/article/details/8177326 Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又 ...
- java内部类和匿名内部类
内部类即是包含在类里面的又一个类. java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 内部类的共性 (1).内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.clas ...
- Java内部类小程序(成员内部类,静态内部类,匿名内部类)
/** * 测试java内部类(成员内部类,静态内部类,匿名内部类) * 局部内部类不常用,就不写了. * @package :java05 * @author shaobn * @Describe ...
- [转] Java内部类详解
作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...
- java内部类的作用分析
提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比.内部类从表面上看,就 ...
- JAVA内部类(转)
源出处:JAVA内部类 在java语言中,有一种类叫做内部类(inner class),也称为嵌入类(nested class),它是定义在其他类的内部.内部类作为其外部类的一个成员,与其他成员一样, ...
随机推荐
- .NET CoreCLR开发人员指南(上)
1.为什么每一个CLR开发人员都需要读这篇文章 和所有的其他的大型代码库相比,CLR代码库有很多而且比较成熟的代码调试工具去检测BUG.对于程序员来说,理解这些规则和习惯写法非常的重要. 这篇文章让所 ...
- WinForm设置控件焦点focus
winform窗口打开后文本框的默认焦点设置,进入窗口后默认聚焦到某个文本框,两种方法: ①设置tabindex 把该文本框属性里的tabIndex设为0,焦点就默认在这个文本框里了. ②Winfor ...
- 编译器开发系列--Ocelot语言4.类型定义的检查
这里主要介绍一下检查循环定义的结构体.联合体.是对成员中包含自己本身的结构体.联合体进行检查.所谓"成员中包含自己本身",举例来说,就是指下面这样的定义. struct point ...
- Android中实现APP文本内容的分享发送与接收方法简述
谨记(指定选择器Intent.createChooser()) 开始今天的内容前,先闲聊一下: (1)突然有一天头脑风暴,对很多问题有了新的看法和见解,迫不及待的想要分享给大家,文档已经写好了,我需要 ...
- Atitit.项目修改补丁打包工具 使用说明
Atitit.项目修改补丁打包工具 使用说明 1.1. 打包工具已经在群里面.打包工具.bat1 1.2. 使用方法:放在项目主目录下,执行即可1 1.3. 打包工具的原理以及要打包的项目列表1 1. ...
- 用SecureCRT连接虚拟机中的Linux系统(Ubuntu)
今天突然练习linux命令行的时候,想在window中联系linux命令行.经过一番dudu找到了一个不错的的工具(SecureCRT--意思安全)就是用SSH链接linux主机.推荐大家使用.毕竟w ...
- [Django]用户权限学习系列之User权限基本操作指令
针对Django 后台自带的用户管理系统,虽说感觉还可以,但是为了方便用户一些操作,特别设计自定义的用户权限管理系统. 在制作权限页面前,首先需要了解权限和用户配置权限的指令,上章讲到权限的添加,删除 ...
- Linux上课笔记--随手记Linux命令
初次接触Linux就是感觉这系统不够友好不够人性化,因为首先接触电脑就是win,图形化界面什么操作都可以清晰看到.随着更多的接触越来越发现Linux的强大,虽然我只是一个小白,可我就是爱上他了.现在就 ...
- 为Xamarin更好的开发而改写的库
欢迎大家加入以下开源社区 Xamarin-Cn:https://github.com/Xamarin-Cn Mvvmcross-Cn:https://github.com/Mvvmcross-Cn ...
- 创建DbContext
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 上一篇介绍了<创建实体>,这一篇我们顺其自然地介绍<创建DbContext>. 温故: 提到DbConte ...