單例模式定義:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
實現手段:將構造方法私有化,在類外不能創建對象。只能通過類內的靜態方法創建對象。第一次訪問這個靜態方法時,創建一個對象,並將對象的引用返回;第二次訪問這個對象時直接將引用返回。
code…
單例類:
public class Singleton {
private static Singleton singleton=null;
private Singleton(){}
public static Singleton createObject()
{
if(singleton==null)
singleton=new Singleton();
return singleton;
}
}
main方法:
public class Main {
public static void main(String[] args) {
Singleton single=Singleton.createObject();
System.out.println(single);
Singleton single1=Singleton.createObject();
System.out.println(single1);
}
}
運行結果:兩個對象hashcode一樣
com.Singleton.Singleton@74a14482
com.Singleton.Singleton@74a14482
先將單例類內的引用懸空,每次訪問靜態方法時先判斷引用是否爲空,如果爲空則表明時第一次訪問,創建一個對象讓引用指向並返回;如果不爲空,則表明不是第一次訪問,直接將引用返回即可。
缺點:在多線程中,如果多個線程同時訪問這個靜態方法,如果引用爲null的判斷都成立,那麼每個線程都會創建一個實例。如下:
public class Main {
static class thread implements Runnable
{
@Override public void run() {
Singleton singleton=Singleton.createObject();
System.out.println(singleton);
}
}
public static void main(String[] args) {
Thread thread1=new Thread(new thread());
Thread thread2=new Thread(new thread());
Thread thread3=new Thread(new thread());
Thread thread4=new Thread(new thread());
Thread thread5=new Thread(new thread());
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
}
由於程序運行較快,難以模擬多個線程同時啓動,因此在if條件中使線程休眠1s:
if(singleton==null)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton=new Singleton();
}
運行結果出現五個不用的對象,單例模式被破壞。
com.Singleton.Singleton@60cb300b
com.Singleton.Singleton@dbefca
com.Singleton.Singleton@63158364
com.Singleton.Singleton@e4c242a
com.Singleton.Singleton@6fec3797
解決方法:
- 對方法進行同步,用synchronized來修飾方法。
- 在靜態塊中創建對象,並賦值給引用
public class Singleton {
private static Singleton singleton=null;
static {
singleton=new Singleton();
}
private Singleton(){}
public static Singleton createObject()
{
return singleton;
}
}
因爲類只加載一次,對應對象創建一次。所以在靜態塊中創建對象。
破解方法:可以通過反射來破解。無論用什麼方法寫的單例模式,都可以通過反射來破解。
反射中最簡單的破解方法,得到單例的Class對象,得到構造方法,然後實例化。
public class Main {
public static void main(String[] args)throws Exception{
Class single=Class.forName("com.Singleton.Singleton");
Constructor constructor=single.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton1=(Singleton) constructor.newInstance();
Singleton singleton2=(Singleton) constructor.newInstance();
Singleton singleton3=(Singleton) constructor.newInstance();
Singleton singleton4=(Singleton) constructor.newInstance();
System.out.println(singleton1);
System.out.println(singleton2);
System.out.println(singleton3);
System.out.println(singleton4);
}
}
這樣得到四個不同的對象。
上一篇 |
---The End---
|
下一篇 |