代码复用是面向对象编程最具魅力的原因之一

  • 组合(Composition)
  • 继承(Inheritance)

组合

仅需要把对象的引用(object references)放置在一个新的类里,就完成了组合。
而编译器不会为每个引用创建一个默认对象,初始化引用有四种方法:

  • 对象被定义时 private int i = 1
  • 该类的构造函数中 Bath(){s1 = 'joy'}
  • 实际使用对象之前(延迟初始化if (s3==null) s3 = 'hello'
  • 使用实例初始化 Bath b = new bath

继承

语法: public class B extends A

初始化基类

子类包含基类中的接口信息,需要调用基类构造函数在构造函数中执行初始化。
初始化顺序由祖先—>子孙
带参数的构造函数:
若基类为带参构造函数,则必须使用 super 关键字和参数列表显式调用。

  1. class Game {
  2. Game(int i) {
  3. System.out.println("Game constructor");
  4. }
  5. }
  6. class BoardGame extends Game {
  7. BoardGame(int i) {
  8. super(i);
  9. System.out.println("BoardGame constructor");
  10. }
  11. }

委托

委托是一种重要的编程方式。与继承相对,是可复用编程的重要方法。
委托指的是在A类中以各种方式利用B类,完成类的功能。
委托的类型:

  1. A use B

    B类对象在A类中出现,但是是以局部变量或是方法参数的形式出现的。A类中并没有B类的对象作为域。
    一般称这种delegation为临时性的delegation。

  2. A has B

    B类对象在A类中出现,B类的对象是A类的域之一。B类对象通过A类对象的constructor方法或其它方法从外部输入A类对象。
    A has B有两种情况。1)Association。A类对象和B类对象之间并没有从属关系。
    2)Aggregation。A类对象由B类聚合而成,但是B类可以脱离A类单独存在。
    一般称这种delegation为永久性的delegation。

  3. A is part of B

    B类对象在A类中出现,B类的对象是A类的域之一。B类对象在A类对象内创建。
    B类对象不能脱离A类对象独立存在。
    一般称这种delegation为永久性的delegation

保证适当的清理:
由于java垃圾自收集机制,无法主动知道垃圾何时被清理。如果要清理,则需编写一个显示的方法调用。

  1. //[1]使用try finally 在finally中显示调用
  2. public static void main(String[] args) {
  3. CADSystem x = new CADSystem(47);
  4. try {
  5. // Code and exception handling...
  6. } finally {
  7. x.dispose();
  8. }
  9. }
  10. //[2]Override子类的dispose函数,并super显示调用父类dispose;最好按照创建的相反顺序进行
  11. class Circle extends Shape {
  12. Circle(int i) {
  13. super(i);
  14. System.out.println("Drawing Circle");
  15. }
  16. @Override
  17. void dispose() {
  18. System.out.println("Erasing Circle");
  19. super.dispose();
  20. }
  21. }

名称隐藏: 即使Java基类中的function被多次重载,但在派生类中重新定义该func仍不会隐藏任何基类版本
因此,使用 @Override 可以防止意外重载
**

组合与继承的选择

当想在新类中包含一个已有类的功能时,使用composition
当想使用一个现有类,并开发出它的新版本时,使用inheritation
考虑使用继承时,首先想”是否需要向上转型”

向上转型

image.png
派生类转型为基类都是向上的。派生类是基类的一个超集。它可能比基类包含更多的方法,但它必须至少具有与基类一样的方法。在向上转型期间,类接口只可能失去方法,不会增加方法。
否需要向上转型”

final理解:
final 只说明是一个常量, static 强调只有一个,而 final static 即表示编译时常量(命名全部大写)
并非被 final 修饰就认为可在编译时知道其值。
final 修饰 参数 时,意味着在该func不能改变所修饰的对象或常量。

  1. void with(final Gizmo g) {
  2. //-g = new Gizmo(); // Illegal -- g is final
  3. }

final 修饰 方法 时,防止子类通过覆写改变方法的行为。
类中 private 方法都被隐式地指定为 final ,不能被覆写。我们可以理解为:覆写必须能将一个对象upcasting为基类并调用相同的方法,如果有一个方法为private,它表示基类接口的一部分,它只是隐藏在类内部的代码,恰好有相同命名。因此即使在子类中有和父类同名func,其并非是被覆写。

  1. class WithFinals {
  2. // Identical to "private" alone:
  3. private final void f() {
  4. System.out.println("WithFinals.f()");
  5. }
  6. // Also automatically "final":
  7. private void g() {
  8. System.out.println("WithFinals.g()");
  9. }
  10. }
  11. class OverridingPrivate extends WithFinals {
  12. private final void f() {
  13. System.out.println("OverridingPrivate.f()");
  14. }
  15. private void g() {
  16. System.out.println("OverridingPrivate.g()");
  17. }
  18. }
  19. public class FinalOverridingIllusion {
  20. public static void main(String[] args) {
  21. OverridingPrivate op = new OverridingPrivate();
  22. //[1]此时可利用op调用f()和g(),调用的是OverridingPrivate.f()
  23. WithFinals wf = op;
  24. //[2]若此时想调用,则调用失败,因为无法覆写,所以无法upcasting

final 修饰类时,表示该类不能被继承。