《Java编程思想》 第十章 内部类 记录

  • 可以将一个类的定义放在另一个类的内部,这就是内部类。
  • 内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性。然而必须要了解,内部类与组合是完全不同的概念。

2. 链接到外部类

  • 当生成一个内部类的对象是,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围对象的所有元素的访问权。
  • 当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后,在你访问此外围类的成员时,就是用那个引用来选择外围类的成员。幸运的是,编译器会帮你处理所有的细节,但你现在可以看到:内部类的对象只能在与其外围类的对象相关联的情况下才能被创建(在内部类是非static类时)。构建内部类对象时,需要一个指向其外围类对象的引用,如果编译器访问不到这个引用就会报错。不过绝大多数时候这无需担心。

3. 使用.this与.new

  • 需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this。
  • 要创建其某个内部类的对象,需要.new语法,且必须使用外部类的对象来创建该内部类对象。
  • 在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建他的外部类对象上。但是,如果你创建的是嵌套类(静态内部类),那么他就不需要对外部类对象的引用。

4. 内部类与向上转型

  • 当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。(从实现了某个接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的。)这是因为此内部类,某个接口的实现,能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。

5. 在方法和作用域内的内部类

  • 在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类,被称作局部内部类。

6. 匿名内部类

  • 在匿名内部类末尾的分号,并不是用来标记此内部类结束的。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类罢了。因此,这与别的地方使用的分号是一致的。
  • 定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
  • 在匿名类中不可能由命名构造器(因为它根本没有名字!),但通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。通过{}实现。
  • 对于匿名类而言,实例初始化的实际效果就是构造器。当然它受到了限制,你不能重载实例初始化方法,所以你仅有一个构造器。
  • 匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。

7. 嵌套类

  • 如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static。这通常称为嵌套类。想要理解static应用于内部类时的含义,就必须记住,普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。然后,当内部类是static时,就不能这样了。
  • 嵌套类意味着:
  1. 要创建嵌套类的对象,就不需要其外围类对象
  2. 不能从嵌套类的对象中访问非静态的外围类对象
  • 嵌套类和普通的内部类还有一个区别。普通内部类的字段与方法,只能放在类的外部层次上,所以普通内部类就不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西。
  • 在一个普通的(非static)内部类中,通过一个特殊的this引用可以链接到其外围类对象。嵌套类就没有这个特殊的this引用,这使得它类似于一个static方法。
  • 如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部类的嵌套类会显得很方便。
  • 一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。

8. 为什么需要内部类

  • 一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。
  • 内部类必须要回答的一个问题是:如果只是需要一个对接口的引用,为什么不通过外围类实现那个接口呢?答案是:“如果这能满足需求,那么就应该这样做。”那么内部类实现一个接口与外围类实现这个接口有什么区别呢?答案是:后者不能总是享有到接口带来的方便,有时需要用到接口的实现。所以使用内部类最吸引人的原因是:
  • 每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
  • 如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。也就是说,内部类允许继承多个非接口类型(类或抽象类)。
  • 由于接口的灵活性,要么使用单一类,要么使用内部类。
  • 这是假设在两种方式下的代码结构都确实有逻辑意义。然而遇到问题的时候,通常问题本身就能给出某些指引,告诉你是应该使用单一类,还是使用内部类。如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
  • 如果不需要解决“多重继承”的问题,那么自然可以用别的方式编码,而不需要使用内部类。但如果使用内部类,还可以获得其他一些特性:
  1. 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立
  2. 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类
  3. 创建内部类对象的时刻并不依赖于外围类对象的创建
  4. 内部类并没有令人疑惑的“is-a”关系,他就是一个独立的实体
  • 闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。
  • Java最引人争议的问题之一就是,人们认为Java应该包含某个类似指针的机制,以允许回调。通过回调,对象能够携带一些信息,这些信息允许他在稍后的某个时刻调用初始的对象。
  • 回调的价值在于它的灵活性,可以在运行时动态地决定需要调用什么方法。
  • “使变化的事物与不变的事物相互分离”,“变化向量”就是各种不同的某个对象所具有的不同行为,而你通过创建不同的该类的子类来表现不同的行为。

9. 内部类的继承

  • 因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,事情会变得有点复杂。问题在于,那个指向外围类对象的“秘密的”引用必须被初始化,而在导出类中不再存在可连接的默认对象。

10. 内部类可以被覆盖吗

  • 当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化,这两个内部类是完全独立的两个实体,各自在自己的命名空间内。当然,明确地继承某个内部类也是可以的。

11. 局部内部类

  • 可以在代码块里创建内部类,典型的方式是在一个方法体的里面创建。局部内部类不能有访问说明符,因为它不是外围类的一部分;但是它可以访问当前代码块内的常量,以及此外围类的所有成员。
  • 既然局部内部类的名字的方法外是不可见的,那为什么我们仍然使用局部内部类而不是匿名内部类?唯一的理由是,我们需要一个已命名的构造器,或者需要重载构造器,而匿名内部了只能用于实例初始化。另一个理由是,需要不止一个该内部类的对象。
发布了57 篇原创文章 · 获赞 11 · 访问量 9885

猜你喜欢

转载自blog.csdn.net/qq_36160730/article/details/96727589