1. 程式人生 > >JAVA程式設計基礎——類和物件(繼承、多型)

JAVA程式設計基礎——類和物件(繼承、多型)

一、單利模式

(1)單例類只能有一個例項;

(2)單例類必須自己建立自己的唯一例項;

(3)單例類必須給所有其他物件提供這個例項。

場景:一個類可以定義無數個物件,但是隻有一個例項。

1、餓漢式單利模式

package practise.test1030;
//餓漢式單利模式
class MySingleTon2{
    private static MySingleTon2 singleTon = new MySingleTon2();
    private MySingleTon2(){
        System.out.println("MySingleTon2().init");
    }

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

public class Test2 {
    public static void main(String[] args) {
        MySingleTon2 mySingleTon = MySingleTon2.getInstance();
        MySingleTon2 mySingleTon2 = MySingleTon2.getInstance();
        MySingleTon2 mySingleTon3 = MySingleTon2.getInstance();
        MySingleTon2 mySingleTon4 = MySingleTon2.getInstance();
        System.out.println(mySingleTon);
        System.out.println(mySingleTon2);
        System.out.println(mySingleTon3);
        System.out.println(mySingleTon4);//地址相同
    }
}

2、懶漢式單利模式

package practise.test1030;
//懶漢式單利模式
class MySingleTon{
    private static Object lock = new Object();
    private static MySingleTon singleTon = null;

    private MySingleTon(){
        System.out.println("MySingleTon().init");
    }

    //提供一個全域性的訪問點
    //可重入函式:執行緒安全的函式     靜態條件    臨界區程式碼段      原子性操作   加鎖

    public static MySingleTon getInstance(){
        //1
        /**
         * 原子操作
         * 多程序時,第一個程序還未來得及new一個物件,singleTon==null
         */
        if(singleTon == null){
            singleTon = new MySingleTon();
        }

        //2
        //單執行緒浪費空間
        synchronized (lock){
            if(singleTon == null){
                singleTon = new MySingleTon();
            }
        }

        //3
        //多執行緒時至少執行兩次
        if(singleTon == null){
            synchronized (lock){
                singleTon = new MySingleTon();
            }
        }

        //4     雙重檢驗    (double-checked locking)
        if(singleTon == null){
            synchronized (lock){
                if(singleTon == null){
                    singleTon = new MySingleTon();
                }
            }
        }

        return singleTon;
    }
}
public class Test1 {
    public static void main(String[] args) {
        MySingleTon mySingleTon = MySingleTon.getInstance();
        MySingleTon mySingleTon2 = MySingleTon.getInstance();
        MySingleTon mySingleTon3 = MySingleTon.getInstance();
        MySingleTon mySingleTon4 = MySingleTon.getInstance();
        System.out.println(mySingleTon);
        System.out.println(mySingleTon2);
        System.out.println(mySingleTon3);
        System.out.println(mySingleTon4);//地址相同
    }
}

3、靜態內部類實現單利模式

package practise.test1030;
//靜態內部類實現單利模式
//只有訪問靜態內部類的時候,才會建立物件
class MySingleTon3{
    private MySingleTon3(){

    }

    private static class SingleTon{
        private static MySingleTon3 single = new MySingleTon3();
    }

    public static MySingleTon3 getInstance(){
        return SingleTon.single;
    }
}
public class Test3 {
    public static void main(String[] args) {
        MySingleTon3 single = MySingleTon3.getInstance();
        MySingleTon3 single2 = MySingleTon3.getInstance();
        MySingleTon3 single3 = MySingleTon3.getInstance();
        MySingleTon3 single4 = MySingleTon3.getInstance();
        System.out.println(single);
        System.out.println(single2);
        System.out.println(single3);
        System.out.println(single4);
    }
}

二、繼承

繼承是一種機制,可以進行程式碼的重用。在JAVA中,所有的繼承都是公有繼承。子類比超類擁有的功能更加豐富

基類(超類):

子類(派生類):

在通過擴充套件類定義子類的時候,僅需要指出子類與超類的不同之處。所以在設計類的時候應該將通用的方法放在超類,而將具有特殊用途的方法放在子類當中。

在子類中可以增加域、增加方法或覆蓋超類的方法,但是絕對不能刪除繼承的任何域和方法。

注:

關鍵字this有兩個用途:1、引用隱式引數;2、呼叫該類其他的構造器。

super關鍵字也有兩個用途:1、呼叫超類的方法;2、呼叫超類的構造器。

在呼叫構造器的時候,這兩個關鍵字的使用方式很相似:呼叫構造器的語句只能作為另一個構造器的第一條語句出現。構造引數既可以傳遞給本類(this)的其他構造器,也可以傳遞給超類(super)的構造器。

package practise.test1030;
//繼承
/**
 * super關鍵字:
 * super();——呼叫基類的建構函式
 * super.data——訪問基類的資料成員
 * super.func()——呼叫基類的成員方法
 */

/**
 * 基類
 */
class Base{
    public int ma;

