可以将一个类定义放在另一个类的定义内部,成为内部类。
内部类和组合是完全不同的概念。
10.1 创建内部类
把类的定义放在外围类的内部。
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,就必须在main()方法中具体地指明这个对象的类型。
10.2 链接到外部类
当生成一个内部类的对象时,此对象与制造它的外围对象(enclosing object)之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何条件。此外,内部类还拥有其外围类的所有元素的访问权。
C++嵌套类的设计中只是单纯的名字隐藏机制,与外围对象没有联系,也没有隐含的访问权。
内部类自动拥有对外围类成员的访问权
10.3 使用.this和.new
.this:生成对外部类对象的引用
.new:创建内部类对象的引用
创建内部类的对象,必须使用外部类的对象来创建该内部类对象,这也解决了内部类名字作用域的问题。
在拥有外部类对象之前是不可能创建内部类对象,内部类对象连接创建它的外部类对象。
但是嵌套类(静态内部类)不需要对外部类对象的引用。
10.4 内部类和向上转型
内部类向上转型为基类或者接口。
实现某个接口的对象,得到这个接口的引用。与向上转型为这个对象的基类效果相同。
接口的成员被自动定义为public
private内部类无法被创建对象引用。
内部类可以影响外围类的private对象。
外部类可以访问其内部类的private元素。
10.5 在方法和作用域内的内部方法
定义内部类的理由:
1)实现某个类型的接口,用于创建并返回对其的引用。
2)解决一个复杂的问题,创建一个不是公用的类来辅助解决问题的方案。
1)一个定义在方法中的类。(局部内部类)
2)一个定义在作用域内的类,此作用域在方法的内部。
3)一个实现了接口的匿名类。
4)一个匿名类,它扩展了有非默认构造器的类。
5)一个匿名类,它执行字段初始化。
6)一个匿名类,它通过实例初始化实现构造(匿名类不可能有构造器)。
10.6 匿名内部类
匿名类定义:
匿名类是不能有名字的类,它们不能被引用,只能在创建时用New语句来声明它们。匿名类的声明是在编译时进行的,实例化在运行时进行,这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
匿名累不累使用外部定义的对象时,需要将参数引用定义为final类型
匿名内部类既可以扩展类,也可以实现接口。但两者不能兼得,而且只能实现一个接口.
10.6.1 再访工厂方法
匿名内部类工厂方法
在匿名内部类中创建工厂接口实现的返回对象引用。
10.7 嵌套类
将内部类声明为static以防止内部类对象与外部类对象之间有联系的方式成为嵌套类。
普通的外部类对象隐式地保存了一个指向创建它的外部类对象的引用。要声明嵌套类,需要声明类为static。
嵌套类:
1)要创建嵌套类的对象,并不需要其外围类的对象。
2)不能从嵌套类的对象中访问非静态的外围类对象。
嵌套类也可以称为静态内部类。
Java中嵌套类可以访问私有成员
10.7.1 接口内部的类
接口中的任何类都自动定义为public和static。所以接口中包含的嵌套类中可以添加方法。
使用嵌套类可以在接口中实现其他接口:接口不能直接实现其他接口,但是使用嵌套类可以在接口中间接的实现其他接口。
接口内部的嵌套类可以创建公共代码,使接口的不同实现所公用。
10.7.2 从多层嵌套类中访问外部类的成员
多层嵌套类中的类可以访问外围类的所有成员对象。
在main()方法中创建多层嵌套类的对象引用的方式
可以通过.new来创建。
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
.new语法能产生正确的作用域。所以不必在调用构造器时限定类名。
10.8 为什么需要内部类
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
内部类实现接口与外围类实现接口的区别:
外围类实现接口不是总能享用到接口带来的方便,有时需要用到接口的实现,如果外围类与内部类同时都可以使用,要是用外围类。
如果拥有的是抽象的类或具体的类,而不是借口,那就只能使用内部类才能实现多重继承。
内部类的特性:
1)内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类,稍后就会展示一个这样的例子。
3)创建内部类对象的时刻并不依赖于外围类对象的创建。
4)内部类并没有令人迷惑的“is-a”关系,它是一个独立的实体。
10.8.1 闭包与回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动又有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。
回调(callback),对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。
回调的价值在于它的灵活性,可以在运行时动态的决定需要调用什么方法。
10.8.2 内部类与控制框架
要运行某个应用程序架构,通常是继承一个或多个类,并覆盖某些方法。这里运用的是设计模式,所覆盖的某些方法为变化的事物。
控制框架是一类特殊的应用程序框架,它用来解决响应时间的需求,主要用来响应时间的系统被称为时间驱动系统。Java Swing库就是一个控制框架。
内部类允许:
1)控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装了起来,内部类
能够来表示解决问题所需的各种不同的action()。
2)内部类能够很容易地访问外围类的任意成员,所以可以避免这种实现变的笨拙。如果没有这种能力,代码将变得令人讨厌,以至于你肯定会选择别的方法。
10.9 内部类的继承
内部类构造器必须连接到指向其外围类对象的引用,并且此引用必须被初始化。
可以单独继承其他类的内部类。
10.10 内部类可以被覆盖吗
当继承某个外围类的时候,重写内部类并没有变化,两个内部类是完全独立的两个实体,也就是说内部类不可被覆盖。
10.11 局部内部类
局部内部类不能有访问说明符,因为它不是外围类的一部分。但是他可以访问当前代码块内的常量,以及此外围类的所有成员。
局部内部类和米名内部类具有相同的行为和能力。
局部内部类具有命名,可以重载构造器,而匿名内部类只能用于实例初始化。
10.12 内部类标识符
每个类都会产生一个.class文件,其中包含了如何创建该类型的对象的全部信息(此信息产生一个“meta-class",叫做Class对象)。内部类也会产生一个.class文件以包含他们的Class对象信息。
这些类文件的命名规则:外围类的名字,加上”$“,再加上内部类的名字。例如LocalInnerClass.java生成的.class文件包括:
Counter.class
LocalInnerClass$1.class
LocalInnerClass$1LocalCounter.class
LocalInnerClass.class
如果内部类是匿名的,编译器会简单的产生一个数字作为其标识符。
如果内部类是嵌套在别的内部类中。直接将它们的名字加在其外围类标识符与”$“的后面。
10.13 总结