第六章:代码复用

第六章:代码复用
合成与继承
继承:
super关键字的使用:super使用在派生类中,如果派生类中重写了基类的方法,但在这个被重写的方法中仍然要调用基类的同名的方法,这就要用到super关键字,特别是在创建对象时,在带参数构造函数中调用基类构造函数的情况。
如:
class Cleanser {
  private String s = new String("Cleanser");
  public void append(String a) { s += a; }
  public void dilute() { append(" dilute()"); }
  public void apply() { append(" apply()"); }
  public void scrub() { append(" scrub()"); }
  public void print() { System.out.println(s); }
  public static void main(String[] args) {
    Cleanser x = new Cleanser();
    x.dilute(); x.apply(); x.scrub();
    x.print();
  }
}
public class Detergent extends Cleanser {
  // Change a method:
  public void scrub() {
    append(" Detergent.scrub()");
    super.scrub(); // Call base-class version
  }
// Add methods to the interface:
  public void foam() { append(" foam()"); }
  // Test the new class:
  public static void main(String[] args) {
    Detergent x = new Detergent();
    x.dilute();
    x.apply();
    x.scrub();
    x.foam();
    x.print();
    System.out.println("Testing base class:");
    Cleanser.main(args);
  }
} ///:~
可以看到基类Cleanser 中定义了scrub方法,但派生类Detergent 中对scrub方法进行了修改,并用在派生类Detergent 的scrub方法中,要调用基本的scrub方法,那么用super.scrub(); 

 基类的初始化:
         当你创建一个派生类的对象的时候,这个对象里面还有一个基类的子对象,这个子对象同基类自己创建的对象没什么两样,只是从外面看来,这个子对象被包裹在派生类的对象里面。
         基类子对象的正确初始化是非常重要的,而且只有一个办法能保证这一点:调用基类的构造函数来进行初始化,因为只有它才能掌握怎么样才能正确地进行初始化的信息和权限。java会让派生类的构造函数自动地调用基类的构造函数。
          示例:
 class Art {
  Art() {
    System.out.println("Art constructor");
  }
}

class Drawing extends Art {
  Drawing() {
    System.out.println("Drawing constructor");
  }
}

public class Cartoon extends Drawing {
  Cartoon() {
    System.out.println("Cartoon constructor");
  }
  public static void main(String[] args) {
    Cartoon x = new Cartoon();
  }
} ///:~
输出结果为:
Art constructor
Drawing constructor
Cartoon constructor
一看结果便一目了然了。

上面的示例是不带任何参数的情况, 如果构造函数中带有参数的话,那这里又要用到super的特性了。与上面super的使用涵意一样,super在这里用作:派生的带参数构造函数中调用基类的带参构造函数,只是这里不象上面那样super.scrub();这里只使用super(i);即可。
        
class Game {
  Game(int i) {
    System.out.println("Game constructor");
  }
}

class BoardGame extends Game {
  BoardGame(int i) {
    super(i);
    System.out.println("BoardGame constructor");
  }
}

public class Chess extends BoardGame {
  Chess() {
    super(11);
    System.out.println("Chess constructor");
  }
  public static void main(String[] args) {
    Chess x = new Chess();
  }
} ///:~
输出结果是:
Game constructor
BoardGame constructor
Chess constructor

合成和继承一起使用,实现类的复用:

class Plate {
  Plate(int i) {
    System.out.println("Plate constructor");
  }
}

class DinnerPlate extends Plate {
  DinnerPlate(int i) {
    super(i);
    System.out.println(
      "DinnerPlate constructor");
  }
}

class Utensil {
  Utensil(int i) {
    System.out.println("Utensil constructor");
  }
}

class Spoon extends Utensil {
  Spoon(int i) {
    super(i);
    System.out.println("Spoon constructor");
  }
}

class Fork extends Utensil {
  Fork(int i) {
    super(i);
    System.out.println("Fork constructor");
  }
}

class Knife extends Utensil {
  Knife(int i) {
    super(i);
    System.out.println("Knife constructor");
  }
}

// A cultural way of doing something:
class Custom {
  Custom(int i) {
    System.out.println("Custom constructor");
  }
}

