Thinking in Java 第七章------复用类(1)

来源:互联网 发布:iphone网络共享在哪里 编辑:程序博客网 时间:2024/05/18 13:28

Thinking in Java 第七章——复用类(1)
Thinking in Java 第七章——复用类(2)
Thinking in Java 第七章——复用类(3)

前言:
复用代码是Java众多引人注目的功能之一。但是想要成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,他还需要能够做更多的事情。
有两种达到这一目的的方法:
1. 在新的类中产生现有类的对象。由于新的类是由现有类的对象所组成,多以这种方法称为组合
2. 按照现有类的类型来创建新类。无需改变现有类的形式,采用现有类的工作形式并在其中添加新代码。这种神奇的方式称之为继承


一、组合语法

使用组合技术,只需要将对象引用置于新类中即可。
例如:
假设你需要某个对象,它具有多个String对象,几个基本数据类型,以及另一个类的对象。对于非基本类型对象,必须将其引用置于新的类中,但是可以直接定义基本类型数据。

class WaterSource {  private String s;  WaterSource() {    System.out.println("WaterSource()");    s = "Constructed";  }  public String toString() { return s; }}   public class SprinklerSystem {  private String valve1, valve2, valve3, valve4;  private WaterSource source = new WaterSource();  private int i;  private float f;  public String toString() {    return      "valve1 = " + valve1 + " " +      "valve2 = " + valve2 + " " +      "valve3 = " + valve3 + " " +      "valve4 = " + valve4 + "\n" +      "i = " + i + " " + "f = " + f + " " +      "source = " + source;  }   public static void main(String[] args) {    SprinklerSystem sprinklers = new SprinklerSystem();    System.out.println(sprinklers);  }} /* Output:WaterSource()valve1 = null valve2 = null valve3 = null valve4 = nulli = 0 f = 0.0 source = Constructed*///:~

在上述定义的两个类中,有一个方法很特殊:toString(),每一个非基本类型的对象都有一个该方法,并且当编译器需要一个String而你却只有一个对象时,该方法就会被调用!
例如在上述代码中System.out.println(sprinklers);,参数需要的是一个String类型的对象,但是我们传入的是SprinklerSystem类型的,其实在底层是调用了SprinklerSystem。toString()

我们知道的是,类中域为基本类型时,编译器可以自动将其初始化为零;对象引用会被初始化为null,但是编译器并不是为每一个引用都创建默认的对象,如果想初始化这些引用,可以在代码中的如下位置进行:

  1. 在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化。
  2. 在类的构造器中。
  3. 就在正要使用这些对象之前,这种方式被称为惰性初始化。在生成对象不值得及不必每次都生成对象的情况下,这种方式可以减少额外的负担。
  4. 使用实例初始化。

如下所示:

import static net.mindview.util.Print.*;class Soap {  private String s;  Soap() {    print("Soap()");    s = "Constructed"; //2 类的构造器中  }  public String toString() { return s; }}   public class Bath {  private String //1 定义初始化    s1 = "Happy",    s2 = "Happy",    s3, s4;  private Soap castille;  private int i;  private float toy;  public Bath() {    print("Inside Bath()");    s3 = "Joy";    toy = 3.14f;    castille = new Soap();  }   //4 使用实例初始化:  { i = 47; }  public String toString() {    if(s4 == null) //5 惰性初始化:      s4 = "Joy";    return      "s1 = " + s1 + "\n" +      "s2 = " + s2 + "\n" +      "s3 = " + s3 + "\n" +      "s4 = " + s4 + "\n" +      "i = " + i + "\n" +      "toy = " + toy + "\n" +      "castille = " + castille;  }   public static void main(String[] args) {    Bath b = new Bath();    print(b);  }} /* Output:Inside Bath()Soap()s1 = Happys2 = Happys3 = Joys4 = Joyi = 47toy = 3.14castille = Constructed*///:~

二、继承语法

当创建一个类时,总是在继承,因此,除非已经明确的指出新类继承于其他的某个类,否则就是在隐式的从Java的标准根类Object进行继承。

import static net.mindview.util.Print.*;class Cleanser {  private String s = "Cleanser";  public void append(String a) { s += a; }  public void dilute() { append(" dilute()"); }  public void apply() { append(" apply()"); }  public void scrub() { append(" scrub()"); }  public String toString() { return s; }  public static void main(String[] args) {    Cleanser x = new Cleanser();    x.dilute(); x.apply(); x.scrub();    print(x);  }}   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();    print(x);    print("Testing base class:");    Cleanser.main(args);  } } /* Output:Cleanser dilute() apply() Detergent.scrub() scrub() foam()Testing base class:Cleanser dilute() apply() scrub()*///:~

上述程序也示范了Java的许多特性。

  1. Cleanserappend()方法中,我们使用“+=”操作符将几个String类型的对象连接成s,此操作符是被Java设计者重载用于处理String对象的操作符之一。(另外一个是“+”)。
  2. CleanserDetergent均含有main()方法。可以为每个类都创建一个main()方法。这种在每一个类中都创建一个main()方法的技术可以试每个类的单元测试都变得简便易行。而且在完成单元测试之后也不需要删除,可以将其留待下次测试。
  3. 既是一个程序中含有多个类,也只有命令行所调用的那个类的main()方法会被调用。
  4. 为了继承,一般的规则是将所有的数据成员都制定陈private,将所有的方法都指定为public。当然在特殊情况下必须做出调整。
  5. Java用super关键字表示超类的意思,当前类就是从超类继承而来的。
  6. 在继承的过程中,并不一定需要使用基类的方法,也可以在导出类中添加新的方法,例如Detergent类中的foam()方法

2.1 初始化基类

现在涉及到基类和导出类这两个概念,从外部看,导出类是一个与基类具有相同接口的新类,或许还会有一些额外的方法和域。但是继承并不只是复制基类的接口。当创建了一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建的对象时一样的。二者的区别在于:导出类来自于外部,而基类的子对象被包装在导出类对象内部。
基类对象的初始化也是至关重要的,而且也仅有一种方法来保证这一点:在构造器中调用基类构造器来初始化,而基类构造器具有执行基类初始化所需要的所有知识和能力。Java会自动在导出类的构造器中插入对基类构造器的调用。 如下:

import static net.mindview.util.Print.*;class Art {  Art() { print("Art constructor"); }}class Drawing extends Art {  Drawing() { print("Drawing constructor"); }}public class Cartoon extends Drawing {  public Cartoon() { print("Cartoon constructor"); }  public static void main(String[] args) {    Cartoon x = new Cartoon();  }} /* Output:Art constructorDrawing constructorCartoon constructor*///:~

构建过程是从基类向外扩散的,所以基类在导出类构造器可以访问它之前,就已经初始化完成了。即使不为Cartoon()创建构造器,编译器也会为你合成一个默认的构造器,该构造器将调用基类构造器。
但是:在基类构造器有参数时,必须使用super关键字显示的编写调用基类构造器的语句,并且配以合适的参数列表。
例如:

import static net.mindview.util.Print.*;class Game {  Game(int i) {    print("Game constructor");  }}class BoardGame extends Game {  BoardGame(int i) {    super(i); //显示调用!!!!!!    print("BoardGame constructor");  }}   public class Chess extends BoardGame {  Chess() {    super(11);//显示调用!!!!    print("Chess constructor");  }  public static void main(String[] args) {    Chess x = new Chess();  }} /* Output:Game constructorBoardGame constructorChess constructor*///:~

Thinking in Java 第七章—复用类(2)

0 0
原创粉丝点击