anonymous的Java學習筆記(17)——Java中的類成員及單例類的實現

類成員

類成員的定義

Java類裏只能包含成員變量方法構造器初始化塊內部類(包括接口、枚舉)5種成員,目前已經介紹了前面4種,其中static可以修飾成員變量方法初始化塊內部類(包括接口、枚舉),static修飾的成員就是類成員。

  • static關鍵字修飾的成員就是類成員,前面已經介紹的類成員類變量類方法靜態初始化塊3個成分
  • static關鍵字不能修飾構造器
  • static修飾的類成員屬於整個類,不屬於單個實例
  • static修飾的初始化塊叫做靜態初始化塊也是一種類成員。

如何訪問類變量

  • 類變量既可通過來訪問,也可通過類的對象來訪問。通過對象訪問類變量只是一種假象,通過對象訪問的依然是該類的類變量,可以這樣理解:通過對象來訪問類變量時,系統會在底層轉換爲通過該類來訪問類變量。因此,從程序運行表面來看,即可看到同一類的所有實例的類變量共享同一塊內存區。
  • 很多對象都不允許通過對象來訪問類變量,對象只能訪問實例變量;類變量必須用類來訪問。
  • 一個null對象訪問實例成員(包括實例變量和實例方法),將會引發java.lang.NullPointerException空指針異常,因爲null表明該實例根本不存在,既然實例不存在,那麼它的實例變量和實例方法自然也不存在.

代碼示例

package com.abc.part5;

/**
 * @author mi
 */
public class NullAccessStatic {
    public static void say() {
        System.out.println("static修飾的類方法~");
    }

    static String name;
    int age = 21;

    public String eat() {
        return "我正在喫飯";
    }

    public static void main(String[] args) {
//        NullAccessStatic nullAccessStatic = new NullAccessStatic();
        NullAccessStatic nullAccessStatic = null;
//        say();
        //以下代碼雖然會正常輸出。但是在IDEA中會報紅,提示:不應該通過類實例訪問靜態成員,所以類成員必須使用類名來訪問。
        nullAccessStatic.say();
        nullAccessStatic.name = "哈哈";
        System.out.println(nullAccessStatic.name);
        System.out.println("************************************************");
        NullAccessStatic.say();
        System.out.println(NullAccessStatic.name);
        System.out.println("************************************************");
        //一個null對象訪問實例成員(包括實例變量和實例方法),將會引發`java.lang.NullPointerException`空指針異常,因爲null表明該實例根本不存在,既然實例不存在,那麼它的實例變量和實例方法自然也不存在.
        //System.out.println(nullAccessStatic.age);
        //nullAccessStatic.eat();
        /**
         * 以上代碼會輸出
         * static修飾的類方法~
         * 哈哈
         * ************************************************
         * static修飾的類方法~
         * 哈哈
         * ************************************************
         */
    }
}

單例類

背景

大部分時候都把類的構造器定義成public訪問權限,允許任何類自由創建該類的對象。但在某些時候,允許其他類自由創建該類的對象沒有任何意義,還可能造成系統性能下降(因爲頻繁地創建對象、回收對象帶來的系統開銷問題)。

基於以上背景引入單例類的概念

如果一個類始終只能創建一個實例,則這個類被稱爲單例類。

實現一個單例類的步驟

  1. 根據良好封裝的原則:一旦把該類構造器隱藏起來,就需要提供public方法作爲該類訪問點,用於創建該類的對象,且該方法必須使用static修飾(因爲調用該方法之前還不存在對象,因此調用該方法的不可能是對象,只能是類)
  2. 該類還必須緩存已經創建的對象,否則該類無法知道是否曾經創建過對象,也就無法保證只創建一個對象。爲此該類需要使用一個成員變量保存曾經創建的對象,因爲該成員變量需要被上面的靜態方法訪問,故該成員變量必須使用static修飾。

單例類代碼示例

package com.abc.part5;

/**
 * @author mi
 */
public class Singleton {
    /**
     * 使用一個類變量來緩存曾經創建過的實例
     */
    private static Singleton instance;

    /**
     * 對構造器使用private修飾,隱藏該構造器
     */
    private Singleton() {

    }

    /**
     * 提供一個靜態方法,用於返回Singleton實例
     * 該方法可以加入自定義控制,保證只產生一個Singleton對象
     *
     * @return Singleton對象
     */
    public static Singleton getInstance() {
        /**
         * 如果instance爲null,則表明還不曾創建過Singleton對象
         * 如果instance不爲null,則表明已經創建了Singleton對象,將不會重新創建新的實例
         */
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

單例測試類

package com.abc.part5;

public class SingletonTest {
    public static void main(String[] args) {
        /**
         * 創建Singleton類的對象不能通過構造器
         * 只能通過getInstance方法來創建類實例
         */
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println("s1:" + s1);
        System.out.println("s2:" + s2);
        System.out.println(s1.equals(s2));
        System.out.println(s1 == s2);
        /**
         * 輸出
         * s1:com.abc.part5.Singleton@60e53b93
         * s2:com.abc.part5.Singleton@60e53b93
         * true
         * true
         */
    }
}

正是通過上面getInstance()方法提供的自定義控制(這也是封裝的優勢:不允許自由訪問類的成員變量和實現細節,而是通過方法來控制合適暴露) ,保證Singleton類只能產生一個實例。

所以,在SingletonTest類的main()方法中,看到兩次產生的Singleton對象實際上是同一個對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章