1. 程式人生 > >單利模式 類的繼承和多型

單利模式 類的繼承和多型

單利模式

  • 單利類只能有一個例項
  • 單利類必須自己建立自己的唯一例項
  • 單利類必須給所有其他物件提供這一例項
  • 單利模式適用條件:一個類可以定義無數個物件,但是隻能有一個例項

單利模式有兩種:懶漢式、餓漢式

懶漢式單利模式,非執行緒安全:
程式碼中有四種建立物件的方法,第四種為完善的方法,保障了在多個執行緒進入的情況下,可以僅產生一個例項

class SingleClass1 {
    private static Object lock = new Object();
    private static SingleClass1 singleClass =
null; private SingleClass1() { System.out.println("這是私有建構函式SigleClass1().init"); } //提供一個全域性訪問點 //可重入函式======執行緒安全的函式 public static SingleClass1 getInstance() { //4.雙重檢驗 if (singleClass == null) { synchronized (lock) { if (singleClass ==
null) { singleClass = new SingleClass1(); } } } //第一個執行緒在開闢記憶體時再進去一個執行緒,第二個執行緒也可以進入,在lock外等待,當第一個執行緒結束後,lock解鎖,會產生兩個物件 /*3. if (singleClass == null) { synchronized (lock) { singleClass = new SingleClass1(); } }*/
//只有一個執行緒時也會產生lock,浪費資源 /* 2.synchronized (lock) { if (singleClass == null) { singleClass = new SingleClass1(); } }*/ //第一個執行緒在開闢記憶體時再進去一個執行緒,會產生兩個物件 /* 1.if (singleClass == null) { singleClass = new SingleClass1(); }*/ return singleClass; } }

餓漢式單利模式:

class SingleClass2 {
    private static SingleClass2 singleClass = new SingleClass2();

    private SingleClass2() {
        System.out.println("這是私有建構函式SigleClass2().init");
    }

    //提供一個全域性的訪問點
    public static SingleClass2 getInstance() {
        return singleClass;
    }
}

類的繼承

繼承:一種機制,可以進行程式碼的重用
派生類會繼承基類除建構函式外的所有屬性

在派生類中可以使用super:

  • super();—>呼叫基類的建構函式,必須放在基類建構函式的第一行
  • super.data—>訪問基類的資料成員
  • super.methods1();—>呼叫基類的成員方法
class Father {
    int data1;
    
    public Father(int data1) {       
        System.out.println("Father.init()");
        this.data1 = data1;
    }

    static {
        System.out.println("Father.static{}");
    }

    {
        System.out.println("Father.instance{}");
    }
}

class Son extends Father {
    private int data2;
    
    public Son(int a, int b) {
        super(a);      
        System.out.println("Son.init{}");
    }

    static {
        System.out.println("Son.static{}");
    }

    {
        System.out.println("Son.instance{}");
    }

    public void methods1() {
        System.out.println("Son.methods1()");
    }
}

派生類構造物件的初始化順序:在這裡插入圖片描述
基類派生類靜態塊初始化 ====> 基類例項塊、建構函式初始化 ====> 派生類例項塊、建構函式初始化

函式的重寫與過載
  • 過載:函式名相同,引數列表不同,與返回值無關,不一定在同一類中,繼承關係也可以
  • 重寫/覆蓋:函式名相同,引數列表相同,返回值相同
class Father {
    int data1;

    public Father(int data1) {
        System.out.println("Father.init()");
        this.data1 = data1;
    }

    public void methods1() {
        System.out.println("Father.methods1()");
    }    
}

class Son extends Father {
    private int data2;

    public Son(int a, int b) {
        super(a);     
        System.out.println("Son.init()");
    }
    
    public void methods1() {
        System.out.println("Son.methods1()");
    }  
    
	public void methods1(int a) {
        System.out.println("Son.methods1(int)");
    }    
}

如上述程式碼,
在基類和派生類中進行了methods1方法的重寫
在派生類中有兩個methods1方法,而引數不同,為函式的過載

基類和派生類之間的相互賦值

派生類可以賦值給基類
基類不可以賦值給派生類

public static void main(String[] args) {
        Father father = new Father(100);
        Son son = new Son(200, 300);
        father = son;//可以,不報錯
        son = father;//不可以,error
    }
基類資料成員在派生類中訪問許可權

在這裡插入圖片描述

類的多型

多型:基類引用,引用了派生類物件,並且基類和派生類物件有同名的覆蓋方法
在建構函式中,也可以發生多型

class Father {
    int data1;

    public Father(int data1) {
        System.out.println("Father.init()");
        this.data1 = data1;
    }

    public void methods1() {
        System.out.println("Father.methods1()");
    }

    public static void methods2() {
        System.out.println("Father.methods2()");
    }
}

class Son extends Father {
    private int data2;

    public Son(int a, int b) {
        super(a);
        System.out.println("Son.init()");
    }

    public void methods1() {
        System.out.println("Son.methods1()");
    }

    public static void methods2() {
        System.out.println("Son.methods2()");
    }
}

public class TestDemo1 {
    public static void main(String[] args) {
        Father father = new Son(200, 300);
        father.methods1();//動多型  執行的時候
        Father.methods2();//靜多型  編譯的時候
    }
}

輸出結果:在這裡插入圖片描述

由圖可知,程式碼中呼叫的是father.methods1()方法,而實際輸出的是Son.methods1()方法,原因如下:

在這裡插入圖片描述
基類引用了派生類物件,Father生成的物件存在於堆上,而其中存在方法表,當函式編譯時,會指向方法區中Class物件的地址
由圖可知,該函式Class物件地址中有基類和派生類的地址,在函式執行的過程中,派生類方法的地址會覆蓋基類方法的地址,所以輸出的為派生類的方法