内部类和组合的概念彻底不一样。最初,内部类看起来就像是一种代码隐藏机制:将类置于其余类的内部。可是,内部类远不止如此,它了解外围类,并能与之通讯;并且用内部类写出的代码更加优雅而清晰。java
10.1闭包
若是想从外部类的非静态方法以外的任意位置建立内部类的对象,那么必须具体指明这个对象的类型:OuterClass.InnerClass。个人理解:内部类的对象能够看做是和外部类的对象相联的。没有外部类的对象,没法建立内部类的对象。函数
10.2测试
内部类的对象只能在与其外围类的对象相关联的状况下才能被建立,构建内部类对象时,须要一个指向其外围类对象的引用。this
10.3编码
在内部类中若是想使用外部类对象的引用,可使用"OuterClass.this"的形式。此引用自动的具备正确的类型,这一点在编译期被知晓并受到检查,所以没有任何运行时开销。spa
若是想要告知某些其余对象,去建立某个内部类的对象,必须在new表达式中提供对其外部类对象的引用:指针
Outer outer = new Outer();code
outer.new Inner();对象
要想直接建立内部类的对象,不能引用外部类的名字,而必须使用外部类的对象来建立该内部类对象。在拥有外部类对象以前是不可能建立内部类对象事务。这是由于内部类对象会暗暗地链接到建立它的外部类对象上。可是若是建立的是嵌套类(静态内部类),就不须要外部类对象的引用。
10.4
练习6中在类中的一个protected内部类,在另外包的类中是没法建立对象的,由于该内部类的构造函数和内部类拥有相同的访问属性,即protected。解决方法是显示写出内部类的构造函数,并把它设置为public。
此处须要注意的特性是,内部类的构造函数和内部类具备相同的访问属性。
练习8告诉咱们,外部类能够访问内部类的private域和方法。内部类和外部类的访问权限是双向的。
10.5
能够在一个方法里或者在任意的做用域内定义内部类。这么作有两个理由:
(1)实现了某个接口,因而能够建立并返回对其的引用;
(2)要解决一个复杂的问题,想建立一个类来辅助,但又不但愿这个类是公共可用的。
个人理解:这种在方法或做用域中的内部类与其余类一块儿被编译,并非说只有到做用域里才编译。可是,它们只在做用域中可见。
匿名类不可能有构造函数。
10.6
若是定义一个匿名内部类,而且但愿它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
因为匿名内部类没有构造函数,能够用语句块的形式对类的对象进行初始化。
练习12 要求在没有指定实现的接口或继承的基类的状况下返回一个匿名内部类,这时可用Object做为引用:
public Object getInnerClass() { return new Object() { ... } }
运用匿名内部类,修改接口那一章的工厂方法,使代码变得更整洁优雅:
10.7
静态的内部类也没称为嵌套类,它与普通内部的区别:
(1)要建立嵌套类的对象,并不须要外围类的对象;
(2)不能从嵌套类的对象中访问非静态的外围类数据;
(3)嵌套类能够有static方法和域,还能够有嵌套类,普通内部类不能够。
练习18 要求建立一个嵌套类并在main函数中建立其实例。答案说,若是在外围类中使用嵌套类,直接使用类名就能够,可是若是在其余地方使用嵌套类,则必须使用“外围类.嵌套类”的形式。通过代码测试,其实直接“import 外围类.嵌套类”也能够在其余地方使用嵌套类。
接口中能够包含嵌套类。放到接口中的任何类都自动的是public和static的。由于类是static的,只是将嵌套类置于接口的命名空间内,并不违反接口的规则。甚至能够在嵌套类中实现其外围接口。若是想要建立某些公共代码,使得它们能够被某个接口的全部不一样实现所共有,那么使用接口内部的嵌套类会很方便。
做者还提到了一个技巧:
他建议在每一个类中都写一个主函数用来测试这个类。这样有一个缺点,是必须带有这些额外的测试代码。可使用嵌套类来放置主函数和测试代码。好比在类TestBed中建立Tester嵌套类,包含主函数和测试代码。那么编译器会生成一个独立的类TestBed$Tester,要运行这个程序,执行java TestBed$Tester(Unix/Linux系统中须要转义$)。当发布产品的时候,简单的删除TestBed$Tester.class文件便可。
经编码测试接口中的嵌套类是不会继承到它的实现中的。
10.7.2小节主要讲了,多层内部类,不论层数有多少,它都能透明的访问全部外围类。我的认为这个讨论不该放在10.7中,由于本节主要讲述嵌套类(即静态的内部类),而此处说的是普通内部类,容易形成混淆。此处应注意,10.7.2小节讨论的是普通内部类的嵌套,而不是嵌套类。
10.8
使用内部类的一个主要缘由是每一个内部类都能独立的继承一个类,而不管外围类是否已经继承了某个类。这样内部类和接口一块儿构成了多重继承的解决方案。
还有其余一些特性:
(1)内部类能够有多个实例,每一个实例都有本身的状态信息,而且与其外围类对象的信息相互对立;
(2)在单个外围类中,可让多个内部类以不一样的方式实现同一个接口,或继承同一个类;
(3)书中有些迷惑。英语原话是:The point of creation of the inner-class object is not tied to the creation of the outer-class object. 个人理解是,内部类对象建立的时间点不与外部类对象的建立绑定在一块儿。这应该是相对于导出类与基类的关系来讲的(由于导出类的对象建立时基类的对象也将建立);
(4)内部类并无使人迷惑的“is-a”关系,它是一个独立的实体。
闭包是一个可调用的对象,它记录了一些信息,这些信息来自于建立它的做用域。按照这个定义,内部类是一种面向对象的闭包。
回调机制中的对象携带一些信息,这些信息容许它在稍后的某个时刻调用初始的对象。C++中经过函数指针能够实现回调,而Java经过内部类提供的闭包功能来实现回调。回调的价值在于它的灵活性——能够在运行时动态的决定须要调用什么方法。在实现GUI功能的时候,处处都用到了回调。
10.9
继承内部类时,语法比较特殊:
class WithInner { class Inner { } } public class InheritInner extends WithInner.Inner { //默认的构造方法不会编译经过 //构造函数必须有一个外部类的引用参数 public InheritInner(WithInner wi){ wi.super(); //个人理解:能够看作是显示的调用Inner的构造函数 } }
10.10
当继承了某个外围类的时候,内部类并无发生什么变化。这两个内部类是彻底独立的两个实体,各自在本身的命名空间内。
10.11
方法或做用域内的内部类(局部内部类)能够访问当前代码块内的常量以及外围类的全部变量。
既然局部内部类的名字在做用域外是不可见的,那为何不适用匿名内部类?有两个缘由:
(1)须要构造函数;
(2)须要多个该内部类的实例。
10.12
每一个类会产生一个.class文件,其中包含了如何建立该类型对象的所有信息(此信息产生一个meta-class,叫作Class对象)。内部类的命名规则:外围类的名字加上$,再加上内部类的名字。若是内部类是匿名的,编译器会简单的产生一个数字做为其标识符。