Java中,为什么外部类可以访问内部类的对象访问私有成员变量

2676人阅读
Java面向对象与模式(1)
今天在分析android的AMS的时候,发现在AMS中出现了内部类访问外部类私有方法的操作,一时之间觉得有些奇怪:为毛private的方法可以被访问??之前的认识是private方法是不可以通过对象方式调用的,但是现在的代码确实这么做了!!为什么呢?
带着这个问题,我google了一下,最终找到了这个stackoverflow的帖子:
帖子中有很多人给予了热情的回复,大家可以看一下。要说明这个问题,需要说明一下在什么样的情况下使用内部类:
1. 你的某个类中的部分功能需要放在一个单独的类中实现,这样比较有利于代码的可读性和可维护性
2. 但是这部分的功能和现在这个类中的一些方法有这密切的关系。
这个时候,你就应该使用内部类,可以看到,内部类的设计目的就是为了将外部类中的一些功能方便地隔离出来,但是为了方便这个类的操作,所以这个类需要对外部类的一些属性和方法进行访问。也就是说这个时候,内部类只是外部类的一部分和外部类中的某个成员变量没有什么区别,当然可以互相访问了,包括私有属性和方法!这是从设计上对这个问题的解释,从编译和JVM角度来说的话,那就是java在编译的时候内部类和外部类的数据处理是共享的,也就是说所有的数据都是可以互相看到的。
下面给出一个测试代码,实际测试一个内部类和外部类的互相访问的问题:
class OuterClass {
private int OuterPrivateInt = 0;
public int OuterPublicInt = 1;
public OuterClass() {
private void OuterPrivateMethod() {
System.out.println("outer private method.");
public void OuterPublicMethod() {
System.out.println("outer public method.");
public void outerTest() {
System.out.println("***********************Outer Test***********************");
InnerClass innerObj = new InnerClass();
System.out.println("inner class private int: " + innerObj.InnerPrivateInt);
System.out.println("inner class public int: " + innerObj.InnerPublicInt);
innerObj.InnerPublicMethod();
innerObj.InnerPrivateMethod();
System.out.println("***********************Outer Test*********************** \n");
innerObj.innerTestOne();
innerObj.innerTestTwo();
class InnerClass {
private int InnerPrivateInt = 2;
public int InnerPublicInt = 3;
private void InnerPrivateMethod() {
System.out.println("inner private method.");
public void InnerPublicMethod() {
System.out.println("inner public method.");
public void innerTestOne() {
System.out.println("***********************Inner Test One***********************");
OuterPrivateMethod();
OuterPublicMethod();
System.out.println("outer class private int: " + OuterPrivateInt);
System.out.println("outer class public int: " + OuterPublicInt);
System.out.println("***********************Inner Test One*********************** \n");
public void innerTestTwo() {
System.out.println("***********************Inner Test Two***********************");
OuterClass outerObj = new OuterClass();
System.out.println("outer class private int: " + outerObj.OuterPrivateInt);
System.out.println("outer class public int: " + outerObj.OuterPublicInt);
outerObj.OuterPrivateMethod();
outerObj.OuterPublicMethod();
System.out.println("***********************Inner Test Two***********************");
public class Demo {
public static void main(String[] arg) {
OuterClass obj = new OuterClass();
obj.outerTest();
运行结果如下:
从实际的运行结果来看,内部类和外部类的访问就是通行的,可以自由访问!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:152808次
积分:2409
积分:2409
排名:第16133名
原创:82篇
转载:15篇
评论:56条
我的github地址:/CreateChance
(1)(1)(3)(3)(8)(3)(6)(9)(1)(3)(1)(1)(12)(3)(16)(13)(5)(1)(3)(6)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'深入理解Java中为什么内部类可以访问外部类的成员
内部类简介
虽然是一门相对比较简单的语言,但是对于初学者, 还是有很多东西感觉云里雾里, 理解的不是很清晰。内部类就是一个经常让初学者感到迷惑的特性。 即使现在我自认为Java学的不错了, 但是依然不是很清楚。其中一个疑惑就是为什么内部类对象可以访问外部类对象中的成员(包括成员变量和成员方法)? 早就想对内部类这个特性一探究竟了,今天终于抽出时间把它研究了一下。
内部类就是定义在一个类内部的类。定义在类内部的类有两种情况:一种是被static关键字修饰的, 叫做静态内部类, 另一种是不被static关键字修饰的, 就是普通内部类。 在下文中所提到的内部类都是指这种不被static关键字修饰的普通内部类。 静态内部类虽然也定义在外部类的里面, 但是它只是在形式上(写法上)和外部类有关系, 其实在逻辑上和外部类并没有直接的关系。而一般的内部类,不仅在形式上和外部类有关系(写在外部类的里面), 在逻辑上也和外部类有联系。
这种逻辑上的关系可以总结为以下两点:
1 内部类对象的创建依赖于外部类对象;
2 内部类对象持有指向外部类对象的引用。
上边的第二条可以解释为什么在内部类中可以访问外部类的成员。就是因为内部类对象持有外部类对象的引用。但是我们不禁要问, 为什么会持有这个引用? 接着向下看, 答案在后面。
通过反编译字节码获得答案
在源代码层面, 我们无法看到原因,因为Java为了语法的简介, 省略了很多该写的东西, 也就是说很多东西本来应该在源代码中写出, 但是为了简介起见, 不必在中写出,编译器在编译时会加上一些代码。 现在我们就看看Java的编译器为我们加上了什么?
首先建一个工程TestInnerClass用于测试。 在该工程中为了简单起见, 没有创建包, 所以源代码直接在默认包中。在该工程中, 只有下面一个简单的文件。
public class Outer {
int outerField = 0;
class Inner{
void InnerMethod(){
int i = outerF
该文件很简单, 就不用过多介绍了。 在外部类Outer中定义了内部类Inner, 并且在Inner的方法中访问了Outer的成员变量outerField。
虽然这两个类写在同一个文件中, 但是编译完成后, 还是生成各自的class文件:
这里我们的目的是探究内部类的行为, 所以只反编译内部类的class文件Outer$Inner.class 。 在命令行中, 切换到工程的bin目录, 输入以下命令反编译这个类文件:
javap -classpath . -v Outer$Inner
-classpath .
说明在当前目录下寻找要反编译的class文件
加上这个参数输出的信息比较全面。包括常量池和方法内的局部变量表, 行号, 访问标志等等。
注意, 如果有包名的话, 要写class文件的全限定名, 如:
javap -classpath . -v com.baidu.Outer$Inner
反编译的输出结果很多, 为了篇幅考虑, 在这里我们省略了常量池。 下面给出除了常量池之外的输出信息。
final Outer this$0;
flags: ACC_FINAL, ACC_SYNTHETIC
Outer$Inner(Outer);
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield
// Field this$0:LO
5: aload_0
6: invokespecial #12
// Method java/lang/Object."":()V
LineNumberTable:
LocalVariableTable:
void InnerMethod();
stack=1, locals=2, args_size=1
0: aload_0
1: getfield
// Field this$0:LO
4: getfield
// Field Outer.outerField:I
7: istore_1
LineNumberTable:
LocalVariableTable:
首先我们会看到, 第一行的信息如下:
final Outer this$0;
这句话的意思是, 在内部类Outer$Inner中, 存在一个名字为this$0 , 类型为Outer的成员变量, 并且这个变量是final的。 其实这个就是所谓的“在内部类对象中存在的指向外部类对象的引用”。但是我们在定义这个内部类的时候, 并没有声明它, 所以这个成员变量是编译器加上的。
虽然编译器在创建内部类时为它加上了一个指向外部类的引用, 但是这个引用是怎样赋值的呢?毕竟必须先给他赋值, 它才能指向外部类对象。 下面我们把注意力转移到构造函数上。 下面这段输出是关于构造函数的信息。
Outer$Inner(Outer);
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield
// Field this$0:LO
5: aload_0
6: invokespecial #12
// Method java/lang/Object."":()V
LineNumberTable:
LocalVariableTable:
我们知道, 如果在一个类中, 不声明构造方法的话, 编译器会默认添加一个无参数的构造方法。 但是这句话在这里就行不通了, 因为我们明明看到, 这个构造函数有一个构造方法, 并且类型为Outer。 所以说,
编译器会为内部类的构造方法添加一个参数, 参数的类型就是外部类的类型。
下面我们看看在构造参数中如何使用这个默认添加的参数。 我们来分析一下构造方法的字节码。 下面是每行字节码的意义:
aload_0 :
将局部变量表中的第一个引用变量加载到操作数栈。 这里有几点需要说明。 局部变量表中的变量在方法执行前就已经初始化完成;局部变量表中的变量包括方法的参数;成员方法的局部变量表中的第一个变量永远是this;操作数栈就是执行当前代码的栈。所以这句话的意思是: 将this引用从局部变量表加载到操作数栈。
将局部变量表中的第二个引用变量加载到操作数栈。 这里加载的变量就是构造方法中的Outer类型的参数。
// Field this$0:LO
使用操作数栈顶端的引用变量为指定的成员变量赋值。 这里的意思是将外面传入的Outer类型的参数赋给成员变量this$0 。
这一句putfield字节码就揭示了, 指向外部类对象的这个引用变量是如何赋值的。
下面几句字节码和本文讨论的话题无关, 只做简单的介绍。 下面几句字节码的含义是: 使用this引用调用父类(Object)的构造方法然后返回。
用我们比较熟悉的形式翻译过来, 这个内部类和它的构造函数有点像这样: (注意, 这里不符合Java的语法, 只是为了说明问题)
class Outer$Inner{
final Outer this$0;
public Outer$Inner(Outer outer){
this.this$0 =
说到这里, 可以推想到, 在调用内部类的构造器初始化内部类对象的时候, 编译器默认也传入外部类的引用。 调用形式有点像这样: (注意, 这里不符合java的语法, 只是为了说明问题)
vcq9ysfP4M2stcShoyDU2sTasr/A4LXESW5uZXJNZXRob2S3vbeo1tCjrCC3w87KwcvN4rK/wOC1xLPJ1LGx5MG/b3V0ZXJGaWVsZKOsIM/Cw+a1xNfWvdrC673Syr7By7fDzsrKx8jnus69+NDQtcSjugo8YnI+Cgo8cHJlIGNsYXNzPQ=="brush:">
void InnerMethod();
stack=1, locals=2, args_size=1
0: aload_0
1: getfield
// Field this$0:LO
4: getfield
// Field Outer.outerField:I
7: istore_1
// Field this$0:LO
将成员变量this$0加载到操作数栈上来
// Field Outer.outerField:I
使用上面加载的this$0引用, 将外部类的成员变量outerField加载到操作数栈
将操作数栈顶端的int类型的值保存到局部变量表中的第二个变量上(注意, 第一个局部变量被this占用, 第二个局部变量是i)。操作数栈顶端的int型变量就是上一步加载的outerField变量。 所以, 这句字节码的含义就是: 使用outerField为i赋值。
上面三步就是内部类中是如何通过指向外部类对象的引用, 来访问外部类成员的。
文章写到这里, 相信读者对整个原理就会有一个清晰的认识了。 下面做一下总结:
本文通过反编译内部类的字节码, 说明了内部类是如何访问外部类对象的成员的,除此之外, 我们也对编译器的行为有了一些了解, 编译器在编译时会自动加上一些逻辑, 这正是我们感觉困惑的原因。
关于内部类如何访问外部类的成员, 分析之后其实也很简单, 主要是通过以下几步做到的:
1 编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象的引用;
2 编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为1中添加的成员变量赋值;
3 在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。本帖子已过去太久远了,不再提供回复功能。22:46 提问
java内部类 引用外部类对象this问题
在内部类里,想引用外部类的对象,为什么是外部类.this而不是外部类加引用变量,还有.操作应该是成员操作符吧,这样this不就成了外部类成员了
按赞数排序
因为类.变量表示静态变量,this.变量才是当前实例的变量
当成员变量和局部变量重名时,在方法中使用this时,表示的是该方法所在类中的成员变量。(this是当前对象自己)
把自己当作参数传递时,也可以用this.(this作当前参数进行传递)
this有作用域的,默认是内部类实例。使用内部类的一个好处就是用外部类.this可以拿到外部类的当前实例。
你好,实际上我们代指当前类对象的this是个简写形式,完整的形式就是 类名字.this,举个例子来说吧
class OuterClass
private String outAttribute = "我是外部类属性";
public void printOutAttribute(){
//return outA
//return this.outA
//上面写法都可以
System.out.println(OuterClass.this.outAttribute);
public static void main (String[] args) throws java.lang.Exception
new OuterClass().printOutAttribute();
像上面这种情况,在类的非静态方面中,对当前类对象的引用,可以用OuterClass.this,也可以直接用this。
但是下面代码里,InnerClass对当前OuterClass的类对象引用就只能用OuterClass.this,不能用this。
class OuterClass
private String outAttribute = "我是外部类属性";
public void printOutAttribute(){
new Thread(new Runnable(){
public void run(){
//这里引用外部类对象只能用OuterClass.this
System.out.println(OuterClass.this.outAttribute);
}).start();
public static void main (String[] args){
new OuterClass().printOutAttribute();
原因很简单,在内部类非静态方法中使用this会被编译器认定为InnerClass.this,只有使用完成写法OuterClass.this才能找到正确的对象引用。
----------------------同志你好,我是CSDN问答机器人小N,奉组织之命为你提供参考答案,编程尚未成功,同志仍需努力!
其他相关推荐

我要回帖

更多关于 对象访问私有成员变量 的文章

 

随机推荐