在学习中遇到的问题:
1. 为什么不能在类的外部直接创建一个内部类.
2. 为什么内部类可以直接访问外部类的属性和方法,包括私有的。
1. 测试代码
public class Main {
public static void main(String[] args) {
/*
* No enclosing instance of type Outside is accessible. Must qualify the
* allocation with an enclosing instance of type Outside (e.g. x.new A()
* where x is an instance of Outside).
*/
// c.a.Outside.Inside inside = newc.a.Outside.Inside();
Outside outside = new c.at.Outside();
c.a.Outside.Inside inside = outside.new Inside();
c.a.Outside.StaticInside stinside = new c.a.Outside.StaticInside();
}
}
public class Outside {
Inside inside = new Inside();
StaticInside stinside = new StaticInside();
private int pint1=2;
private int pint2=2;
private double pdouble=123d;
public class Inside {
public void a() {
//这里引用外部类属性
stinside.b();
pint1=3;
pdouble=111d;
int a=pint2;
}
}
public static class StaticInside {
public void b() {
//Cannot make a static reference to the non-static field inside
//inside.a();
}
}
}
从代码实验来看,在类的外部,直接初始化一个内部类,编译时无法通过的。提示如下信息,而静态内部类则没有问题。根据提示,在创建了外部类的情况下,通过外部类对象的引用,成功的创建内部类。由此我们可以得出以下三条:
- 内部类不允许在没有已经实例化的外部类的情况下直接实例化。
- 如果已经拥有实例化的外部类的对象,则可以通过该对象进行内部类的创建。
- 静态内部类,可以直接被实例化。
对以上两个java文件进行编译,生成以下class文件:
由此可见,内部类是Java编译器一手操办的。虚拟机并不知道内部类与常规类有什么不同。这说明内部类Inner仍然被编译成一个独立的类(Outer$Inner.class),而不是Outer类的某一个域。 虚拟机运行的时候,也是把Inner作为一种常规类来处理的。
为了能够清晰地看到编译器对java文件做什么,我们使用java提供的工具 javap来将三个类进行反编译,生成以下内容.
2. 生成的Outside类文件
Compiled from "Outside.java"
public class c.a.Outside {
c.a.Outside$Inside inside;
c.a.Outside$StaticInside stinside;
public c.a.Outside();
Code:
0: aload_0
1: invokespecial #17 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #19 // class c/a/Outside$Inside
8: dup
9: aload_0
10: invokespecial #21 // Method c/a/Outside$Inside."<init>":(Lc/a/Outside;)V
13: putfield #24 // Field inside:Lc/a/Outside$Inside;
16: aload_0
17: new #26 // class c/a/Outside$StaticInside
20: dup
21: invokespecial #28 // Method c/a/Outside$StaticInside."<init>":()V
24: putfield #29 // Field stinside:Lc/a/Outside$StaticInside;
27: aload_0
28: iconst_2
29: putfield #31 // Field pint1:I
32: aload_0
33: iconst_2
34: putfield #33 // Field pint2:I
37: aload_0
38: ldc2_w #35 // double 123.0d
41: putfield #37 // Field pdouble:D
44: return
//为内部类访问外部类的属性而创建的静态方法
static void access$0(c.a.Outside, int);
Code:
0: aload_0
1: iload_1
2: putfield #31 // Field pint1:I
5: return
static void access$1(c.a.Outside, double);
Code:
0: aload_0
1: dload_1
2: putfield #37 // Field pdouble:D
5: return
static int access$2(c.a.Outside);
Code:
0: aload_0
1: getfield #33 // Field pint2:I
4: ireturn
}
外部类特意为内部类访问私有属性创建了相应的静态方法,当内部类对外部对进行赋值时,会自动生成set的方法,取值时会生成get法.
外部类的构造函数中,按照,属性的定义顺序,依次进行实例化。所以即使没有定义,在构造函数中的new操作,也依然会放在构造函数中进行执行。(这个只是顺便提一下)
3. 生成的Outside$Inside类文件
Compiled from "Outside.java"
public class c.a.Outside$Inside {
final c.a.Outside this$0;
public c.a.Outside$Inside(c.a.Outside);
Code:
0: aload_0
1: aload_1
2: putfield #10 // Field this$0:Lc/a/Outside;
5: aload_0
6: invokespecial #12 // Method java/lang/Object."<init>":()V
9: return
public void a();
Code:
0: aload_0
1: getfield #10 // Field this$0:Lc/a/Outside;
4: getfield #20 // Field c/a/Outside.stinside:Lc/a/Outside$StaticInside;
7: invokevirtual #26 // Method c/a/Outside$StaticInside.b:()V
10: aload_0
11: getfield #10 // Field this$0:Lc/a/Outside;
14: iconst_3
15: invokestatic #31 // Method c/a/Outside.access$0:(Lc/a/Outside;I)V
18: aload_0
19: getfield #10 // Field this$0:Lc/a/Outside;
22: ldc2_w #35 // double 111.0d
25: invokestatic #37 // Method c/a/Outside.access$1:(Lc/a/Outside;D)V
28: aload_0
29: getfield #10 // Field this$0:Lc/a/Outside;
32: invokestatic #41 // Method c/a/Outside.access$2:(Lc/a/Outside;)I
35: istore_1
36: return
}
- 内部类会持有一个的外部类的对象的引用,
- 内部类默认拥有一个以外部类对象为参数的构造函数。所以在没有外部类对象情况下,是无法创建内部类对像的。
因为内部类会有一个外部类的对象,所以内部类,可以对外不类的一些公开属性和方法进行操作。但是内部类,为什么可以操作,外部类的私有属性呢?
4. 生成的Outside$StaticInside类文件
Compiled from "Outside.java"
class c.a.Outside$StaticInside {
c.a.Outside$StaticInside();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public void b();
Code:
0: return
}
- 静态内部类与外部类基本没有太大的关联,因此也就无法访问外部类的普通属性和方法等;同类它的初始化也就无需外部类的对象。