从反编译认识内部类
来源:互联网 发布:唐筛检查结果数据分析 编辑:程序博客网 时间:2024/06/06 04:02
在此之前,我们已经讨论过了成员内部类可以无条件访问外部类的成员,那具体究竟是如何实现的呢?下面通过反编译字节码文件看看究竟。事实上,编译器在进行编译的时候,会将成员内部类单独编译成一个字节码文件,下面是Outter.java的代码:
编译之后,出现了两个字节码文件:
反编译Outter$Inner.class文件得到下面信息:
第11行到35行是常量池的内容,下面逐一第38行的内容:
这行是一个指向外部类对象的指针,看到这里想必大家豁然开朗了。也就是说编译器会默认为成员内部类添加了一个指向外部类对象的引用,那么这个引用是如何赋初值的呢?下面接着看内部类的构造器:
2.为什么局部内部类和匿名内部类只能访问局部final变量?
想必这个问题也曾经困扰过很多人,在讨论这个问题之前,先看下面这段代码:
这段代码会被编译成两个class文件:Test.class和Test1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outterx.class(x为正整数)。
根据上图可知,test方法中的匿名内部类的名字被起为 Test$1。
上段代码中,如果把变量a和b前面的任一个final去掉,这段代码都编译不过。我们先考虑这样一个问题:
当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制 的手段来解决这个问题。将这段代码的字节码反编译可以得到下面的内容:
我们看到在run方法中有一条指令:
这条指令表示将操作数10压栈,表示使用的是一个本地局部变量。这个过程是在编译期间由编译器默认进行,如果这个变量的值在编译期间可以确定,则编译器默认会在匿名内部类(局部内部类)的常量池中添加一个内容相等的字面量或直接将相应的字节码嵌入到执行字节码中。这样一来,匿名内部类使用的变量是另一个局部变量,只不过值和方法中局部变量的值相等,因此和方法中的局部变量完全独立开。
下面再看一个例子:
反编译得到:
我们看到匿名内部类Test$1的构造器含有两个参数,一个是指向外部类对象的引用,一个是int型变量,很显然,这里是将变量test方法中的形参a以参数的形式传进来对匿名内部类中的拷贝(变量a的拷贝)进行赋值初始化。
也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
从上面可以看出,在run方法中访问的变量a根本就不是test方法中的局部变量a。这样一来就解决了前面所说的 生命周期不一致的问题。但是新的问题又来了,既然在run方法中访问的变量a和test方法中的变量a不是同一个变量,当在run方法中改变变量a的值的话,会出现什么情况?
对,会造成数据不一致性,这样就达不到原本的意图和要求。为了解决这个问题,java编译器就限定必须将变量a限制为final变量,不允许对变量a进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。
到这里,想必大家应该清楚为何 方法中的局部变量和形参都必须用final进行限定了。
3.静态内部类有特殊的地方吗?
- 从反编译认识内部类
- 从Android适配器的一种写法认识静态内部类
- 认识内部类
- 认识内部类
- 反编译内部类代码解释
- 内部类的深度认识
- 带你认识内部类
- 十、内部类的认识
- 从反编译深入理解JAVA内部类类结构以及final关键字
- 领略内部类的“内部” [反编译代码]
- 内部类的认识与应用
- 从头认识java-8.1 内部类
- 从头认识java-8.6 匿名内部类
- 重新认识java(九) ---- 内部类
- 从内部类继承 内部类被覆盖 内部类生成的class文件名
- 【浅析】JAVA内部类的实现及反编译解析
- 从字节码角度深入分析内部类(局部内部类,成员内部类,静态内部类,匿名内部类)
- 关于java匿名内部类的一点认识
- myBatis Mapper接口注入null指针
- [leetcode]1. Two Sum
- 正则表达式
- HTML/CSS/JavaScript
- mysql安装配置
- 从反编译认识内部类
- dpdk中文-DPDK学习路线图
- 职场新人如何快速成长
- 【设计模式】纲要
- 开发收藏
- 详解第三方支付之无卡支付底层系统设计
- Generational GC
- ffmpeg 在linux下编译
- An internal error occurred during: “Build Project”. GC overhead limit exceeded