我從我的博客里把我的文章粘貼過來吧,對于單例模式模式應(yīng)該有比較清楚的解釋:
專注于為中小企業(yè)提供成都網(wǎng)站建設(shè)、網(wǎng)站制作服務(wù),電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)深澤免費做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了成百上千企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴充和轉(zhuǎn)變。
單例模式在我們?nèi)粘5捻椖恐惺殖R?,?dāng)我們在項目中需要一個這樣的一個對象,這個對象在內(nèi)存中只能有一個實例,這時我們就需要用到單例。
一般說來,單例模式通常有以下幾種:
1.饑漢式單例
public class Singleton {
private Singleton(){};
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
這是最簡單的單例,這種單例最常見,也很可靠!它有個唯一的缺點就是無法完成延遲加載——即當(dāng)系統(tǒng)還沒有用到此單例時,單例就會被加載到內(nèi)存中。
在這里我們可以做個這樣的測試:
將上述代碼修改為:
public class Singleton {
private Singleton(){
System.out.println("createSingleton");
};
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
public static void testSingleton(){
System.out.println("CreateString");
}
}
而我們在另外一個測試類中對它進行測試(本例所有測試都通過Junit進行測試)
public class TestSingleton {
@Test
public void test(){
Singleton.testSingleton();
}
}
輸出結(jié)果:
createSingleton
CreateString
我們可以注意到,在這個單例中,即使我們沒有使用單例類,它還是被創(chuàng)建出來了,這當(dāng)然是我們所不愿意看到的,所以也就有了以下一種單例。
2.懶漢式單例
public class Singleton1 {
private Singleton1(){
System.out.println("createSingleton");
}
private static Singleton1 instance = null;
public static synchronized Singleton1 getInstance(){
return instance==null?new Singleton1():instance;
}
public static void testSingleton(){
System.out.println("CreateString");
}
}
上面的單例獲取實例時,是需要加上同步的,如果不加上同步,在多線程的環(huán)境中,當(dāng)線程1完成新建單例操作,而在完成賦值操作之前,線程2就可能判
斷instance為空,此時,線程2也將啟動新建單例的操作,那么多個就出現(xiàn)了多個實例被新建,也就違反了我們使用單例模式的初衷了。
我們在這里也通過一個測試類,對它進行測試,最后面輸出是
CreateString
可以看出,在未使用到單例類時,單例類并不會加載到內(nèi)存中,只有我們需要使用到他的時候,才會進行實例化。
這種單例解決了單例的延遲加載,但是由于引入了同步的關(guān)鍵字,因此在多線程的環(huán)境下,所需的消耗的時間要遠遠大于第一種單例。我們可以通過一段測試代碼來說明這個問題。
public class TestSingleton {
@Test
public void test(){
long beginTime1 = System.currentTimeMillis();
for(int i=0;i100000;i++){
Singleton.getInstance();
}
System.out.println("單例1花費時間:"+(System.currentTimeMillis()-beginTime1));
long beginTime2 = System.currentTimeMillis();
for(int i=0;i100000;i++){
Singleton1.getInstance();
}
System.out.println("單例2花費時間:"+(System.currentTimeMillis()-beginTime2));
}
}
最后輸出的是:
單例1花費時間:0
單例2花費時間:10
可以看到,使用第一種單例耗時0ms,第二種單例耗時10ms,性能上存在明顯的差異。為了使用延遲加載的功能,而導(dǎo)致單例的性能上存在明顯差異,
是不是會得不償失呢?是否可以找到一種更好的解決的辦法呢?既可以解決延遲加載,又不至于性能損耗過多,所以,也就有了第三種單例:
3.內(nèi)部類托管單例
public class Singleton2 {
private Singleton2(){}
private static class SingletonHolder{
private static Singleton2 instance=new Singleton2();
}
private static Singleton2 getInstance(){
return SingletonHolder.instance;
}
}
在這個單例中,我們通過靜態(tài)內(nèi)部類來托管單例,當(dāng)這個單例被加載時,不會初始化單例類,只有當(dāng)getInstance方法被調(diào)用的時候,才會去加載
SingletonHolder,從而才會去初始化instance。并且,單例的加載是在內(nèi)部類的加載的時候完成的,所以天生對線程友好,而且也不需要
synchnoized關(guān)鍵字,可以說是兼具了以上的兩個優(yōu)點。
4.總結(jié)
一般來說,上述的單例已經(jīng)基本可以保證在一個系統(tǒng)中只會存在一個實例了,但是,仍然可能會有其他的情況,導(dǎo)致系統(tǒng)生成多個單例,請看以下情況:
public class Singleton3 implements Serializable{
private Singleton3(){}
private static class SingletonHolder{
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
}
通過一段代碼來測試:
@Test
public void test() throws Exception{
Singleton3 s1 = null;
Singleton3 s2 = Singleton3.getInstance();
//1.將實例串行話到文件
FileOutputStream fos = new FileOutputStream("singleton.txt");
ObjectOutputStream oos =new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
//2.從文件中讀取出單例
FileInputStream fis = new FileInputStream("singleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (Singleton3) ois.readObject();
if(s1==s2){
System.out.println("同一個實例");
}else{
System.out.println("不是同一個實例");
}
}
輸出:
不是同一個實例
可以看到當(dāng)我們把單例反序列化后,生成了多個不同的單例類,此時,我們必須在原來的代碼中加入readResolve()函數(shù),來阻止它生成新的單例
public class Singleton3 implements Serializable{
private Singleton3(){}
private static class SingletonHolder{
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
//阻止生成新的實例
public Object readResolve(){
return SingletonHolder.instance;
}
}
再次測試時,就可以發(fā)現(xiàn)他們生成的是同一個實例了。
代碼已寫好,希望對你有幫助,順便求好評。
package?architecture;
import?java.io.File;
import?java.io.FileNotFoundException;
import?java.io.FileReader;
import?java.io.FileWriter;
import?java.io.IOException;
/**
*?
*?@author?Wang?Zhenhui
*?@blog?
*
*/
public?class?AboutIOReaderWriter?{
/**
?*?main
?*?@param?args
?*/
public?static?void?main(String[]?args)?{
String?filePath?=?"d:\\out2.txt";
write(filePath);
read(filePath);
}
/**
?*?Read
?*?@param?filePath?file?path
?*/
public?static?void?read(String?filePath)?{
FileReader?fr?=?null;
StringBuilder?result?=?new?StringBuilder();
;
try?{
fr?=?new?FileReader(new?File(filePath));
char[]?charArray?=?new?char[1024];
while?(fr.read(charArray)?!=?-1)?{
result.append(charArray);
}
}?catch?(FileNotFoundException?e)?{
e.printStackTrace();
}?catch?(IOException?e)?{
//?TODO?Auto-generated?catch?block
e.printStackTrace();
}
if?(fr?!=?null)?{
try?{
fr.close();
}?catch?(IOException?e)?{
e.printStackTrace();
}
}
System.out.println(result.toString());
}
/**
?*?Write
?*?@param?filePath?file?path
?*/
public?static?void?write(String?filePath)?{
FileWriter?fw?=?null;
try?{
fw?=?new?FileWriter(new?File(filePath));
for?(int?i?=?1;?i?=?50;?i++)?{
fw.append(String.valueOf(i));
}
fw.flush();
}?catch?(IOException?e)?{
e.printStackTrace();
}
if?(fw?!=?null)?{
try?{
fw.close();
}?catch?(IOException?e)?{
e.printStackTrace();
}
}
}
}
importjava.awt.*;importjava.awt.event.*;classShopFrameextendsFrameimplementsActionListener{Labellabel1,label2,label3,label4;Buttonbutton1,button2,button3,button4,button5;TextAreatext;Panelpanel1,panel2;staticfloatsum=0.0f;ShopFrame(Strings){super(s);setLayout(newBorderLayout());label1=newLabel("面紙:3元",Label.LEFT);label2=newLabel("鋼筆:5元",Label.LEFT);label3=newLabel("書:10元",Label.LEFT);label4=newLabel("襪子:8元",Label.LEFT);button1=newButton("加入購物車");button2=newButton("加入購物車");button3=newButton("加入購物車");button4=newButton("加入購物車");button5=newButton("查看購物車");text=newTextArea("商品有:"+"\n",5,10);text.setEditable(false);addWindowListener(newWindowAdapter(){publicvoidwindowClosing(WindowEvente){System.exit(0);}});button1.addActionListener(this);button2.addActionListener(this);button3.addActionListener(this);button4.addActionListener(this);button5.addActionListener(this);panel1=newPanel();panel2=newPanel();panel1.add(label1);panel1.add(button1);panel1.add(label2);panel1.add(button2);panel1.add(label3);panel1.add(button3);panel1.add(label4);panel1.add(button4);panel2.setLayout(newBorderLayout());panel2.add(button5,BorderLayout.NORTH);panel2.add(text,BorderLayout.SOUTH);this.add(panel1,BorderLayout.CENTER);this.add(panel2,BorderLayout.SOUTH);setBounds(100,100,350,250);setVisible(true);validate();}publicvoidactionPerformed(ActionEvente){if(e.getSource()==button1){text.append("一個面紙、");sum=sum+3;}elseif(e.getSource()==button2){text.append("一只鋼筆、");sum=sum+5;}elseif(e.getSource()==button3){text.append("一本書、");sum=sum+10;}elseif(e.getSource()==button4){text.append("一雙襪子、");sum=sum+8;}elseif(e.getSource()==button5){text.append("\n"+"總價為:"+"\n"+sum);}}}publicclassShopping{publicstaticvoidmain(String[]args){newShopFrame("購物車");}}我沒用Swing可能顯示不出來你的效果。不滿意得話我在給你編一個。
package test;
public class Student {
private String name;
private String id;
private String clazz;
private int age;
private String address;
/**
* sayHello方法
*/
public void sayHello() {
System.out.println("學(xué)號為" + this.id + "的同學(xué)的具體信息如下:");
System.out.println("姓名:" + this.name);
System.out.println("班級:" + this.clazz);
System.out.println("年齡:" + this.age);
System.out.println("家庭住址:" + this.address);
}
/**
* 測試方法
*
* @param args
*/
public static void main(String[] args) {
// 第一問
Student student = new Student();
student.setAddress("百度知道");
student.setAge(1);
student.setClazz("一班");
student.setId("071251000");
student.setName("lsy605604013");
student.sayHello();
// 第二問
Student studentNew = new Student();
studentNew.setAddress("搜搜知道");
studentNew.setAge(2);
studentNew.setClazz("二班");
studentNew.setId("071251001");
studentNew.setName("lady");
if (student.getAge() studentNew.getAge())
studentNew.sayHello();
else if (student.getAge() studentNew.getAge())
student.sayHello();
else
System.out.println("兩人一樣大");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
代碼如下:
import java.util.ArrayList;
import java.util.List;
class Org {
private String id;
private String name;
private String pid;
public Org(String id, String name, String pid) {
this.id = id;
this.name = name;
this.pid = pid;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
@Override
public String toString() {
return "Org [id=" + id + ", name=" + name + ", pid=" + pid + "]";
}
}
public class App {
static void find(ListOrg list, String pid) {
list.stream().filter(p - p.getPid().equals(pid))
.forEach(org - {
System.out.println(org);
find(list, org.getId());
});
}
public static void main(String[] args) {
ListOrg list = new ArrayList();
list.add(new Org("111", "公司", "0"));
list.add(new Org("222", "部門", "111"));
list.add(new Org("333", "小組", "222"));
list.add(new Org("444", "員工1", "333"));
list.add(new Org("555", "員工2", "333"));
find(list, "0");
System.out.println("------------------------------------");
find(list, "111");
}
}
運行結(jié)果: