Java内部类

简介

多继承

java中一个类不能直接继承两个类

比如说这样:class A extends B,C
不能这样写,因为java不支持多继承,但是你可以像下面这样实现继承多个类
class A extends B
class C extends A

这样C就同时继承了B和A两个类。

在C++中,这个多继承就好解决的多了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A{
public:virtual void foo(){
printf("A");
}
}
calss B{
public:virtual void foo(){
printf("B");
}
}

class x:public A,public B,...{
void foo()
printf("X");
}
}

菱形继承问题——python

Python是支持多重继承的,但为了解决多重继承的方法查找顺序问题(被称为MRO),有一场苦难史。

传统模式

直接使用深度优先算法(并且,从左向右),每个类选择其第一次出现的位置。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
def save(self):
pass

class B(A):
pass

class C:
def save(self):
pass

class D(B, C):
pass

作为D类的实例,成员搜索顺序为D=>B(=>A)=>C

解释:

  • 默认优先搜索当前类,然后搜索父类,所以D优先级最高,其次B的优先级高于A。
  • 因为继承有先后顺序,所以B的优先级高于C。
  • 综上,搜索顺序为D=>B(=>A)=>C

但如果是下面的情况,就会出现菱形继承问题。

1
2
3
4
5
6
7
8
9
10
class A:
def save(self):
pass
class B(A):
pass
class C(A):
def save(self):
pass
class D(B, C):
pass

问题:

这种情况的问题就是在D类调用save方法时,究竟是调用的谁的实现?

分析:

根据传统模式,D的实例调用save方法时最终会调用A类下的save方法,而B类中的方法时永远也不会有被调用的机会。而这样的特性,也会在设计上变得异常复杂和难以处理。

new-style class——新式类

关键字:广度优先

新式类是python为解决菱形问题而提出的解决方案。Python2.2中引进了new-style class,说白了就像java一样,所有类都继承自最根本的object类。这就让“菱形继承”变成十分普遍,于是只能改变MRO策略。仍然使用深度优先搜索、从左向右,但是每个类选择其最后一次出现的位置。这样一来,对上面的“菱形继承”就处理比较完美了,形成的顺序是:D、B、C、A,越是公用基础类,越放在后面。

交叉继承问题:
> 后续补充
#### C3算法
> 后续补充

接口继承

Java的接口继承功能,既实现了静态语言的多重继承性,又避免了多重继承的数据构造的冲突和类层次的复杂性。但是,我们并不能说接口是解决问题的完美方案。接口也有不能共享实现的缺点。本来只是为了跨越继承层次来共享代码,现在却需要另外生成一个独立对象,而且每次方法调用都要委派给这个对象,这实在是不太合理,而且执行的效率也不高。
——《松本行弘的程序世界》

总结:
>* 内部类的出现,正是Java为避免多继承而引出的难题而采用了单继承的设计模式,以及接口在多继承上特性上的不足而提出的一个新的概念。
>* 在程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

作用——《Thinking in java

  • 1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立
  • 2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
  • 3、创建内部类对象的时刻并不依赖于外围类对象的创建。

    待考证。

  • 4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
  • 5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

内部类基础

暂时不讨论反射对内部类的影响。

定义及使用

回顾一下类的定义语法

  • 一个java文件有且只有一个公有类。
  • 一个公有类仅且只能有一个main方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//公有类
public class Outter {
//内部类
public class InnnerOfOuter {
public void func(){
//局部内部类
class InnerOfFunc{
public void func(){
//匿名内部类
new Runnable(){
@Override
public void run() {
System.out.println("annoymous class");
}
};
}
}
}
}
}
class Outter1{
}

解读:

  • Outter为公有类,可以被任何引入的地方调用。
    1
    Outter outter = new Outter();
  • Outter1为公有类的同级类,只能在当前class字节码中调用。
  • InnerOfOutter为成员内部类,可以看做是类型为class的成员变量,根据修饰符也可以被外界引入,但其生命周期依赖于外部类。
    1
    2
    Outter outter = new Outter();
    Outter.InnnerOfOuter innnerOfOuter = outter.new InnnerOfOuter();
  • InnerOfFunc为局部内部类,存在于方法中。
  • AnnoymousClass为匿名内部类,基本只出现在只使用一次的地方。

成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类

局部内部类

有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

下面是Thinking in java中的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Parcel5 {
public Destionation destionation(String str){
class PDestionation implements Destionation{
private String label;
private PDestionation(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestionation(str);
}

public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destionation d = parcel5.destionation("chenssy");
}
}

匿名内部类

在做Swing编程中,我们经常使用这种方式来绑定事件,有点像临时定义了一个类及实例给调用者。

1
2
3
4
5
6
7
button2.addActionListener(  
new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("你按了按钮二");
}
}
);

注意:

  • 1、 匿名内部类是没有访问修饰符的。
  • 2、 new 匿名内部类,这个类首先是要存在的。没错,接口其实也是class字节码。
  • 4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

静态内部类

静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

  • 1、 它的创建是不需要依赖于外围类的。
  • 2、 它不能使用任何外围类的非static成员变量和方法。

最后

到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass 的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.classOuterClass$InnerClass.class

问题:

  • 1.静态内部类的实例会被GC回收吗?
  • 2.为什么静态内部类不能包含静态变量?

参考