程序題–自增變量
源自B站視頻:傳送門
這裏注意容易出錯的地方是上圖右邊步驟中的2,5 自增是在局部變量表中自增。而賦值操作是將操作數棧裏的數賦值給局部變量表對應的變量。
編程題–寫一個單例設計模式
餓漢式的三種創建方式
1:直接實例化
/*
*餓漢式
*在類初始化的時候,直接創建實例對象,不管你是否需要這個對象,都會創建
*
* (1)構造器私有化
* (2)自行創建,並保存在靜態變量中
* (3)對外提供獲取該實例對象的方式--public
* (4)強調單例,用final修飾(final修飾的一般爲常量,因此instance改爲大寫)
*/
public class Singleton1 {
public static final Singleton1 INSTANCE = new Singleton1();//餓漢式直接創建實例對象
private Singleton1() {
}
}
2:枚舉式
/*
* 枚舉類型代表該類型對象是有限的幾個
* 我們可以限定爲只有一個,就成爲單例了
* 更簡潔了
*
*/
public enum Singleton2 {
INSTANCE
}
3:靜態代碼塊
public class Singleton3 {
public static final Singleton3 INSTANCE ;
private String info;
//在靜態代碼塊中實例化,也是在類加載和初始化過程中,靜態常量便創建出來
static {
try {
//從配置文件去讀取info值,配置文件放在src下,用類加載器去加載
//靜態代碼塊適用於比較複雜的,需要讀一堆初始化數據的
Properties properties = new Properties();
properties.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));
//用當前類得類加載器去加載配置文件
INSTANCE = new Singleton3(properties.getProperty("info"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
single.properties文件放上我們想要的數據
#key=value
info=abc
上述三種方式的測試
懶漢式的三種創建方式
1:線程不安全
package singleton;
/*
* (1)構造器私有化
* (2)不自行創建,只定義一個靜態變量
* (3)提供一個靜態方法,獲取實例對象
*/
public class Singleton4 {
private static Singleton4 instance;
//這裏就不能public,因爲我們沒有給他創建對象,那麼直接這樣調用,可能是個空對象
private Singleton4() {
}
public static Singleton4 getInstance(){
if(instance==null) {
//測試當兩個線程,先後進入到這裏休眠,那麼就會創建兩個實例,所以這個例子存在線程安全問題
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance = new Singleton4();
}
return instance;
}
}
測試代碼:
public class TestSingleton4 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Singleton4> c = new Callable<Singleton4>() {
@Override
public Singleton4 call() throws Exception {
// TODO Auto-generated method stub
return Singleton4.getInstance();
}
};
/*
* 啓動線程服務
* 創建線程池
* 提交線程服務
*/
ExecutorService eService =Executors.newFixedThreadPool(2);
Future<Singleton4> future1 = eService.submit(c);
Future<Singleton4> future2 = eService.submit(c);
Singleton4 s1 = future1.get();
Singleton4 s2 = future2.get();
System.out.println(s1 == s2);
eService.shutdown();
}
}
因此線程不安全,返回的是false,
2:線程安全
package singleton;
/*
* (1)構造器私有化
* (2)不自行創建,只定義一個靜態變量
* (3)提供一個靜態方法,獲取實例對象
*/
public class Singleton5 {
private static Singleton5 instance;
//這裏就不能public,因爲我們沒有給他創建對象,那麼直接這樣調用,可能是個空對象
private Singleton5() {
}
public static Singleton5 getInstance(){
//添加一個同步鎖,保證線程安全
//爲了保證線程安全得同時,提供效率,把對instance==null得判斷放在鎖得外面
if(instance==null) {
synchronized (Singleton5.class) {
if(instance==null) {
//測試當兩個線程,先後進入到這裏,休眠,那麼就會創建兩個實例,所以這個例子存在線程安全問題
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance = new Singleton5();
}
}
}
return instance;
}
}
同樣的測試線程安全,返回的是true,
3:靜態內部類
package singleton;
/*
* 在內部類加載和初始化時,才創建instance實例對象
* 靜態內部類不會隨着外部類得加載和初始化而初始化,它是要單獨去加載和初始化得
*因爲是在內部類加載和初始化時創建得,因此是線程安全的
*/
public class Singleton6 {
private Singleton6() {
}
private static class Inner {
private static Singleton6 instance = new Singleton6();
}
public static Singleton6 getInstance() {
return Inner.instance;
}
}
小結
如果是餓漢式,枚舉形式最簡單
如果是懶漢式,靜態內部類形式最簡單