Outline
- 组合和继承
- 组合: 现有的类型作为新类型的底层实现的一部分
- 继承: 接口/基类,继承可以向上转型至基类
- 优先选择组合,确实必要的时候选择继承
- 程序/系统的开发是一个增量的过程
7.1 组合语法
对于非基本类型的对象,必须将其引用于新的类中,但可以直接定义基本类型数据
class WaterSource {
private String s;
WaterSource() {
System.out.println("WaterSource()");
s = "Constructed";
}
public String toString() { return s; }
}
初始化
- 在定义对象的地方, 使可以在构造器之前进行初始化
- 在类的构造器中
- 在正要使用这些对象之前(惰性初始化), 减少额外负担
- 使用实例初始化
7.2 继承语法
基本概念
public class Detergent extends Cleanser
- 可以为每一个类都创建一个main()来进行单元测试
- main可以调用其他类的main()方法, 并且可以传递String[]参数
- 为了继承一般会将所有数据成员指定为private, 所有方法指定为public
- super表示超类, super.func()可以调用基类的方法
- 可以重写基类的方法,也可以添加继承类独有的方法
初始化基类
-
当创建了一个子类的对象时,该对象包含了一个基类的子对象,这个字对象与用基类直接创建的对象是一样的.
-
对基类子对象的正确初始化只有一种方法, 在构造器中调用基类构造器进行初始化
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(); } }
-
带参数的构造器必须关键字super显式的调用基类构造器语句
class Game {Game(int i) {}} class BoardGame extends Game { BoardGame(int i) {super(i);}}
7.3 代理
- 将一个成员对象置于索要构造的类中, 但与此同时我们在新类中暴露了该成员对像所有的方法
- 代理可以拥有更多的控制力, 因为我们可以选择只提供成员对象中的方法的某个子集
public class SpaceShipControls {
void up(int velocity) {}
void down(int velocity) {}
}
public class SpaceShipDelegation {
private SpaceShipControls controls = new SpaceShipControls();
// Delegated methods:
public void up(int velocity) { controls.back(velocity);}
public void down(int velocity) { controls.down(velocity);}
...
}
7.4 结合使用组合和继承
- 虽然编译器强制初始化基类,并要求在构造器起始处进行,但是并不监督对成员对象的初始化
class Plate {Plate(int i) {}}
class DinnerPlate extends Plate {DinnerPlate(int i) {super(i);}}
class Utensil {Utensil(int i) {}}
class Spoon extends Utensil {Spoon(int i) {super(i);}}
...
public class PlaceSetting extends Custom {
private Spoon sp;
...
private DinnerPlate pl;
public PlaceSetting(int i) {
super(i + 1);
sp = new Spoon(i + 2);
...
pl = new DinnerPlate(i + 5);
}
}
正确清理
- 垃圾回收器在必要的时候释放内存,又是类可能在其生命周期内执行必要的清理活动
- 显式的编写一个特殊方法
- 将清理动作置于finally子句之中,以预防异常的出现
名称屏蔽
- 如果Java的基类拥有某个已被多次重载的方法名称,那么在子类中重新定义该方法名称并不会屏蔽其在基类中的任何版本
@Override
关键字可以用来表示覆写,当不留心重载而不是覆写的时候就会触发编译器错误- 重载: 名字一样,参数不一样,返回不一样
- 重写: 名字一样,参数一样,返回一样,内容不一样
区别 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
组合和继承的选择
- 组合和继承都允许在新的类中放置子对象, 组合是显式的, 继承是隐式的
- 组合技术通常用于想要在新类中使用现有类的功能而非它的接口, 需要一个该现有类的private对象
- 但是有时声明为public允许类的用户直接访问新类中的组合成份是极有意义的
- 继承:使用现有的类开发一个它的特殊版本, 将一个通用类进行特殊化
- 继承是面向对象的关键,但是不一定必须
protected关键字
- 就类用户而言, 这是private的, 但是对于继承该基类的子类来说是可访问的
- 最好保持private
向上转型
需要向上转型处理的必须使用继承而不是组合
class Instrument {
public void play() {}
static void tune(Instrument i) {i.play();}
}
public class Wind extends Instrument {
public static void main(String[] args) {
Wind flute = new Wind();
Instrument.tune(flute);
}
}
7.5 final关键字
7.5.1. final数据
- 一个永远不变的编译时常量
- 一个运行时被初始化的值, 你不希望它被改变
- 一个既是static又是final的域只会占据一段不能改变的存储空间
- 按照惯例会进行大写表示
private static final int VALUE_TWO = 99;
- 空白final可以声明为final但未给定初值, 有更大的灵活性, 可以做到根据对象不同而有所区别
public class BlankFinal {
private final int i = 0; // Initialized final
private final int j; // Blank final
private final Poppet p; // Blank final reference
// Blank finals MUST be initialized in the constructor:
public BlankFinal() {
j = 1; // Initialize blank final
p = new Poppet(1); // Initialize blank final reference
}
public BlankFinal(int x) {
j = x; // Initialize blank final
p = new Poppet(x); // Initialize blank final reference
}
}
7.5.2. final方法
-
设计原因:
- 锁定方法, 以防止任何继承类修改它的含义
- 效率, 编译器将对该方法的调用都转为内嵌调用//已废弃
-
final与private
- 所有private方法都是隐式的final
- 伪覆写: 实际上为定义一个新方法
class WithFinals { private final void f() { print("WithFinals.f()"); } private void g() { print("WithFinals.g()"); } } class OverridingPrivate extends WithFinals { private final void f() {print("OverridingPrivate.f()");} private void g() {print("OverridingPrivate.g()");} } class OverridingPrivate2 extends OverridingPrivate { public final void f() {print("OverridingPrivate2.f()");} public void g() {print("OverridingPrivate2.g()");} } public class FinalOverridingIllusion { public static void main(String[] args) { OverridingPrivate2 op2 = new OverridingPrivate2(); op2.f(); op2.g(); // You can upcast: OverridingPrivate op = op2; // But you can’t call the methods: // ! op.f(); ! op.g(); // Same here: WithFinals wf = op2; // ! wf.f(); ! wf.g(); } }
final类
- 将类定义为final, 表明该类不可被继承, 永远不需要改动
- final类中未明确声明为final的域可以进行修改
7.6. 初始化和类的加载
- 类的代码在初次使用时才会被加载, 创建类的第一个对象&访问static域&static方法的时候