第八章:多态
基类 base = new 子类 ==> base引用指向子类对象,时基类类型
class Instrument {
public void play(Note n) {
print("Instrument.play()");
}
}
public class Wind extends Instrument {
// Redefine interface method:
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
}
public class Music {
public static void tune(Instrument i) {
// ...
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wind flute = new Wind();
tune(flute); // Upcasting 向上转型
}
} /* Output:
Wind.play() MIDDLE_C
*///:~
一、转机
如上例子,多态情况下编译器如何知道调用哪个方法?
Instrument引用调用,通过后期绑定实现,利用方法调用机制找到正确的方法。
1、方法绑定
将一个方法调用同方法主体关联起来叫做绑定
前期绑定:①程序执行前绑定(编译器和连接器实现) ②private(类私有)、static(随类加载)、final(关闭动态绑定)修饰的方法属于前期绑定,其他属于后期绑定,它自动发生。
后期绑定(动态绑定):①根据某种方法调用机制,判断对象的类型执行相应的方法;编译器不知道对象的类型,方法调用机制找到正确的方法
class A {
protected void f() {
//调用第二个方法
System.out.println("A.f()");
this.g();
}
protected void g() {
System.out.println("A.g()");
}
}
class B extends A {
@Override protected void g() {
System.out.println("B.g()"); //子类覆盖父类方法
}
}
public class Ex10 {
public static void main(String[] args) {
B b = new B();
//自动升级为基类以调用基类方法A.f()
//通过多态性,它将调用派生类方法B.g():
b.f();
}
}
结果:
A.f()
B.g()
2、扩展性
3、缺陷,覆盖私有方法:private
基类私有方法private修饰(可认为final修饰,不可覆盖,也有隐士的final意识),对于导出类是屏蔽的。所以子类的public同名方法是新方法并不是覆盖
public class PrivateOverride {
private void f() {
print("private f()"); }
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
}
}
class Derived extends PrivateOverride {
public void f() {
print("public f()"); }
} /* Output:
private f()
*///:~
4、缺陷:域或静态方法
静态是随着类的加载,与类相关联,不具有多态性;而多态是动态绑定
class Super {
public int field = 0;//相同名字的成员变量,在各自对象的域中
public int getField() {
return field; }
public static String dynamicGet() {
return "Super dynamicGet()";
}
}
class Sub extends Super {
public int field = 1; //相同名字的成员变量,在各自对象的域中
public int getField() {
return field; }
public int getSuperField() {
return super.field; }
public static String dynamicGet() {
return "Sub dynamicGet()";
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub(); // Upcast 向上转型
System.out.println(sup.dynamicGet()); // 静态方法,调用基类中
System.out.println("sup.field = " + sup.field +", sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println(sub.dynamicGet()); // 静态方法,调用本类
System.out.println("sub.field = " +sub.field + ", sub.getField() = " +
sub.getField() +", sub.getSuperField() = " +sub.getSuperField());
}
} /* Output:
Super dynamicGet()
sup.field = 0, sup.getField() = 1
Sub dynamicGet()
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~
二、构造器与多态
1、构造器的调用顺序
1、导出类按照继承体系树向上链接,找到基类,然后逆序初始化构造直到导出类构造,这样也是为了保证导出类调用基类的数据完整性;
2、导出类构造中有句默认的super(); ,如果基类中没有默认的构造将会报错
3、构造其实是一个static方法,只不过是隐式的
class Meal {
private static String s = "Meal_test成员变量";
Meal() {
print("Meal()构造方法"+s); }
static{
System.out.println("Meal()_静态代码块"+s);
}
{
System.out.println("Meal()_构造代码块"+s);
}
}
class Bread {
Bread() {
print("Bread()"); }
}
class Cheese {
Cheese() {
print("Cheese()"); }
}
class Lettuce {
Lettuce() {
print("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() {
print("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch() {
print("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() {
print("Sandwich()"); }
public static void main(String[] args) {
new Sandwich();
}
} /* Output:
Meal()_静态代码块Meal_test成员变量
Meal()_构造代码块Meal_test成员变量
Meal()构造方法Meal_test成员变量
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*///:~
总结:执行顺序是先静态域、静态代码块、基类成员变量初始化、构造代码块、基类构造初始化、子类成员变量初始化、构造代码块、子类构造初始化
2、继承与清理
引用计数 清理垃圾
class Shared {
private int refcount = 0; // refcount=5,由于同一对象成员变量调用自增方法
private static long counter = 0;//追中Shared实例数量
private final long id = counter++; //counter=0,使用final修饰,不希望生命周期内改变
public Shared() {
print("Creating " + this);
}
public void addRef() {
refcount++; }//追踪引用数,清理时方便知道没有引用
protected void dispose() {
if(--refcount == 0)
print("Disposing " + this);
}
public String toString() {
return "Shared " + id; }
}
class Composing {
private Shared shared;
private static long counter = 0;
private final long id = counter++;
public Composing(Shared shared) {
print("Creating " + this);
this.shared = shared;
this.shared.addRef();
}
protected void dispose() {
print("disposing " + this);
shared.dispose();
}
public String toString() {
return "Composing " + id; }
}
public class ReferenceCounting {
public static void main(String[] args) {
Shared shared = new Shared(); // refcount=5,由于同一对象成员变量调用自增方法
Composing[] composing = {
new Composing(shared),
new Composing(shared), new Composing(shared),
new Composing(shared), new Composing(shared) };
for(Composing c : composing) //按照数组顺序输出,所以id是从0开始
c.dispose();
}
} /* Output:
Creating Shared 0
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
disposing Composing 0
disposing Composing 1
disposing Composing 2
disposing Composing 3
disposing Composing 4
Disposing Shared 0
*///:~
3、构造器内部的多态行为
基类构造器内部调用一个动态绑定的方法(被覆盖),导致导出类未初始化,出现意外
class Glyph {
void draw() {
print("Glyph.draw()"); }
Glyph() {
print("Glyph() before draw()");
draw();
print("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
print("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
print("RoundGlyph.draw(), radius = " + radius);//被覆盖方法
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
} /* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~
三、关于继承和组合设计
1、如果向上转型使用继承,不然使用组合
2、由于继承的多态性,需要动态确定类型是基类还是导出类,方法调用机制才好正确调用方法。(动态绑定)
1、类型转换
编译时父类有该方法,才能调用子类覆盖的方法,不然编译时异常
类型转换必须确定类型(instanceof),不然出现类型转换异常(ClassCastException)
class Useful {
public void f() {
}
public void g() {
}
}
class MoreUseful extends Useful {
public void f() {
}
public void g() {
}
public void u() {
}
public void v() {
}
public void w() {
}
}
public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
// Compile time: method not found in Useful: 编译时错误
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI 没有确定类型就强制转换,类型转换异常
((MoreUseful)x[0]).u(); // Exception thrown
}
} ///:~