類成員
類成員的定義
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訪問權限
,允許任何類自由創建該類的對象。但在某些時候,允許其他類自由創建該類的對象沒有任何意義,還可能造成系統性能下降
(因爲頻繁地創建對象、回收對象
帶來的系統開銷問題
)。
基於以上背景引入單例類的概念
如果一個類始終只能創建一個實例,則這個類被稱爲單例類。
實現一個單例類的步驟
- 根據良好封裝的原則:一旦把
該類
的構造器隱藏起來
,就需要提供public方法
作爲該類
的訪問點
,用於創建該類的對象
,且該方法
必須使用static修飾(因爲調用該方法之前還不存在對象,因此調用該方法的不可能是對象,只能是類)
。 該類
還必須緩存已經創建的對象
,否則該類
無法知道是否曾經創建過對象
,也就無法保證只創建一個對象
。爲此該類
需要使用一個成員變量
來保存曾經創建的對象
,因爲該成員變量
需要被上面的靜態方法訪問
,故該成員變量
必須使用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對象實際上是同一個對象。