第八章多态

Overview

最近在学习Thinking in Java这部java经典,书非常不错,作者有非常深厚的编程功底,讲解知识时候说的很透彻,而且有一定发散,深入浅出,是一本阅读起来非常舒服。目前阅读到第八章,除了第五章初始化与清理里的垃圾回收机制有些难度以外,其他章节有一定编程经验的都可以看懂。从第八章开始,内容开始增多,各种重要知识点开始出现,如不好好总结很可能会漏掉一些细节,所以从这里开始对第八章进行总结。

有关垃圾回收的知识我准备在以后在整理jvm相关知识时再做详细总结。

Contents

  • 初始化的顺序
    • 先将分配给对象的存储空间初始化成二进制的零。
    • 调用基类的构造器。这个步骤一直递归下去,直至递归到根类,再开始从根向导出类开始初始化。
    • 按声明顺序调用成员的初始化方法。
    • 调用导出类构造器的主体。

初始化的顺序还要注意在第五章中提到的知识,静态对象要先于非静态对象。

  • 构造器内部的多态方法的行为
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 JavaTest{
    public static void main(String[] args) {
        Super sup = new Sub(); //向上转型
        //这里在直接访问域的时候,并没有出现理想中的多态
        System.out.println("sup.field: " + sup.field + "\nsup.getField: " + sup.getField() + "\n");
        Sub sub = new Sub();
        System.out.println("sub.field: " + sub.field + "\nsub.getField: " + sub.getField() + "\nsub.getSuperField():" + sub.getSuperField());
    }
}

输出为:
sup.field: 0
sup.getField: 1
sub.field: 1
sub.getField: 1
sub.getSuperField():

当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,由于多态采用的是动态绑定,而不是靠编译器,所以无法完成多态。

在上面的例子中,一个sub对象中有两个叫做field的域(Super.field和Sub.field),然而在引用sub中的field时所产生的默认域并非Super版本的field,因此必须显示的指明super.field。

在实际编程中非常不赞成出现这样的代码,这样的代码难以阅读,进而难以维护。

public class Test1 {
    public void printStr(String s){
        System.out.println("this is test1.printStr");
        System.out.println(s);
    }

    public void printStr2(String s){
        printStr(s);
    }
}
public class Test2 extends Test1 {
    public void printStr(String s){
        System.out.println("this is test2.printStr");
        System.out.println(s);
    }
    public static void main(String[] args) {
        Test1 test = new Test2();
        test.printStr2("test");
    }
}

输出为:
this is test2.printStr
test

这是第八章练习10的运行效果,和上面的结果放在一起对比看起来会更佳直观,这里的printStr()方法被覆盖了,即使是Test1的方法调用printStr()也会调用被覆盖的方法,与上面的变量覆盖是不同的。

  • 构造器内部的多态方法的行为
    • 在Java1.4及以前,子类方法如果要覆盖超类的某个方法,必须具有完全相同的方法签名,包括返回值也必须完全一样。
    • Java5.0放宽了这一限制,只要子类方法与超类方法具有相同的方法签名,或者子类方法的返回值是超类方法的子类型,就可以覆盖。
    • 注意:"协变返回(covariant return)",仅在subclass(子类)的返回类型是superclass(父类)返回类型的extension(继承)时才被容许。

你可能感兴趣的:(第八章多态)