    public Base (int ma){
        this.ma = ma;
    }

    //Base的靜態程式碼塊
    static {
        System.out.println("Base.static{}");
    }

    //Base的例項程式碼塊
    {
        System.out.println("Base.instance");
    }

    public void fun(){
        System.out.println("Base.fun()");
    }

    public static void fun2(){
        System.out.println("Base.fun2()");
    }
}

/**
 * 派生類
 * 繼承父類除建構函式外的所有屬性
 * 派生類構造物件的初始化順序
 * 基類靜態
 * 派生類靜態
 * 基類例項
 * 基類構造方法
 * 派生類例項
 * 派生類構造方法
 */

class Derieve extends Base {
    private int mb;

    public Derieve(int a,int b){
        super(a);//必須放在第一行
        this.ma = ma;
    }

    //Base的靜態程式碼塊
    static {
        System.out.println("Base.static{}");
    }

    //Base的例項程式碼塊
    {
        System.out.println("Base.instance");
    }

    public void fun(){
        System.out.println("Base.fun()");
    }

    public static void fun2(){
        System.out.println("Base.fun2()");
    }
}

//ERROR
/**
 * 繼承了ma,沒有繼承建構函式,
 * 如何構造基類的資料成員???(呼叫基類的建構函式)
 */
/*class Derieve extends Base{
    private  int mb;
}*/

/**
 * ERROR
 * 派生類的建構函式只能構造派生類的資料成員,不能構造基類的資料成員
 *
class Derieve extends Base {
    private int mb;
    public Derieve(int a,int b){
        //ERROR ma = a;
        this.mb = mb;
    }
}*/

public class Test4 {
    public static void main(String[] args) {
        Base base = new Base(100);
        Derieve derieve = new Derieve(111,222);//new物件  1、分配類存  2、
        /**
         * JAVA中只支援向上繼承
         */
        base = derieve;
        /**
         * 基類和派生類之間的相互賦值
         */

        /**
         * 過載:overloade:函式名相同,引數列表不同,和返回值沒關係
         * 並不一定是在同一個類當中,在繼承關係上也能構成過載
         * 重寫(覆蓋)overwrite;函式名相同,引數列表相同,函式返回值相同
         * 返回值不同能否構成過載:???
         * 遵守協變型別——JAVA程式設計思想
         */

    }
}

問題一:派生類繼承了父類除構造方法外的所有屬性。

問題二:super關鍵字:

super關鍵字:

(1)super();——呼叫基類的建構函式

(2)super.data——訪問基類的資料成員

(3)super.func()——呼叫基類的成員方法

問題三:派生類構造物件的初始化順序?

(1)基類靜態;
(2)派生類靜態;
(3)基類例項;
(4)基類構造方法;
(5)派生類例項;
(6)派生類構造方法。
package practise.test1030;

//(public)基類的資料成員在派生類中的訪問許可權

class Base1{
    public int ma;

//    public Base (int ma){
//        this.ma = ma;
//    }

    //Base的靜態程式碼塊
    static {
        System.out.println("Base.static{}");
    }

    //Base的例項程式碼塊
    {
        System.out.println("Base.instance");
    }

    public void fun(){
        System.out.println("Base.fun()");
    }

    public static void fun2(){
        System.out.println("Base.fun2()");
    }
}

//class Derieve1 extends Base {
//
//}

public class Test5 {
    public static void main(String[] args) {

    }
}

問題四:基類資料成員在派生類當中的訪問許可權:

(public)基類的資料成員在派生類當中的訪問許可權
  同包子類 同包非子類 不同包子類 不同包非子類
public     可以      可以       可以       可以
private        /         /          /          /
proctected      可以      可以       可以          /

預設許可權

(包訪問許可權)

      可以       可以         /          /

問題五:過載和重寫(覆蓋):

(1)過載:overloade:函式名相同,引數列表不同,和返回值沒關係。

          注:並不一定是在同一個類當中,在繼承關係上也能構成過載 。

(2)重寫(覆蓋)override:函式名相同,引數列表相同,函式返回值相同。

三、多型

多型:基類引用,引用了派生累物件,並且和派生類有同名的覆蓋方法。

JAVA實現多型有三個必要條件:繼承、重寫、向上轉型。

(1)繼承:在多型中必須存在有繼承關係的子類和父類;

(2)重寫:子類對父類中的某些方法進行重新定義,在呼叫這些方法時就會呼叫子類的方法;

(3)向上轉型:在多型中,只有將子類的引用賦給父類物件,才能夠呼叫父類的方法和子類的方法。

問題六:建構函式內,能不能發生多型?

問題七:動多型和靜多型的理解:

(1)動多型:發生在執行的時候(過載);

(2)靜多型:發生在編譯的時候(重寫).

在JAVA只有兩種方式可以實現多型:繼承和實現介面。