OOP 继承,抽象,多态
多态的作用是消除类型之间的耦合
8.1 向上转型
把某个对象的引用视为其基类的引用的做法称为向上转型
8.2 转机
- 绑定:将一个方法调用同一个方法主体关联起来被称作绑定
静态绑定:在程序执行前进行绑定,是面向过程的默认绑定方式
动态绑定:直到运行时通过某种机制确定对象的类型,才与方法绑定 - 有了动态绑定,只编写基类的代码,所有的导出类都可以正确运行
Shape s = new Circle();
//创建一个 Circle 对象,并把得到的引用立即复制给 Shape
s.draw();
public Music {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
}
public static void tuneAll(Instrument[] e) {
for(Instrument i : e)
tune(i);
}
public static void main(String[] args) {
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
- 多态不能覆盖 private 方法,因为 private 方法不能被继承,导出类的同名方法将被视为新的方法
class Base {
private f() {}
}
class Derived extends Base {
public f() {} //这是一个新的方法
}
- 基类成员的域和静态方法不能被继承,也不能使用多态
class Super {
public int field = 0;
public int getField() { return field; }
}
class Sub extends Super {
public int field = 1;
public int getField() { return field; }
public int getSuperField() { return super.field; }
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField()); //基类的域不能被继承,不能实现多态
Sub sub = new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField() = " + sub.getField() + ", sub.getSuperField() = " + sub.getSuperField());
}
}
//sub 和 sup 各有一个field域,是不同的存储空间
//sup.field = 0, sup.getField() = 1
//sub.field = 1, sub.getField() = 1,sub.getSuperField() = 0
8.2 构造器与多态
- 基类的构造器总是在导出类的构造过程中被调用
1)调用基类构造器,这个步骤会反复递归下去
2)按声明顺序调用成员的初始化方法
3)调用导出类构造器的主体
class B {
B() {
System.out.println("B constructor");
}
}
class D {
D() {
System.out.println("D constructor");
}
}
class Base {
public static int i = print(1);
public static int print(int i) {
System.out.println("Base static i = " + i);
return i;
}
public int j = 2;
public B b = new B();
Base() {
System.out.println("Base j = " + j);
System.out.println("Base constructor");
}
}
class Derived extends Base {
public static int i = print(3);
public static int print(int i) {
System.out.println("Derived static i = " + i);
return i;
}
public int j = 4;
public D d = new D();
Derived() {
System.out.println("Derived j = " + j);
System.out.println("Derived constructor");
}
public static void main(String[] args) {
Derived derrived = new Derived();
}
}
/*
Base static i = 1 //基类静态成员
Derived static i = 3 //导出类静态成员
B constructor //基类非静态成员
Base j = 2
Base constructor //基类构造器
D constructor //导出类非静态成员
Derived j = 4
Derived constructor //导出类构造器
*/
- 继承与清理
必须清理时,要手动添加 dispose() 方法,在导出类中也要手动调用基类的 dispose() 方法
protected void dispose() {
super.dispose();
}
- 若一个对象要依赖于其他对象,销毁顺序应该和初始化顺序相反,对于基类,应先对其导出类进行清理,再对基类进行清理
- 尽量不在基类构造器中调用可被覆盖的动态方法,在基类中唯一能够安全调用的方法是那些 final 的或者 private 的方法
class P {
public static void print(String s) { System.out.println(s); }
}
class Glyph {
void draw() { P.print("Glyph.draw()"); }
Glyph() {
P.print("Glyph() before draw()");
draw(); //Glyph的构造器调用 draw 时,实际上调用的是导出类的 draw, radius 还没有初始化, 所以是0
P.print("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
P.print("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
P.print("RoundGlyph.draw(), radius = " + radius);
}
}
public class Poly {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
/*
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*/
8.4 协变返回类型
- 导出类中被覆盖的方法可以返回基类方法的返回类型的某种导出类型
Shape process() { return new Shape(); }
Circle process() { return new Circle(); }
//返回导出类型对象也被视为合法
8.5 用继承进行设计
- 使用继承必须在编译时知道确切类型,使用组合可以动态选择类型
class Stage {
private Actor actor = new HappyActor();
public void change() { actor = new SadActor(); }
public void perform() { actor.act(); }
public static void main(String[] args) {
Stage stage = new Stage();
stage.perform(); //HappyActor
stage.change();
stage.perform(); //SadActor
}
- 用继承表达行为(方法)间的差异,用字段表达状态上的变化
- 只有纯粹的替代,即is-a关系,导出类可以完全替代基类,只有在基类中已经建立的方法才可以被覆盖
- 如果是is-like-a关系,导出类有基类中没有的新方法,一旦向上转型,就会丢失这些新方法
- 向下转型,在Java中,所有类型转化都会得到RTTI(运行时类型识别)的检查
class Useful {
public void f() {}
public void g() {}
}
class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
}
public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].f();
((MoreUseful)x[1]).u(); //向下转型 RTTI
((MoreUseful)x[0]).u(); //异常,本身是基类对象,不能向下转型
}
}