第八章多态

多态是继数据抽象和继承之后的第三种基本特征.多态也叫(动态绑定,运行时绑定或者后期绑定)

8.1方法调用绑定

方法调用和同一个方法主体关联起来叫绑定.在调用执行前就绑定的称为前期绑定,面向过程编程的采用的默认方式就是前期绑定.例如c语言;java中除了static 和final 方法(private属于final方法)之外,其他的方法都是采用后期绑定的方式(动态绑定或者运行时绑定).

8.2.3可扩展性

以instrument为例.由于多态机制我们可以向系统中添加多个新的乐器类型,而不需要修改tune().良好的OOP程序中,大多数或者所有的方法都应该遵循tune()模型,只与基类接口对外公开方法)通信,这样的程序是可扩展的,因为可以从通用基类继承接口,从而修改成新的功能,而不需要修改基类的任何代码就可以实现.

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public enum Note {
    MIDDLE_C, C_SHARP, B_FLAT;
}

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Instrment {
    public void play(Note note) {
        System.out.println("play" + note);
    }
}

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Brass extends Instrment {
    @Override
    public void play(Note note) {
        System.out.println("Brass play" + note);
    }
}

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Wind extends Instrment {
    @Override
    public void play(Note note) {
        System.out.println("Wind play" + note);
    }
}

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Music {
    public void tune(Instrment instrment) {
        instrment.play(Note.MIDDLE_C);
    }
    public static void main(String[] args) {
        Music music = new Music();
        Wind wind = new Wind();
        music.tune(wind);
    }
}

8.2.4 :陷阱 不能重写(overriding) private 方法

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class PrivateOverride {
    private void f() {
        System.out.println("private --");
    }
    public static void main(String[] args) {
        PrivateOverride privateOverride = new Deliver();
        privateOverride.f();
    }
}


package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Deliver extends PrivateOverride {
    public void f() {
        System.out.println("public-----");
    }
}
//output –
private --

事实上PrivateOverride的f(),在子类Deliver是无法override的,Deliver中的f()是当做一个新方法,对于基类是无法知晓的.所以在PrivateOverride类里面调用的f(),只能是PrivateOverride本身的.

8.2.5 陷阱 域(成员变量)和静态方法

成员变量和静态方法是无法重写overriding

package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Super {
    public int field = 0;
    public int getField() {
        return field;
    }
}
 package tinking_in_java.Polymorphism;
/**
 * Created by leon on 17-12-14.
 */
public class Sub extends Super {
    public int field = 1;
    public int getField() {
        return field;
    }
    public int getSurperField() {
        return super.getField();
    }
    public static void main(String[] args) {
        Super sub = new Sub();
        System.out.println("sub.field() " + sub.field);
        System.out.println("sub.getField() " + sub.getField());
    }
}
//---output
sub.field() 0
sub.getField() 1

任何访问域(成员变量)操作都是由编译器解析,所以不是多态的.事实上sub.field和super.field分配了不同的存储空间.Sub实际上包含了两个field域,一个自己的,一个是它从Super得到的.

8.3构造器和多态

构造器不具有多态性,也就是说构造器不能重写,必须是调用.实际上构造器是隐私的申明成static方法.基类的构造器总是在导出来的构造过程中被调用,按照继承的顺序逐层向上调用,以保证每个基类构造器都能得到调用.(最先构造的是最顶层的基类).

复杂对象调用构造器的顺序:

1.调用构造器,逐层递推.首先是构造基类的根类,然后往下一层导出类,最后是调用的直接调用的导出类.
2.按照顺序调用成员初始化方法
3.调用导出类的构造函器主体
如果遵守了这中顺序,那么在导出类的所有基类的成员都初始化了.但是遗憾这种做法不适用与所有的情况.

8.3.2 继承与清理

如果导出类overriding了基类的 自定义垃圾回收方法dispose(),必须在overriding的dispose()中调用super.dispose(),否则基类的dispose不会被执行.在进行垃圾清理时,必须先清理完导出类,在调用基类的dispose(),原因是所有对象清理的顺序都是按照构造时顺序的逆序进行的,这样才安全.

如果一个基类(或者被其他类多次调用的类)存在共享对象的情况(有static 对象),这时候情况变得比较复杂,必须在基类或者提供调用类本身进行引用计数器管理共享对象,然后进行释放.原因是共享对象不管创建多少实例,他都只会生成一分,如果在其他的需要垃圾回收的实例被释放了,很可能会导致剩下没有来得及垃圾回收的实例引用造成数据为空.
以下例子:

package tinking_in_java.share;
/**
 * Created by leon on 17-12-14.
 */
public class Share {
    private static String shareStr = "shareABC";
    private int refrence = 0;
    private static int count = 0;
    private int id = count++;
    public Share() {
        System.out.println("Creat" + this);
    }
    public void addRef() {
        refrence++;
    }
    @Override
    public String toString() {
        return "Share" + id;
    }
    public void printShare() {
        System.out.println(shareStr);
    }
    public void dispose() {
        if (--refrence == 0) {
System.out.println("readydispose" + shareStr.toString());
            shareStr = null;
        }
    }
}
package tinking_in_java.share;
/**
 * Created by leon on 17-12-14.
 */
public class Compose {
    private Share share;
    private static long count = 0;
    private final long id = count++;
    public Compose(Share share) {
        this.share = share;
        this.share.printShare();
        this.share.addRef();
    }
    @Override
    public String toString() {
        return "Compose" + id;
    }
    public void dispose() {
        share.dispose();
    }
    public static void main(String[] args) {
        Share share = new Share();
        Compose[] composes = {
                new Compose(share),
                new Compose(share),
                new Compose(share),
                new Compose(share)};
        for (Compose compose : composes) {
            compose.dispose();
        }
    }
}
//output------
CreatShare0
shareABC
shareABC
shareABC
shareABC
ready disposeshareABC

如果不进行引用判断,会抛出NPE异常.

8.3.3 构造器内部调用多态方法行为

构造器内部调用多态方法,会直接调用被覆盖后的方法.但是构造器的本质工作是创建对象,导出类在调用基类的构造器的时候,导出类其实还处于没有初始化状态,但是对于基类构造器里面动态绑定的方法确是可以调用导出类中的覆盖方法,这样的结果可能导致调用导出类中的overriding方法中的成员可能还没初始化.

package tinking_in_java.ConstructInner;
/**
 * Created by leon on 17-12-15.
 */
public class Super {
    public Super() {
        System.out.println("Super before draw");
        draw();
        System.out.println("Super atfer draw");
    }
    void draw() {
        System.out.println("Super draw");
    }
}

package tinking_in_java.ConstructInner;
/**
 * Created by leon on 17-12-15.
 */
public class Sub extends Super {
    String drawTxt;
    public Sub(String txt) {
        drawTxt = txt;
    }
    @Override
    void draw() {
        System.out.println("Sub draw " + drawTxt);
    }
    public static void main(String[] args) {
        Sub sub = new Sub("hello");
    }
}
//output----
Super before draw
Sub draw null
Super atfer draw

初始化的实际过程:

1.在其他任何事物发生之前,先分配给对象存储空间(进行定义),赋默认值.
2.调用基类的构造器
3.按照顺序调用成员的初始化方法
4.调用导出类的构造器主体

构造器有一条有效的准则:
用尽可能简单的方式使对象进入正常状态,如果可以的话,不要调用其他的方法.

8.5用继承进行设计

在创建新类的时候应该首先考虑组合方式,其实在考虑继承关系

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容