public class PlaceSetting extends Custom {
  Spoon sp;
  Fork frk;
  Knife kn;
  DinnerPlate pl;
  PlaceSetting(int i) {//把初始化工作都放在构造函数中
    super(i + 1);
    sp = new Spoon(i + 2);
    frk = new Fork(i + 3);
    kn = new Knife(i + 4);
    pl = new DinnerPlate(i + 5);
    System.out.println(
      "PlaceSetting constructor");
  }
  public static void main(String[] args) {
    PlaceSetting x = new PlaceSetting(9);
  }
} ///:~
        尽管编译器会强迫我们对基础类进行初始化,并要求我们在构建器最开头做这一工作,但它并不会监视我们是否正确初始化了成员对象。所以对此必须特别加以留意。
FINAL关键字:
        FINAL关键字指“那样东西是不允许改动”,你可能会出于两点考虑不想让别人作改动:设计和效率。由于这两个原因差别很大,所以很可能会误用final关键字。
final的三种用途:数据(Data)、方法(method)、类(class)。
 很多语言通知编译器:“这段常量(constant)数据”的手段。常量能用下列两种情况出现:
        1、可以是“编译时的常量”,这样以后就不能改了;
        2、也可以是运行时初始化的值,这个值以后就不想再改了。
        如果是编译时的常量,编译器会把常量放到算式里面;这样编译的时候就能进行计算,因此也就降低了运行时的开销。在Java 中这种常量必须是primitive 型的,而且要用final 关键词表示。这种常量的赋值必须在定义的时候进行。
        一个既是static 又是final 的数据成员会只占据一段内存,并且不可修改。
        当final 不是指primitive,而是用于对象的reference 的时候,意思就有点不一样了。对primitive 来说,final 会将这个值定义成常量,但是对于对象的reference 而言,final 的意思则是这个reference 是常量。初始化的时候,一旦将reference 连到了某个对象,那么它就再也不能指别的对象了。但是这个对象本身是可以修改的;Java 没有提供将某个对象作成常量的方法。
        (但是你可以自己写一个类,这样就能把类当做常量了)
        这种局限性也体现在数组上,因为它也是一个对象。
注意,通常约定,被初始化为常量值的final static 的primitive 的名字全都用大写,词与词之间用下
划线分开,如VAL_ONE
Final 方法
使用final 方法的目的有二:
        第一,为方法上“锁”,禁止派生类进行修改。这是出于设计考虑。当你希望某个方法的功能,能在继承过程中被保留下来,并且不被覆写,就可以使用这个方法。
        第二个原因就是效率。如果方法是final 的,那么编译器就会把调用转换成“内联的(inline)”。它会用方法本身的拷贝来代替方法的调用
final 和private
        private 方法都隐含有final 的意思。由于你不能访问private 的方法,因此你也不能覆写它。你可以给private 方法加一个final 修饰符,但是这样做什么意义也没有。
        这个问题有可能会造成混乱,因为即使你覆写了一个private 方法(它隐含有final 的意思),看上去它还是可以运行的,而且编译器也不会报错:
        class WithFinals {
            // Identical to "private" alone:
            private final void f() {
                    System.out.println("WithFinals.f()");
                                          }
            / / Also automatically "final":
           private void g() {
                    System.out.println("WithFinals.g()");
                                 }
        }
        class OverridingPrivate extends WithFinals {
                private final void f() {
                        System.out.println("OverridingPrivate.f()");
                                                  }
                private void g() {
                        System.out.println("OverridingPrivate.g()");
                                                  }
         }
只有是基类接口里的东西才能被“覆写”,如果方法是private 的,那它就不属于基类的接口。它只能算是被类隐藏起来的,正好有着相同的名字的代码。如果你在派生类里创建了同名的public 或protected,或package 权限的方法,那么它们同基类中可能同名的方法,没有任何联系。你并没有覆写那个方法,你只是创建了一个新的方法。由于private 方法是无法访问的,实际上是看不见的,因此这么作除了会影响类的代码结构,其它什么意义都没有。
Final 类
把整个类都定义成final 的(把final 关键词放到类的定义部分的前面)就等于在宣布,你不会去继承这个类,你也不允许别人去继承这个类。换言之,出于类的设计考虑,它再也不需要作修改了,或者从安全角度出发,你不希望它再生出子类。
final class Dinosaur{}
注意,final 类的数据可以是final 的,也可以不是final 的,这要由你来决定。无论类是不是final 的,这一条都适用于“将final 用于数据的”场合。但是,由于final 类禁止了继承,覆写方法已经不可能了,因
此所有的方法都隐含地变成final 了。你可以为final 类的方法加一个final 修饰符,但是这一样没什么意义。


你可能感兴趣的:(第六章:代码复用)