Java学习笔记22---内部类之成员内部类的继承问题

时间:2024-09-23 20:07:26

成员内部类可以继承其他的类,也可以被其它类继承,本文主要说明其它类继承成员内部类的问题。

本文要点如下:

1).成员内部类的子类可以是内部类,也可以不是内部类;

2).当成员内部类的子类不是内部类或子类虽是内部类但与其父类不在同一个外部类时,子类的构造方法第一句要显式添加如下的语句:

外部类对象引用.super(参数);

这里的外部类指父类所在的外部类;

3).编译器默认给成员内部类的构造方法传入一个参数,该参数是内部类所依附的外部类对象的引用;

注:本文涉及到的类都是非静态类

作者: 蝉蝉

请尊重作者劳动成果,转载请在标题注明“转载”字样,并标明原文链接:

http://www.cnblogs.com/chanchan/p/8345144.html

参考资料:

http://www.cnblogs.com/dolphin0520/p/3811445.html

背景知识点:

1).内部类是如何依附于外部类的?

编译器编译时,是把外部类与成员内部类编译成两个独立的文件的;

但会给成员内部类默认添加一个类型为外部类对象引用的成员变量并为构造方法默认传入一个类型为外部类对象引用的参数,并以该参数值来初始化该成员变量;

如下所示:

final Person this$0;

InnerClass(Person per){

    ...

    this$0 = per;

    ...

  }

也就是说,成员内部类的对象保存了一个外部类对象的引用,通过这个引用,内部类就可以无限制的访问外部类的成员了。

从这里也可以看出,创建成员内部类对象之前必须要存在一个外部类对象,即,成员内部类是依附于外部类的。

2).创建子类对象时,要先调用父类构造方法再调用子类构造方法。

详细参见笔记11

3).子类构造方法中如果没有显式调用super(参数),则会在构造方法最前面默认添加super();。

下面分三种情况来讨论:

1.子类是内部类且与父类位于同一个外部类

外部类是Person,父类是成员内部类InnerClassParent,子类是成员内部类InnerClassChild。

     //笔记22--成员内部类--父类
class InnerClassParent {
InnerClassParent (){
System.out.println("内部类--父类");
}
} //笔记22-成员内部类--子类
class InnerClassChild extends InnerClassParent {
InnerClassChild(){
System.out.println("内部类的子类是内部类且位于同一外部类");
}
} public static void main(String[] args) {
Person per22 = new Person();
InnerClassChild inCCh = per22.new InnerClassChild();
}

输出结果为:

内部类--父类
内部类的子类是内部类且位于同一外部类

分析:

  1).由背景知识点1)可知,编译后,

子类实际的样子大概是如下这样的:

     //笔记22-成员内部类--子类
class InnerClassChild extends InnerClassParent {
final Person this$0;

InnerClassChildIn(Person per){
super();
this$0 = per;
System.out.println("内部类的子类是内部类且位于同一外部类");
}
}

父类实际的样子大概是如下这样的:

     //笔记22--成员内部类--父类
class InnerClassParent {
final Person this$0;
InnerClassParent(Person per){
this$0 = per;
System.out.println("内部类--父类");
}
}

  2).根据上面的分析,作出执行的流程图,如下所示:

Java学习笔记22---内部类之成员内部类的继承问题

1>.先创建外部类对象per,再通过per来创建其成员内部类InnerClassChild(子类);

2>.要创建子类对象,必须先加载父类再加载子类(这里子类、父类都是初次使用,尚未加载),然后初始化父类成员并调用父类构造方法,最后再初始化子类成员并调用子类的构造方法;

3>调用完子类构造方法后,子类对象inCCh创建完成;

  3).子类对象inCCh创建后的内存结构图如下所示:

Java学习笔记22---内部类之成员内部类的继承问题

子类对象创建完成后,子类与父类都有一个外部类对象引用this$0,且都指向了per。

相关知识点:

1>.堆、栈,参见笔记把大端、小端与堆、栈的生长方向联系起来记忆

2>.隐藏与覆盖,参见笔记12

2.子类是内部类且与父类位于不同的外部类

外部类DustMan的成员内部类是InnerClassChildDM,InnerClassChildDM继承了外部类Person的成员内部类InnerClassParent。

子类InnerClassChildDM的代码如下:

     //笔记22--成员内部类--子类为内部类且不在同一个外部类
class InnerClassChildDM extends Person.InnerClassParent {
InnerClassChildDM(Person per){
per.super();
System.out.println("成员内部类的子类为内部类且与父类不在同一个外部类");
}
}
public static void main(String[] args) {
DustMan du = new DustMan();
Person per = new Person();
InnerClassChildDM inCChDM = du.new InnerClassChildDM(per);
}

输出结果如下:

内部类--父类
成员内部类的子类为内部类且与父类不在同一个外部类

分析:

    1).编译后,子类的实际样子大概是下面这样的:

     //笔记22--成员内部类--子类为内部类且不在同一个外部类
class InnerClassChildDM extends Person.InnerClassParent {
final DustMan this$0;
InnerClassChildDM(DustMan du, Person per){
per.super();
this$0 = du;
System.out.println("成员内部类的子类为内部类且与父类不在同一个外部类");
}
}

    2).与第一种情况不同的是,子类InnerClassChildDM中显式添加了per.super();

这是因为,子类与父类处于不同的外部类,编译时,子类的构造方法默认传入的是DustMan类的对象引用,而不是Person类的对象引用;

如果要调用父类的构造方法的话,必须要给它传入一个其外部类Person的对象引用;

所以这时,子类的构造方法显式传入一个Person类对象引用per,并通过per.super();的方式把per传给父类的构造方法并调用之。

    3).执行的流程图如下所示:

Java学习笔记22---内部类之成员内部类的继承问题

    4).子类对象inCChDM创建完成后的内存分配图如下:

Java学习笔记22---内部类之成员内部类的继承问题

其中,子类的this$0是指向DustMan类的对象du的,父类的this$0是指向Person类的对象per的。

3.子类不是内部类

InnerClassChild不是内部类,它继承了外部类Person的成员内部类InnerClassParent。

 package human;

 public class InnerClassChild extends Person.InnerClassParent {
InnerClassChild(Person per) {
per.super();
System.out.println("内部类的子类不是内部类");
} public static void main( String[] args ) {
Person per = new Person();
InnerClassChild inCCh = new InnerClassChild(per);
}
}

输出结果如下:

内部类--父类
内部类的子类不是内部类

分析:

    1).子类不是内部类,所以编译器不会在其构造方法中传入其外部类的对象引用。

    2).子类的构造方法也显式的添加了per.super();语句,原因同上。

    3).执行的流程图如下所示:

Java学习笔记22---内部类之成员内部类的继承问题

    4).子类对象inCChNoI创建完成后,内存分配图如下:

Java学习笔记22---内部类之成员内部类的继承问题

其中,只有父类有this$0成员变量,且指向了Person类的对象per。

总结:

成员内部类的非静态子类可以是与其位于同一个外部类的子类,也可以是位于不同外部类的子类,还可以是一般类。

后两种情况,必须在子类的构造方法中显式添加 父类的外部类的对象.super(参数); 这样一条语句,以保证为父类传入其外部类的对象引用,继而保证能调用父类的构造方法。