第八章 多態 成員對象的清理工作。引用計數構造器內部多態行爲,初始化(真)

1.清理

  • 一個對象擁有自己的成員對象,並且知道他們應該存活多久,因此一個對象可以調用其特殊方法對其成員對象進行釋放。
    然而,如果這些成員對象存在於其他一個或多個對象共享的情況,問題就複雜了,這個時候需要使用引用計數來跟蹤仍舊訪問着共享對象的對象數量。
  • 下面的代碼可以清楚的說明這個問題的解決辦法,也是引用計數法
    class Shared{
    	private int refcount = 0;
    	private static long counter = 0;
    	private final long id = counter ++;
    	public Shared(){
    		System.out.println("Createing " + this);
    	}
    	//每有一個對象使用就調用這個方法進行加1,
    	public void addRef(){
    		refcount ++;
    	}
    	protected void dispose(){
    		//如果共享這個對象都使用完了就清理這個類
    		if (--refcount == 0) {
    			System.out.println("Disposiing" + this);
    		}
    	}
    }
    
    class Composing{
    	private Shared shared;
    	private static long counter = 0;
    	private final long id = counter ++;
    	public Composing(Shared shared){
    		System.out.println("Createing " + this);
    		this.shared = shared;
    		this.shared.addRef();
    	}
    	protected void dispose(){
    		System.out.println("disposing " + this);
    		shared.dispose();
    	}
    	public String toString(){return "Composing " + id;}
    }
    public class ReferenceCounting {
    
    	public static void main(String[] args) {
    		Shared shared = new Shared();
    		/**
    		 * shared被對個Composing共享,不能釋放一個Composiing就把這個shared對象給釋放了,
    		 * 這樣會報錯的,所以使用引用計數,只有到最後一個對象使用完畢後纔對shared進行釋放
    		 */
    		Composing[] composing = {
    				new Composing(shared),
    				new Composing(shared),
    				new Composing(shared),
    				new Composing(shared),
    				new Composing(shared)
    		};
    		for (Composing c : composing) {
    			c.dispose();
    		}
    	} }
    

2.構造器內部的多態方法調用行爲

  • 什麼意思呢,就是在一個父類的構造器內部調用其可以被子類重寫的方法,這一種特殊的情況
    代碼如下
    package com.yue.dt;
    
    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.RoundGlyph(),radius = " + radius);
    	}
    
    	void draw() {
    		System.out.println("RoundGlyph.draw(), radius = " + radius);
    	}
    }
    
    public class PolyConstructors {
    	public static void main(String[] args) {
    		new RoundGlyph(5);//聲明初始化子類
    	}
    }
    /**
     * 輸出結果
     * Glyph() before draw() 
     * RoundGlyph.draw(), radius = 0 調用了子類的方法
     * Glyph() after draw()
     * RoundGlyph.RoundGlyph(),radius = 5
     */
    由上面代碼可以看出:由於集成過程中構造器的執行順序問題,在加上調用了可以被子類重寫的方法的問題的重合,只是輸出了我們意想不到的結果

3.初始化的實際過程

  1. 在其他事物發生之前,將分配給對象的存儲空間初始化成二進制的零。
  2. 如前面那樣調用基類的構造器。此時調用覆蓋後的子類中draw()方法,在調用子類構造器之前調用,由於1,radius (int)被初始化成默認值0,
  3. 按照聲明的順序調用成員的初始化方法。
  4. 最後,調用子類的構造器中的代碼。
  • 這樣做的好處是所有的東西都至少初始化爲0,其中通過“組合”嵌入進類內部的對象引用也被初始化爲null,使用這樣一個引用會拋出運行時異常。
  • 怎樣解決上面的奇葩問題呢。
    那就是不要在父類構造器中填寫可以被覆蓋的方法,如果要填寫方法,就填寫父類中private 或者final修飾的方法,儘量避免調用方法,儘可能的簡單





發佈了26 篇原創文章 · 獲贊 14 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章