Java 私有内部类的探索

在学习中遇到的问题:
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        
}
  • 静态内部类与外部类基本没有太大的关联,因此也就无法访问外部类的普通属性和方法等;同类它的初始化也就无需外部类的对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值