第七章
方法覆盖
Java SE5中新增加了 @Override 注解,它虽非关键字,但可以用作关键字。当编写程序想要覆盖某方法时可以选择添加,在不小心将方法重载而非注释时,编辑器将会报错。
protected关键字
它指明“就类用户而言,这是private”,但对于任何继承与此类的导出类(子类)或其他位于同一包内的类来说,它是可以访问的。
第八章 多态
构造器内部的多态方法的行为
在一般方法内部,动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所在的那个类,还是属于那个类的导出类。
如果要调用构造器内部的一个动态绑定方法,就要用到那个方法的被覆盖后的定义。然而这个调用的效果可能难以预料,因为被覆盖的方法在对象被完全构造前就会被调用。这可能会造成一些难以发现的隐藏错误。
public class test
{
public static void main(String[] args)
{
new RoundGlyph(5);
}
}
class Glyph
{
void draw()
{
System.out.println("Glyph.draw()");
}
Glyph()
{
System.out.println("Glyph() before draw");
draw();
System.out.println("Glyph() after draw");
}
}
class RoundGlyph extends Glyph
{
private int radius=1;
RoundGlyph(int r)
{
radius=r;
System.out.println("RoundGlyph.raduis= "+radius);
}
void draw()
{
System.out.println("RoundGlyph.draw().radius= "+radius);
}
}
运行结果:
Glyph.draw()方法设计为将要被覆盖,这种覆盖是在RoundGlyph中发生的。但Glyph构造器会调用这个方法,结果调用的是Round Glyph.draw()方法。但查看输出结果,当Glyph调用draw()方法时,radius不是默认的初始值1,而是0。
我们需要知道初始化的实际过程:
- 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
- 如前所述那样调用基类构造器。此时,调用被被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,此时radius=0。
- 按照声明顺序调用成员的初始化方法
- 调用导出类的构造器主体。
这种情况可以在基类中对方法声明final避免,导出类的覆盖是真的完全覆盖啊(当然也可以用super再调用)!
向下转型
由于向上转型(将导出类的参数带入参数类型为基类的方法)会丢失具体类型的信息。但向上转型是安全的,因为基类不会具有大于导出类的接口。在Java中所有转型都会得到检查,包括进行一次普通的加括弧形式的类型转换,确保得到的是我们需要的那种类型,否则将返回ClassCastException(类转型异常)
class useful
{
public void f(){System.out.println("f() in useful");}
public void g(){System.out.println("g() in useful");}
}
class more_useful extends useful
{
public void f(){System.out.println("f() in more useful");}
public void g(){System.out.println("g() in more useful");}
public void u(){System.out.println("u() in more useful");}
}
public class test
{
public static void main(String[] args) {
useful[] x= {new useful(),new more_useful()};
x[0].f();
x[1].g();
//x[1].u(); 这是不被允许的
try
{
((more_useful)x[1]).u();
((more_useful)x[0]).u();
}
catch(ClassCastException e)
{
System.out.println("向下转型失败!");
}
}
}
运行结果:
more_useful继承自useful,所以可以向上转型到useful类型,数组中两个对象都属于useful类型,调用f()、g()方法都是许可的,但如果调用u()方法就会编译失败。如果想再使用,可以尝试向下转型,如果所转类型是正确的,那么转型成功,否则将抛出一个ClassCastException异常。
个人认为,向下转型是为了允许基类数组中存在子类,且允许子类能“反悔”调用扩展的成员方法。
[1] Bruce Eckel.Java编程思想[M].陈昊鹏,北京:机械工业出版社出版社.2007