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

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 = null
i = 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 = Happy
s2 = Happy
s3 = Joy
s4 = Joy
i = 47
toy = 3.14
castille = 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 constructor
Drawing constructor
Cartoon 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 constructor
BoardGame constructor
Chess constructor
*///:~

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值