成员内部类详解

Posted SQP51312

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了成员内部类详解相关的知识,希望对你有一定的参考价值。

成员内部类

作为外部类的一个成员存在的类称为成员内部类。


注意:

必须先创建外部类才能创建内部类。

成员内部类不能含有static的变量和方法。因为成员内部类需要先创建外部类,才能创建它自己的。 


代码:

public class Outer {
    private String s1 = "this is s1 in Outer";
    private String s2 = "this is s2 in Outer";

    public void method1() {
        // 外部类可通过内部类的对象调用内部类的私有成员变量或方法
        System.out.println(new Inner().s1);
        System.out.println(new Inner().method2());
    }

    private String method2() {
        return "this is method2 in Outer";
    }

    public class Inner {
        private String s1 = "this is s1 in Inner";
        public final static String s2 = "this is s2 in Inner";

        public void method1() {
            // 内部类可直接使用外部类的私有成员变量或方法
            System.out.println(s2);
            // 内部类和外部类有同名变量和方法时
            System.out.println(s1);
            System.out.println(Outer.this.s1);
            System.out.println(method2());
            System.out.println(Outer.this.method2());
        }

        private String method2() {
            return "this is method2 in Inner";
        }
    }
}

调用:

public class MainClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        System.out.println("------外部类测试--------");
        outer.method1();
        System.out.println("------内部类测试--------");
        outer.new Inner().method1();
        System.out.println(Outer.Inner.s2);
    }
}

打印:

------外部类测试--------
this is s1 in Inner
this is method2 in Inner
------内部类测试--------
this is s2 in Inner
this is s1 in Inner
this is s1 in Outer
this is method2 in Inner
this is method2 in Outer
this is s2 in Inner

分析:

反编译后自动生成文件:Outer$Inner.class

Outer 反编译代码1:

public class jichu.Outer {
  private java.lang.String s1;
  private java.lang.String s2;
  public jichu.Outer();
  public void method1();
  private java.lang.String method2();
  static java.lang.String access$0(jichu.Outer);
  static java.lang.String access$1(jichu.Outer);
  static java.lang.String access$2(jichu.Outer);
}

Outer 反编译代码2:

public class Outer
{
  private String s1 = "this is s1 in Outer";
  private String s2 = "this is s2 in Outer";
  
  public void method1()
  {
    System.out.println(new Inner().s1);
    System.out.println(new Inner().method2());
  }
  
  private String method2()
  {
    return "this is method2 in Outer";
  }
  
  public class Inner
  {
    private String s1 = "this is s1 in Inner";
    public static final String s2 = "this is s2 in Inner";
    
    public Inner() {}
    
    public void method1()
    {
      System.out.println("this is s2 in Inner");
      
      System.out.println(this.s1);
      System.out.println(Outer.this.s1);
      System.out.println(method2());
      System.out.println(Outer.this.method2());
    }
    
    private String method2()
    {
      return "this is method2 in Inner";
    }
  }
}

Outer$Inner反编译代码1:

public class jichu.Outer$Inner {
  private java.lang.String s1;
  public static final java.lang.String s2;
  final jichu.Outer this$0;
  public jichu.Outer$Inner(jichu.Outer);
  public void method1();
  private java.lang.String method2();
  static java.lang.String access$0(jichu.Outer$Inner);
  static java.lang.String access$1(jichu.Outer$Inner);
}

Outer$Inner反编译代码2:

public class Outer$Inner
{
  private String s1 = "this is s1 in Inner";
  public static final String s2 = "this is s2 in Inner";
  
  public Outer$Inner(Outer paramOuter) {}
  
  public void method1()
  {
    System.out.println("this is s2 in Inner");
    
    System.out.println(this.s1);
    System.out.println(Outer.access$0(this.this$0));
    System.out.println(method2());
    System.out.println(Outer.access$1(this.this$0));
  }
  
  private String method2()
  {
    return "this is method2 in Inner";
  }
}

MainClass反编译代码:

public class MainClass
{
  public static void main(String[] args)
  {
    Outer outer = new Outer();
    System.out.println("------外部类测试--------");
    outer.method1();
    System.out.println("------内部类测试--------");
Outer tmp33_32
= outer;
tmp33_32.getClass();
new Outer.Inner(tmp33_32).method1();
System.out.println("this is s2 in Inner"); } }

MainClass中有代码:

Outer tmp33_32 = outer;
new Outer.Inner(tmp33_32).method1();

可以看出生成Inner对象时,将tmp33_32(即外部类对象outer的引用)传入Inner的构造器中,这个构造器从哪来的?

Outer$Inner中有代码:

public jichu.Outer$Inner(jichu.Outer);

这是编译器自动生成的构造器,参数为外部类对象,通过将tmp33_32传入构造器,来建立外部类与内部类的联系。

Outer$Inner中有代码:

 final jichu.Outer this$0;

猜测Outer$Inner通过构造器将外部类对象的引用赋予变量this$0;

当内部类调用外部类的私有变量和方法时,

Outer$Inner中有代码:

System.out.println(Outer.access$0(this.this$0));
System.out.println(Outer.access$1(this.this$0));
System.out.println(Outer.access$2(this.this$0));

Outer中有代码:

static java.lang.String access$0(jichu.Outer);
static java.lang.String access$1(jichu.Outer);
static java.lang.String access$2(jichu.Outer);

access$i是JAVA编译器自动生成的十分重要的方法(该方法的个数与内部类要访问的外部类的变量个数相关),目的是:用于内部类访问外部类的数据成员时使用。

在内部类中通过Outer.access$i(this.this$0)的方式,可实现对外部类的私有变量和方法的随意访问。


疑问

成员内部类不能含有static的变量和方法。但是测试发现变量可以被static final修饰,为什么?

主要是因为final类型在编译期间jvm有优化,常量池会维护这些变量。虽然非静态内部类不能脱离外部类这个上下文实例化,但是常量池使得final变量脱离了类实例化这个条件,编译期间便可确定。

Outer$Inner中有代码:

public static final String s2 = "this is s2 in Inner";

MainClass中有代码:

 System.out.println(Outer.Inner.s2);

通过Outer.Inner.s2来访问static final的变量;

MainClass反编译代码中有:

 System.out.println("this is s2 in Inner");

s2在编译时被"this is s2 in Inner"直接替换。 


总结:

1、内部类可直接使用外部类的私有成员变量或方法,外部类对内部类是可见的;

2、外部类中可以创建私有内部类对象。

3、外部类可通过内部类的对象调用内部类的私有成员变量或方法;

4、当内部类与外部类有同名成员变量和方法时,内部类可通过Outer.this方式来引用外部类成员。

5、必须先创建外部类才能创建内部类。

6、成员内部类不能含有static的变量和方法。因为成员内部类需要先创建外部类,才能创建它自己的。 

 

以上是关于成员内部类详解的主要内容,如果未能解决你的问题,请参考以下文章

匿名内部类详解

JAVA基础——内部类详解

静态内部类详解

Java连载58-静态内部类成员内部类详解

Java中内部类详解—匿名内部类

java-内部类详解