thinking in java test chapter8(13)~(17)

来源:互联网 发布:sql server pdf下载 编辑:程序博客网 时间:2024/06/05 23:33

练习(13):在ReferenceCounting.java中添加一个finalize()方法,用来检校终止条件。
finalize()方法是垃圾回收器在清理对象是会优先调用的方法,用以清理一些垃圾回收器不知道如何释放的特殊内存。
此处shared就是Composing中一个难以判断是否回收的特殊内存。

public class Test13 {    public static void main(String[] args) throws Throwable {        Shared shared = new Shared();        Composing[] composings = new Composing[]{new Composing(shared),new Composing(shared),new Composing(shared),                                    new Composing(shared),new Composing(shared),new Composing(shared)};        for (Composing c :                composings) {            c.dispose();            c.finalize();        }        System.gc();//使达到调用finalize()方法的目的    }}class Shared {    private int refcount = 0;    private static long counter = 0;//对所有shared对象只有一份的计数器    private final long id = counter ++;    public Shared(){        System.out.println("Creating "+this);    }    public void addRef(){//该方法用来记住shared被多少个对象作为成员对象使用        refcount ++;    }    protected void dispose(){        if (--refcount == 0){//当不再有Composing对象持有同一个shared对象时,可以dispose()。            System.out.println("Disposing "+this);        }    }    @Override    public String toString() {        return "Shared" + id;    }    @Override    protected void finalize() throws Throwable {//题目要求添加的方法        if (--refcount!= 0){            return;        } else {            super.finalize();        }    }    public int getRefcount() {//用于外界获取shared当前的状态        return refcount;    }}class Composing {    private Shared shared;    private static long counter = 0;    private final long id = counter ++;    public Composing(Shared shared){        System.out.println("Creating " + this);        this.shared = shared;        this.shared.addRef();//每有一个Composing使用了某个shared,就给其加1计数    }    protected void dispose(){        System.out.println("dispose "+this);        shared.dispose();    }    @Override    public String toString() {        return "Composing" + id;    }    @Override    protected void finalize() throws Throwable {//题目要求添加的方法        if (shared.getRefcount() != 0){            System.out.println("can not finalize now");            return;        } else {            System.out.println("finalize now");            super.finalize();        }    }}

控制台输出:
Creating Shared0
Creating Composing0
Creating Composing1
Creating Composing2
Creating Composing3
Creating Composing4
Creating Composing5
dispose Composing0
can not finalize now
dispose Composing1
can not finalize now
dispose Composing2
can not finalize now
dispose Composing3
can not finalize now
dispose Composing4
can not finalize now
dispose Composing5
Disposing Shared0
finalize now

练习(14):修改练习12,使得其某个成员对象变为具有引用计数的共享对象,并证明它可以正确运行。
如上一题代码所示,引用计数是用来记下当前对象被引用了几次,每引用一次就加1,每少一次引用就减1,计数不为0则无法被删除,放置发生错误。

public class Test912 {    public static void main(String[] args){        Peanut peanut = new Peanut();        Rodent[] rodents = new Rodent[]{new Rodent(peanut),new Rodent(peanut),new Rodent(peanut),        new Rodent(peanut),new Rodent(peanut)};        for (Rodent r :                rodents) {            r.dispose();        }    }}class Peanut{    private int refcounter = 0;    private static long count = 0;    private final long id = count++;    public Peanut(){        System.out.println("creating " + this);    }    public void addref(){        refcounter++;    }    @Override    public String toString() {        return "peanut" + id;    }    public void dispose(){        if (--refcounter==0){            System.out.println("disposing" + this);        }    }}class Rodent{    private Peanut peanut;    private static long count=0;    private final long id = count++;    public Rodent(Peanut peanut){        shanghai = 100;        this.peanut = peanut;        peanut.addref();        System.out.println("Rodent");    }    private int shanghai;    public void bite(){        System.out.println("造成伤害" +shanghai + "点" );    }    public void dispose(){        System.out.println("Rodent" + this + "dispose");        peanut.dispose();    }    @Override    public String toString() {        return "Rodent" + id;    }}

练习(15):在PolyConstructors.java中添加一个RectangularGlyph,并证明会出现本节所描述的问题.

public class Test15 {    public static void main(String[] args){        new RoundGlyph(10);        new RectangularGlyph(10,10);    }}class Glyph{    void draw(){        System.out.println("Glyph.draw()");    }    public Glyph(){        System.out.println("Glyph before draw()");        draw();        System.out.println("Glyph after draw()");    }}class RoundGlyph extends Glyph{    private int radius = 1;    public RoundGlyph(int r){        radius = r;        System.out.println("RoundGlyph.radius = " + radius);    }    @Override    void draw() {        System.out.println("RoundGlyph.draw()" + radius);    }}class RectangularGlyph extends Glyph{    private int width;    private int height;    public RectangularGlyph(int w,int h){        width = w;        height = h;        System.out.println("Rect.width and height" + width + " " + height);    }    @Override    void draw() {        System.out.println("Recf.draw() :" + width + " " + height );    }}

输出:
Glyph before draw()
RoundGlyph.draw()0
Glyph after draw()
RoundGlyph.radius = 10
Glyph before draw()
Recf.draw() :0 0
Glyph after draw()
Rect.width and height10 10
代码中的问题是初始化的问题。
我们前面说到过java的方法绑定是动态绑定的,运行时才确定绑定谁。
java的初始化是从基类开始的。代码中我们新建一个Rectangular(矩形)Glyph时,它先调用了父类Glyph的构造方法,在父类的构造方法里,调用了draw()方法。
我们前面说过继承是把所有的东西都继承过来,重写方法是把那些跟父类方法一样的隐式的方法显示的修改,子类其实一直都是调用自己的方法。所以这时调用的draw()是子类重写的draw()方法。
同时我们说一下初始化的实际过程:
1,在任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
2,如前所述那样调用基类构造器。此时,调用被覆盖后的draw()(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们会发现radius的值为0
3,按照初始化顺序调用成员的初始化方法
4,调用导出类的构造器主体
所以在构造器中我们要尽量简单,尽可能避免调用其他方法。

练习(16):遵循Transmogrify.java这个例子,创建一个Starship类,包含一个AlertStatus引用,此引用可以只是三种不同的状态。纳入一些可以改变这些状态的方法。

public class Test16 {    public static void main(String[] args){        stage s = new stage();        s.move();        Which w = Which.SMALL;        Which w1 = Which.MIDD;        Which w2 = Which.BIG;        s.change(w1);        s.move();        s.change(w2);        s.move();    }}class StarShip{    public void action(){}}class SmallShip extends StarShip{    @Override    public void action() {        System.out.println("run");    }}class MiddShip extends StarShip{    @Override    public void action() {        System.out.println("fly");    }}class BigShip extends StarShip{    @Override    public void action() {        System.out.println("jump");    }}class stage{    private StarShip ship = new SmallShip();    public void change(Which w){        switch (w){            case SMALL:                ship = new SmallShip();                break;            case MIDD:                ship = new MiddShip();                break;            case BIG:                ship = new BigShip();                break;        }    }    public void move(){        ship.action();    }}enum Which{    SMALL,MIDD,BIG;}

这个练习主要是让我们理解继承和组合结合使用。
用继承表达行为间的差异,用字段表达状态上的变化。
这是书上的话,能够理解,但完美应用到代码中对于笔者还是很难的,先记住吧。

练习(17):使用练习(1)中的Cycle的层次结构,在Unicycle和Bicycle中添加Balance()方法,而Tricycle中不添加。创建所有这三种类型的实例,并将它们向上转型为Cycle数组。在该数组的每一个元素上都尝试调用balance(),并观察结果。然后将它们向下转型,再次调用balance(),并观察将所产生什么。
前面我们说过向上转型:
父类名 a = new 子类;
向上转型就是把子类看出父类,也就是把子类比父类多出来的方法成员都屏蔽掉。但实际上没有删除,内存里还是存在的。
这里可以把引用名看成遥控器,当把一个父类声明的引用给子类时,子类多出来的方法在遥控器来说是不可见的。所以也没办法调用子类扩展的部分。
而向下转型,就是:
子类 引用名 = a;
a里存的实际上是对象的内存地址。
当然向下转型是有风险的,因为你不知道你转的类型对不对,万一你试图把郭德纲转成冯小刚,那他们老婆会报一个ClassCastException的错误然后找你算账。

0 0