8.1 再论向上转型
在前面我们已经知道,对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。而这种把某个对象的引用视为对其基类型的引用的做法,称为向上转型。因为在继承树的画法中,基类是放置在上方的。
enum Note{
MIDDLE_C,C_SHARP,B_FALT;
}
class Instrument{
public void play(Note n){
System.out.println("Instrument.play()");
}
}
class Wind extends Instrument{
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);
}
}
8.2 转机
编译器怎么才知道Instrument引用指向的是Wind对象,而不是其他对象呢?实际上编译器无法得知。为了深入理解这个问题,有必要研究一下绑定这个话题。
8.2.1 方法调用绑定
将一个方法调用同一个方法主体关联起来被称作绑定。
后期绑定,它的含义就是在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定或运行时绑定。
java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。
8.2.4 缺陷:“覆盖”私有方法
public class PrivateOverride {
private void f()
{
System.out.println("Private f()");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
PrivateOverride p = new Dervied();
p.f();//结果是Private f()
}
}
class Dervied extends PrivateOverride{
public void f()
{
System.out.println("public f()");
}
}
结论:是有非private方法才可以被覆盖。在导出类中,对于基类中的private方法,最好采用不同的名字。
8.2.5 缺陷:域和静态方法
class Super{
public int filed = 0;
public int getFiled(){
return filed;
}
}
class Sub extends Super{
public int filed = 1;
public int getFiled(){
return filed;
}
public int getSuperFiled()
{
return super.filed;
}
}
public class FiledAccess {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Super sup = new Sub();
System.out.println(sup.filed+ " "+sup.getFiled());
Sub sub = new Sub();
System.out.println(sub.filed+ " "+sub.getFiled() + " "+ sub.getSuperFiled());
/*
*
0 1
1 1 0
*/
}
}
8.3 构造方法和多态
构造方法实际上是static方法,只不过该static声明是隐式的。
8.3.1 构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。
class Meal{
Meal(){
System.out.println("Meal()");
}
}
class Bread{
Bread(){
System.out.println("Bread()");
}
}
class Cheese{
Cheese(){
System.out.println("Cheese()");
}
}
class Lettuce{
Lettuce(){
System.out.println("Lettuce()");
}
}
class Lunch extends Meal{
Lunch(){
System.out.println("Lunch()");
}
}
class ProtableLunch extends Lunch{
ProtableLunch(){
System.out.println("ProtableLunch()");
}
}
public class SandWich extends ProtableLunch{
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public SandWich(){
System.out.println("SandWich()");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new SandWich();
/*
Meal()
Lunch()
ProtableLunch()
Bread()
Cheese()
Lettuce()
SandWich()
*/
}
}
表明复杂对象调用构造器要遵照下面顺序:
1)调用基类构造器。这个步骤会不断的反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,知道最低层的导出类
2)按声明顺序调用成员的初始化方法
3)调用导出类构造器的主体。
8.3.2 继承与清理
通过组合和继承方法来创建新类时,永远不必担心对象的清理问题,子对象通常都会留给垃圾回收器进行处理。
如果确实遇到清理问题,使用dispose()方法,务必记住调用基类版本的dispose()方法,否则基类的清理动作就不会发生,
8.3.3 构造器内部的多态方法的行为
class Glyph{
Glyph(){
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
void draw(){
System.out.println("Glyph.draw()");
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){
radius = r;
System.out.println("RoundGlyph.RoundGlyph().radius = " + radius);
}
void draw(){
System.out.println("RoundGlyph.draw(), radius =" + radius);
}
}
public class PloyConstructors {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new RoundGlyph(5);
/*
Glyph() before draw()
RoundGlyph.draw(), radius =0
Glyph() after draw()
RoundGlyph.RoundGlyph().radius = 5
*/
}
}
8.4 协变返回类型
Java SE5 添加了协变返回类型,它表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。