單利模式 類的繼承和多型
阿新 • • 發佈:2018-11-02
單利模式
- 單利類只能有一個例項
- 單利類必須自己建立自己的唯一例項
- 單利類必須給所有其他物件提供這一例項
- 單利模式適用條件:一個類可以定義無數個物件,但是隻能有一個例項
單利模式有兩種:懶漢式、餓漢式
懶漢式單利模式,非執行緒安全:
程式碼中有四種建立物件的方法,第四種為完善的方法,保障了在多個執行緒進入的情況下,可以僅產生一個例項
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物件地址中有基類和派生類的地址,在函式執行的過程中,派生類方法的地址會覆蓋基類方法的地址,所以輸出的為派生類的方法