public class OuterClass {
/**
- 定义一个公有成员变量
*/
public Object publicVariable = “public member variable”;
private Object privateVariable = “private member variable”;
/**
- 定义两个私有成员变量
*/
private Object privateVariable2 = “private member variable2”;
/**
-
定义一个私有成员方法
-
@param parameter
*/
private void privateMethod(String parameter) {
System.out.println(parameter);
}
/**
- 调用inner class
*/
public void show() {
InnerClass innerClass = new InnerClass();
innerClass.print();
}
/**
- inner class
*/
class InnerClass {
void print() {
// inner class里面直接调用了外部类的私有成员变量和成员方法
System.out.println(privateVariable);
privateMethod(“invoke outer class private method.”);
// 调用外层类的公有变量
System.out.println(publicVariable);
}
}
public static void main(String[] args) {
new OuterClass().show();
}
}
上面代码定义了外层类是 OuterClass ,内部类是 InnerClass 。外层类定义了1个公有变量、2个私有变量和1个私有方法。然后在InnerClass里面直接使用了OuterClass的所有成员。程序运行结果如下:
private member variable
invoke outer class private method.
public member variable
反编译一下上面的两个类:
javap -p OuterClass.class
Compiled from “OuterClass.java”
public class OuterClass {
public java.lang.Object publicVariable;
private java.lang.Object privateVariable;
private java.lang.Object privateVariable2;
public OuterClass();
private void privateMethod(java.lang.String);
public void show();
public static void main(java.lang.String[]);
注意这两个静态方法
static java.lang.Object access$000(OuterClass);
static void access$100(OuterClass, java.lang.String);
}
javap -p OuterClass$InnerClass.class
Compiled from “OuterClass.java”
class OuterClass$InnerClass {
注意这个final的成员变量和下面的构造函数
final OuterClass this$0;
OuterClass$InnerClass(OuterClass);
void print();
}
结论就是:
-
编译器修改了内部类:增加了一个final的外层类实例作为内部类的成员变量;修改了内部类的构造函数,将外部类实例通过内部类的构造函数传递给内部类。这样内部类就有了外部类的实例,上面的第1个条件就达成了。
-
编译器在外部类中增加了几个非private的静态方法。对于内部类访问外部类的每一个私有成员,都会有这么一个方法。这样内部类就可以通过这些静态方法去访问外部类的私有成员了。非私有的成员直接通过1中的外层类实例即可访问,所以就无需生成这些静态方法了。
再进一步验证一下上面的结论1,当执行 InnerClass innerClass = new InnerClass(); 语句创建一个内部类实例之后,可以观测到下面的结果:
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
可以看到,内部类实例( OuterClass$InnerClass@470 )自动引用了外层类实例( OuterClass@464 )。所以, inner class之所以能访问外层类的成员是因为它在实例化的时候就已经和一个外层类的实例关联了 ,实际是通过这个外层类实例去访问外层类的成员。对于私有成员,还生成了一些辅助的静态方法。这也说明,要实例化inner class,必须先实例化它的外层类。
另外有个限制就是inner class里面不能定义静态变量和静态方法,一个例外是可以定义基础类型和String类型的静态常量。比如:
static final String s = “s”; // OK
static final int i = 5; // OK
static final Integer ii = 5; // 错误
local class和anonymous class都属于特殊的inner class,所以上面讲述的所有东西对他们也适用。
为什么static nested class不能访问外层类的非静态成员
=======================================
原因很简单,static nested class除了被定义到某个类里面以外,几乎和普通的类没有什么区别。 它不会和外层类的某个实例关联 ,比如我们在上面的OuterClass里面再定义一个StaticNestedClass :
static class StaticNestedClass {
private int a;
void foo() {}
}
反编译以后:
javap -p OuterClass$StaticNestedClass.class
Compiled from “OuterClass.java”
class OuterClass$StaticNestedClass {
private int a;
OuterClass$StaticNestedClass();
void foo();
}
除了类名被改写了以外,和原来定义的类没有任何区别。所以如果没有被定义为private的话,static nested class完全可以独立于外层类使用。
所有nested class都可以访问外层类的静态成员
===============================
上面讨论的都是nested class能不能访问外层类的非静态成员,那如果是静态成员呢?结论就是所有nested class都可以访问的静态成员,不管是不是私有。原理的话和inner class访问外层类非static成员是一样的,如果是private的,编译器会在外层中生成一个辅助访问的static方法,如果是非私有的,那通过类就可以直接访问。
## 如果nested class是private的?
我们知道正常的类是不能使用private和protected的(只能是public或者不加访问修饰符),但nested class却可以,因为nested class其实就是外层类的一个特殊成员,就像成员变量、成员方法一样。比如,如果我们不想让外部的其它类看到nested class的类,就可以将它设置成private的,但对于外层类是没有影响的,照样可以操作这个类。这个怎么做到的呢?
我们将上面的StaticNestedClass改为private的:
private static class StaticNestedClass {
void foo() {
System.out.println(a);
}
}
反编译看下: