Java编程思想(第七章 复用类&&第八章 多态)

第七章

方法覆盖
Java SE5中新增加了 @Override 注解,它虽非关键字,但可以用作关键字。当编写程序想要覆盖某方法时可以选择添加,在不小心将方法重载而非注释时,编辑器将会报错。
Java编程思想(第七章 复用类&&第八章 多态)
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);
    }
}

运行结果:
Java编程思想(第七章 复用类&&第八章 多态)
Glyph.draw()方法设计为将要被覆盖,这种覆盖是在RoundGlyph中发生的。但Glyph构造器会调用这个方法,结果调用的是Round Glyph.draw()方法。但查看输出结果,当Glyph调用draw()方法时,radius不是默认的初始值1,而是0。
我们需要知道初始化的实际过程:

  1. 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
  2. 如前所述那样调用基类构造器。此时,调用被被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,此时radius=0。
  3. 按照声明顺序调用成员的初始化方法
  4. 调用导出类的构造器主体。

这种情况可以在基类中对方法声明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("向下转型失败!");
        }
    }
}

运行结果:
Java编程思想(第七章 复用类&&第八章 多态)
more_useful继承自useful,所以可以向上转型到useful类型,数组中两个对象都属于useful类型,调用f()、g()方法都是许可的,但如果调用u()方法就会编译失败。如果想再使用,可以尝试向下转型,如果所转类型是正确的,那么转型成功,否则将抛出一个ClassCastException异常。
个人认为,向下转型是为了允许基类数组中存在子类,且允许子类能“反悔”调用扩展的成员方法。

[1] Bruce Eckel.Java编程思想[M].陈昊鹏,北京:机械工业出版社出版社.2007

上一篇:leetcode-36 + this may be useful when development is performed under newer sdk version


下一篇:[LeetCode每日一题]781. 森林中的兔子