从字节码的角度看Java内部类与外部类的互相访问
Java中non-static内部类为何可以访问外部类的变量?Java中外部类又为何可以访问内部类的private变量?这两个问题困扰过我一段时间,查了一些网上的答案,大多从“闭包”概念入手,理解起来很是费劲,能否从另外一个角度来解释这个问题呢?有句话叫做“真正了不起的程序员应该对每一个字节都了如指掌”,而弄明白Java程序的“每个字节”还是相对容易的,下面就通过一段Java代码的bytecode来分析:
public class Test
{
public static void main(String[] args)
{
new Test().initData();
} private void initData()
{
new A().privateVar = 0;
new B().privateVar = 0;
} // non-static inner class A
private class A
{
private int privateVar;
int defaultVar;
protected int protectedVar;
public int publicVar;
} // static inner class B
private static class B
{
private int privateVar;
int defaultVar;
protected int protectedVar;
public int publicVar;
}
}
由于Java内部类会编译成单独的.class文件,我们用javap命令反编译每个.class文件来一探究竟。
non-static内部类A的bytecode如下:
E:\workspace\testClass\bin>javap -c Test$A
Compiled from "Test.java"
class Test$A extends java.lang.Object{
int defaultVar; protected int protectedVar; public int publicVar; final Test this$0; Test$A(Test, Test$A);
Code:
0: aload_0
1: aload_1
2: invokespecial #25; //Method "<init>":(LTest;)V
5: return static void access$1(Test$A, int);
Code:
0: aload_0
1: iload_1
2: putfield #29; //Field privateVar:I
5: return }
static内部类B的bytecode如下:
E:\workspace\testClass\bin>javap -c Test$B
Compiled from "Test.java"
class Test$B extends java.lang.Object{
int defaultVar; protected int protectedVar; public int publicVar; Test$B(Test$B);
Code:
0: aload_0
1: invokespecial #20; //Method "<init>":()V
4: return static void access$1(Test$B, int);
Code:
0: aload_0
1: iload_1
2: putfield #23; //Field privateVar:I
5: return }
从bytecode可以很清晰地看出,non-static内部类A的默认构造函数实质上传入了两个参数,第一个是外部类Test对象的引用,并且在内部类A中用final对象来持有这一引用(另一个参数是返回值,A的引用,与本文阐述主题无关),而static内部类B的默认构造函数则没有传入外部类Test对象的引用。这样就回答了第一个问题,non-static内部类是通过隐含传入的外部类对象的引用来完成对外部类的访问的。
再看A和B中均有针对内部类private变量提供了一个access静态方法(注:若没有针对private变量的访问,编译器会把access方法优化掉,所以必须存在外部类访问内部类private变量的代码才有此方法),那么这一方法是否就是外部类可以访问内部类private变量的原因呢?反编译外部类Test的.class文件可以得到:
E:\workspace\testClass\bin>javap -verbose Test
Compiled from "Test.java"
public class Test extends java.lang.Object
SourceFile: "Test.java"
InnerClass:
#42= #22 of #1; //A=class Test$A of class Test
#43= #31 of #1; //B=class Test$B of class Test
minor version: 0
major version: 50
Constant pool:
const #1 = class #2; // Test
const #2 = Asciz Test;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Method #3.#9; // java/lang/Object."<init>":()V
const #9 = NameAndType #5:#6;// "<init>":()V
const #10 = Asciz LineNumberTable;
const #11 = Asciz LocalVariableTable;
const #12 = Asciz this;
const #13 = Asciz LTest;;
const #14 = Asciz main;
const #15 = Asciz ([Ljava/lang/String;)V;
const #16 = Method #1.#9; // Test."<init>":()V
const #17 = Method #1.#18; // Test.initData:()V
const #18 = NameAndType #19:#6;// initData:()V
const #19 = Asciz initData;
const #20 = Asciz args;
const #21 = Asciz [Ljava/lang/String;;
const #22 = class #23; // Test$A
const #23 = Asciz Test$A;
const #24 = Method #22.#25; // Test$A."<init>":(LTest;LTest$A;)V
const #25 = NameAndType #5:#26;// "<init>":(LTest;LTest$A;)V
const #26 = Asciz (LTest;LTest$A;)V;
const #27 = Method #22.#28; // Test$A.access$1:(LTest$A;I)V
const #28 = NameAndType #29:#30;// access$1:(LTest$A;I)V
const #29 = Asciz access$1;
const #30 = Asciz (LTest$A;I)V;
const #31 = class #32; // Test$B
const #32 = Asciz Test$B;
const #33 = Method #31.#34; // Test$B."<init>":(LTest$B;)V
const #34 = NameAndType #5:#35;// "<init>":(LTest$B;)V
const #35 = Asciz (LTest$B;)V;
const #36 = Method #31.#37; // Test$B.access$1:(LTest$B;I)V
const #37 = NameAndType #29:#38;// access$1:(LTest$B;I)V
const #38 = Asciz (LTest$B;I)V;
const #39 = Asciz SourceFile;
const #40 = Asciz Test.java;
const #41 = Asciz InnerClasses;
const #42 = Asciz A;
const #43 = Asciz B; {
public Test();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0 LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTest; public static void main(java.lang.String[]);
Code:
Stack=2, Locals=1, Args_size=1
0: new #1; //class Test
3: dup
4: invokespecial #16; //Method "<init>":()V
7: invokespecial #17; //Method initData:()V
10: return
LineNumberTable:
line 5: 0
line 6: 10 LocalVariableTable:
Start Length Slot Name Signature
0 11 0 args [Ljava/lang/String; }
从进入initData方法栈后的代码分析,首先是调用了A的<init>方法,这个方法是自动生成的两个方法之一,用于调用A的构造函数(另外一个是<cinit>,用于在虚拟机第一次加载.class时初始化静态变量等),随后访问了A中的private变量,通过.操作符的访问已经被更替为const #27 = Method #22.#28; // Test$A.access$1:(LTest$A;I)V,虽然没法知道#22.#28代表的含义,不过javap已经很人性化地给我们加上了注释,标明这一段就是在调用A中的access方法,而后面$1的含义表示这是A中第一个private变量;对B中private变量的访问大体相同,不再多说。至此,我们已经可以回答第二个问题,外部类是通过内部类隐含的access静态方法来访问其中的private变量的,并没有破坏private修饰符的作用原则。
另求助一下:哪位仁兄能发一下JVM部分的源码(下载JDK后目录下的src.zip是JDK部分的源码,不是说的这个),以前在sun的官网好像还看到过,现在在oracle的网站上找不着了。。。
从字节码的角度看Java内部类与外部类的互相访问的更多相关文章
- Java内部类与外部类的那些事
昨天去笔试的时候遇到了Java的内部类的创建方式与访问权限的问题,我不懂,没写,故今天起来特意去试验一下,就有了这篇总结性的文章. Java中的内部类又分为非静态内部类(匿名内部类也是非静态的内部类) ...
- java内部类 和外部类的区别
java 内部类和静态内部类的区别 详细连接https://www.cnblogs.com/aademeng/articles/6192954.html 下面说一说内部类(Inner Class)和 ...
- java内部类和外部类
1.使用static可以声明一个内部类, 可以直接在外部调用 class Outer{ // 定义外部类 private static String info = "hello world& ...
- Java内部类引用外部类中的局部变量为何必须是final问题解析
今天编写一个多线程程序,发现在方法内定义内部类时,如果内部类调用了方法中的变量,那么该变量必须申明为final类型,百思不得其解,后来想到应该是生命周期的原因,因为方法内定义的变量是局部变量,离开该方 ...
- Java内部类与外部类
错误提示: 没有任何类型 TestThread 的外层实例可访问.必须用类型 TestThread 的外层实例(例如,x.new A(),其中 x 是 TestThread 的实例)来限定分配. pu ...
- java 内部类与外部类的区别
最近在看Java相关知识的时候发现Java中同时存在内部类以及非公有类概念,而且这两个类都可以不需要单独的文件编写,可以与其他类共用一个文件.现根据个人总结将两者的异同点总结如下,如有什么不当地方,欢 ...
- Java内部类和外部类的通信探索
1.内部类访问外部类的成员和方法 在内部类中,可以无障碍地访问外部类的所有成员和方法. 在下面的实验代码中,可以看到,内部类sl可以访问外部类的私有成员:sz 和 cur. 同时可以访问私有方法:pr ...
- Java内部类持有外部类的引用详细分析与解决方案
在Java中内部类的定义与使用一般为成员内部类与匿名内部类,他们的对象都会隐式持有外部类对象的引用,影响外部类对象的回收. GC只会回收没有被引用或者根集不可到达的对象(取决于GC算法),内部类在生命 ...
- 学以致用,通过字节码理解:Java的内部类与外部类之私有域访问
目录: 内部类的定义及用处 打开字节码理解内部类 一.内部类的定义及用处 内部类(inner class)是定义在另一个类中的类.使用内部类,我们可以: 访问该类定义所在的作用域中的数据,包括私有的数 ...
随机推荐
- hyperledger-fabirc1.2-ca-server的生产示例
hyperledger-fabirc1.2-ca-server的生产示例,带TLS 在fabirc-samples/first-network中启动网络,其ca证书是利用crypto的工具生成的,但是 ...
- LeetCode 633. Sum of Square Numbers平方数之和 (C++)
题目: Given a non-negative integer c, your task is to decide whether there're two integers a and b suc ...
- Restful api 防止重复提交
当前很多网站是前后分离的,前端(android,iso,h5)通过restful API 调用 后端服务器,这就存在一个问题,对于创建操作,比如购买某个商品,如果由于某种原因,手抖,控件bug,网络错 ...
- Python day5 --------递归、匿名函数、高阶函数、内置函数
一.递归 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. 递归要求: 1. 必须有一个明确的结束条件 2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减 ...
- BZOJ2324 ZJOI2011营救皮卡丘(floyd+上下界费用流)
虽然不一定每次都是由编号小的点向编号大的走,但一个人摧毁的顺序一定是从编号小的到编号大的.那么在摧毁据点x的过程中,其只能经过编号小于x的点.并且这样一定合法,因为可以控制其他人先去摧毁所经过的点.那 ...
- MT【182】系数奇怪的二次函数
设函数$f(x)=3ax^2-2(a+b)x+b,$其中$a>0,b\in R$证明:当$0\le x\le 1$时,$|f(x)|\le \max\{f(0),f(1)\}$ 分析:由$a&g ...
- 【BZOJ1432】[ZJOI2009]Function(找规律)
[BZOJ1432][ZJOI2009]Function(找规律) 题面 BZOJ 洛谷 题解 这...找找规律吧. #include<iostream> using namespace ...
- 解决360WiFi有时候手机连接不上
有可能是无线网卡的问题: 右击“计算机”->选择“管理”->“设备管理器”->网络适配器->选择“Broadcom 802.11n 网络适配器”,或者你实在不知道哪个是无线网 ...
- 解题:HEOI 2012 朋友圈
题面 因为$A$中只有奇偶性不同的人才能做朋友,所以A中只可能出0/1/2个人,分类讨论 然后$B$中求最大团,转成补图后正好是个二分图(不然就不用做了),求最大点独立集=总点数-最大匹配 我洛谷上交 ...
- CF1025D Recovering BST
题意:给定序列,问能否将其构成一颗BST,使得所有gcd(x, fa[x]) > 1 解:看起来是区间DP但是普通的f[l][r]表示不了根,f[l][r][root]又是n4的会超时,怎么办? ...