這是寫的第一個設計模式在講以前,先介紹一下該如何學習設計模式!掌握住方法以後學習別的設計模式就會很容易。 結合我我的的經驗,給出一下學習設計模式的建議: java
這個圖那是我多年前學習HeadFirst設計模式時的海報(強烈推薦你們看一下這本說,老外寫的淺顯易懂!)你們也能夠像我同樣總結出一個海報貼到本身書房中反覆去看。web
講完這些以後咱們開始學習第一個設計模式——單例,咱們從定義、結構、使用場景、思考單例入手。spring
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。apache
Singleton: 負責建立Singleton類本身的惟一實例,並提供一個getInstance的方法,讓外部來訪問這個類的惟一實例。編程
在一個系統運行期間,咱們要求某個類只須要實例一次,此時就能夠了,eg: 對配置文件、封裝MySQL數據源、HttpClient客戶端的封裝等。設計模式
/** * * @Description:餓漢式 * @author:俠客人生 * @date:2017-4-18 上午7:18:56 * @version:V1.0 * @Copyright:2017 俠客人生 Inc. All rights reserved. */ public class Singleton { //3主動建立靜態變量來存儲實例 private static Singleton instance = new Singleton(); //1.先私有構造方法 private Singleton(){ } //2.提供一個全局訪問點 public static Singleton getInstance(){ //4.直接返回已經建立好的實例 return instance; } /** * 從時間和空間角度分析:空間換時間 * 從線程安全角度分析:線程安全 */ }
餓漢式調用順序示意圖:緩存
import java.util.concurrent.atomic.AtomicInteger; /** * * @Description:懶漢式 * @author:俠客人生 * @date:2017-4-18 上午7:18:56 * @version:V1.0 * @Copyright:2017 俠客人生 Inc. All rights reserved. */ public class Singleton implements Serializable { //3 建立變量來存儲實例 /** * 經過volatile 讓instance具備可見性,但不能保證它具備原子性。 */ private static Singleton instance = null; // 定義變量記錄調用的次數 /** * AtomicInteger 原子操做,線程安全 */ private static AtomicInteger count = new AtomicInteger(0); //私有構造方法,好在內部控制建立實例的數目 private Singleton() { count.incrementAndGet(); } public void show(){ System.out.println("初始化實例次數:"+count); } /** * 經過這個單例問題你們必定要學會兩個編程思想 * 1) 延遲加載的思想 * 2) 緩存思想 * 咱們再開發過程當中,這兩個思想會在咱們的項目中常用,咱們能夠借鑑懶漢式去寫本身的緩存來提升性能 */ //2.提供一個全局訪問點 //避免先生雞仍是先有蛋的問題,咱們static 讓其變成類級方法 public static Singleton getInstance(){ //4 判斷咱們instance 是否爲空 B if(instance == null){ instance = new Singleton(); } //4.1 直接返回已經建立好的實例 return instance; } /** * 從時間和空間角度分析:時間 換 空間 * 從線程安全角度分析:線程不安全 */ }
定義變量記錄調用的次數和show方法均可以去掉,這些跟單例沒有關係它們只是用於測試。安全
懶漢式調用順序示意圖:服務器
import java.util.concurrent.atomic.AtomicInteger; /** * * @Description:懶漢式 * @author:俠客人生 * @date:2017-4-18 上午7:18:56 * @version:V1.0 * @Copyright:2017 俠客人生 Inc. All rights reserved. */ public class Singleton implements Serializable { //3 建立變量來存儲實例 /** * 經過volatile 讓instance具備可見性,但不能保證它具備原子性。 */ private static volatile Singleton instance = null; // 定義變量記錄調用的次數 /** * AtomicInteger 原子操做,線程安全 */ private static AtomicInteger count = new AtomicInteger(0); //1.私有構造方法,好在內部控制建立實例的數目 private Singleton() { count.incrementAndGet(); } public void show(){ System.out.println("初始化實例次數:"+count); } /** * 經過這個單例問題你們必定要學會兩個編程思想 * 1) 延遲加載的思想 * 2) 緩存思想 * 咱們再開發過程當中,這兩個思想會在咱們的項目中常用,咱們能夠借鑑懶漢式去寫本身的緩存來提升性能 */ //2.提供一個全局訪問點 //避免先生雞仍是先有蛋的問題,咱們static 讓其變成類級方法 public static Singleton getInstance(){ //4 判斷咱們instance 是否爲空 B if(instance == null){ /** * synchronized 保證其操做的原子性 */ synchronized (Singleton.class) { if(instance==null){ //4.1 直到須要用我纔去建立 A B instance = new Singleton(); } } } //4.1 直接返回已經建立好的實例 return instance; } /** * 從時間和空間角度分析:時間 換 空間 * 從線程安全角度分析:線程不安全 */ }
對於該方法的講解咱們能夠看註釋,按照步驟去看,咱們解決線程安全使用了雙重檢查加鎖。多線程
所謂雙重檢查加鎖機制,指的是:並非每次進入getInstance方法都須要同步,而是先不一樣步,進入方法事後,先檢查實例是否存在,若是不存在才進入下面的同步塊,這是第一重檢查。進入同步塊事後,再次檢查實例是否存在,若是不存在,就在同步的狀況下建立一個實例,這是第二重檢查。這樣一來,就只須要同步一次了,從而減小了屢次在同步狀況下進行判斷所浪費的時間。 雙重檢查加鎖機制的實現會使用一個關鍵字 ,它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,全部對該變量的讀寫都是直接操做共享內存,從而確保多個線程能正確的處理該變量。
對於懶漢式或者餓漢式在真正開發中都不會使用。但懶漢體現了兩個思想你們必定要學會。
1) 延遲加載的思想
通俗點說,就是一開始不要加載資源或者數據,一直等到立刻就要使用這個資源或者數據了,躲不過去了才加載,因此也稱Lazy Load,不是懶惰啊,是「延遲加載」,這在實際開發中是一種很常見的思想,儘量的節約資源。
2) 緩存思想
若是某些資源或者數據會被頻繁的使用,能夠把這些數據緩存到內存裏面,每次操做的時候,先到內存裏面找,看有沒有這些數據,若是有,那麼就直接使用,若是沒有那麼就獲取它,並設置到緩存中,下一次訪問的時候就能夠直接從內存中獲取了。從而節省大量的時間,固然,緩存是一種典型的空間換時間的方案。
咱們再開發過程當中,這兩個思想會在咱們的項目中常用,咱們能夠借鑑懶漢式去寫本身的緩存來提升性能 。
/** * * @Description:靜態內部類實現單例 * @author: 俠客人生 * @date:2017-4-18 上午7:18:56 * @version:V1.0 * @Copyright:2017 俠客人生 Inc. All rights reserved. */ public class Singleton{ //跟外部類沒有綁定關係, private static class SingletonHolder{ //3主動建立靜態變量來存儲實例 jvm //只有調用時候才實現 延遲加載 private static Singleton instance = new Singleton(); } //1.先私有構造方法 private Singleton(){ } //2.提供一個全局訪問點 public static Singleton getInstance(){ //4.直接返回已經建立好的實例 return SingletonHolder.instance; } /** * 從時間和空間角度分析:空間換時間 * 從線程安全角度分析:線程安全 */ }
想深刻了解靜態內部類爲何能達到單例效果的朋友,能夠關注我博客,後期會將虛擬機知識,到時候再具體講。
/** * * @Description:枚舉實現單例 * @author:俠客人生 * @date:2017-5-16 下午3:09:11 * @version:V1.0 * @Copyright:2017 俠客人生 Inc. All rights reserved. */
public enum Singleton{ /*enum Color { RED, BLUE, GREEN; }*/ /** * public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable * * enum Color {RED, BLUE, GREEN} * 編譯器將會把他轉成以下內容: * * public final class Color extends Enum<Color> { * public static final Color[] values() { return (Color[])$VALUES.clone(); } * public static Color valueOf(String name) { ... } * * private Color(String s, int i) { super(s, i); } * * public static final Color RED; * public static final Color BLUE; * public static final Color GREEN; * * private static final Color $VALUES[]; * * static { * RED = new Color("RED", 0); * BLUE = new Color("BLUE", 1); * GREEN = new Color("GREEN", 2); * $VALUES = (new Color[] { RED, BLUE, GREEN }); * } * } */ uniqueInstance; private int i =5; public void print(){ System.out.println("枚舉實現了單例 i:" + i++); } /** * 本類編譯 * private Singleton(String s, int i) { super(s, i); } * public static final Singleton unionSingleton; * static{ * unionSingleton = new Singleton(s,i); * } * */ }
--------------------------------------------------------------------------------------------------------- 去掉註釋後編譯後的字節碼: Compiled from "Singleton.java" public final class com.jhaso.dp.singleton.singleton.example6.Singleton extends java.lang.Enum<com.bawei.m1507.dp.singleton.singleton.example6.Singleton> { public static final com.jhaso.dp.singleton.singleton.example6.Singleton uniqueInstance; public static com.jhaso.dp.singleton.singleton.example6.Singleton[] values(); Code: 0: getstatic #1 // Field $VALUES:[Lcom/jhaso/dp/singleton/singleton/example6/Singleton; 3: invokevirtual #2 // Method "[Lcom/jhaso/dp/singleton/singleton/example6/Singleton;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[Lcom/jhaso/dp/singleton/singleton/example6/Singleton;" 9: areturn public static com.jhaso.dp.singleton.singleton.example6.Singleton valueOf(java.lang.String); Code: 0: ldc #4 // class com/jhaso/dp/singleton/singleton/example6/Singleton 2: aload_0 3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 6: checkcast #4 // class com/jhaso/dp/singleton/singleton/example6/Singleton 9: areturn public void print(); Code: 0: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #9 // class java/lang/StringBuilder 6: dup 7: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V 10: ldc #11 // String 枚舉實現了單例 i: 12: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_0 16: dup 17: getfield #7 // Field i:I 20: dup_x1 21: iconst_1 22: iadd 23: putfield #7 // Field i:I 26: invokevirtual #13 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 29: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 35: return static {}; Code: 0: new #4 // class com/jhaso/dp/singleton/singleton/example6/Singleton 3: dup 4: ldc #16 // String uniqueInstance 6: iconst_0 7: invokespecial #17 // Method "<init>":(Ljava/lang/String;I)V 10: putstatic #18 // Field uniqueInstance:Lcom/jhaso/dp/singleton/singleton/example6/Singleton; 13: iconst_1 14: anewarray #4 // class com/jhaso/dp/singleton/singleton/example6/Singleton 17: dup 18: iconst_0 19: getstatic #18 // Field uniqueInstance:Lcom/jhaso/dp/singleton/singleton/example6/Singleton; 22: aastore 23: putstatic #1 // Field $VALUES:[Lcom/jhaso/dp/singleton/singleton/example6/Singleton; 26: return }
你們重點看編譯後的紅色部分,這些不正是單例的結構嗎?
枚舉的做用:
對於枚舉不太熟悉的朋友能夠看一下枚舉的知識,更多枚舉的使用方法請參看Java編程入門資料和《Effective Java中文版 第2版》。
AppConfig .java
import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class AppConfig { private String courseTeacher; private String courseName; //跟外部類沒有綁定關係, private static class SingletonHolder{ //3主動建立靜態變量來存儲實例 jvm //只有調用時候才實現 延遲加載 private static final AppConfig instance = new AppConfig(); } private AppConfig() { readConfig(); } public static AppConfig getInstance(){ return SingletonHolder.instance; } private void readConfig() { Properties p = new Properties(); InputStream in = null; try { System.out.println("---------加載配置文件-----------"); in = AppConfig.class.getResourceAsStream("AppConfig.properties"); p.load(in); this.courseName = p.getProperty("courseName"); this.courseTeacher = p.getProperty("courseTeacher"); } catch (IOException e) { System.out.println("---------加載配置文件失敗錯誤信息以下:"); e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } public String getCourseTeacher() { return courseTeacher; } public String getCourseName() { return courseName; } }
AppConfig.properties
courseTeacher=\u738B\u5E05\u5175 courseName=\u8BBE\u8BA1\u6A21\u5F0F\u2014\u2014\u5355\u4F8B\u6A21\u5F0F
Client.java
public class Client { /** * @Title:main * @Description: TODO(這裏用一句話描述這個方法的做用) * @param:@param args * @return:void * @throws */ public static void main(String[] args) { for (int i = 0; i < 5; i++) { AppConfig app = AppConfig.getInstance(); String teacher = app.getCourseTeacher(); String name = app.getCourseName(); System.out.println("teacher:"+teacher+" name:"+name); } } }
UploadUtil.java(該類需與枚舉實戰二配合使用)
import com.bawei.exception.MyException; import fm.last.moji.MojiFile; import fm.last.moji.spring.SpringMojiBean; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartFile; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class UploadUtil { private static final Log log = LogFactory.getLog(UploadUtil.class); private UploadUtil(){} private static class UploadUtilSingles{ private static UploadUtil uploadUtil = new UploadUtil(); } public static UploadUtil getInstace(){ return UploadUtilSingles.uploadUtil; } private SpringMojiBean moji = MogileFSTool.mogileFSTool.getMoji(); /** * 批量上傳附件 * sss.doc * @param request * @param response */ public String UploadDocument(HttpServletRequest request, HttpServletResponse response) { // 保存文件 String fileName = ""; String myFileName = ""; String relativePath = ""; CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver( request.getSession().getServletContext()); String path = ""; // 判斷 request 是否有文件上傳,即多部分請求 if (multipartResolver.isMultipart(request)) { // 轉換成多部分request MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; // 取得request中的全部文件名 Iterator<String> iter = multiRequest.getFileNames(); while (iter.hasNext()) { // 記錄上傳過程起始時的時間,用來計算上傳時間 int pre = (int) System.currentTimeMillis(); // 取得上傳文件 MultipartFile file = multiRequest.getFile(iter.next()); if (file != null) { // 取得當前上傳文件的文件名稱 myFileName = file.getOriginalFilename(); // 若是名稱不爲「」,說明該文件存在,不然說明該文件不存在 if (myFileName.trim() != "") { // 重命名上傳後的文件名 fileName = file.getOriginalFilename().substring(file.getOriginalFilename().indexOf(".")); path = saveFile(file, fileName); } } } } if (path.equals("")) { return ""; } else { return fileName; } } public static boolean uploadCommon(String path, String fileName, MultipartFile file) { try { File targetFile = new File(path, fileName); if (!targetFile.exists()) { targetFile.mkdirs(); } else { targetFile.delete(); } file.transferTo(targetFile); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * mogileFS文件上傳通用方法 * * @param files springMVC控制器接收的文件參數 * @param fileKey */ public String saveFile(MultipartFile file, String fileKey) { String resultFileUrl = ""; try { //上傳文件,並返回長傳後的文件URL信息 resultFileUrl = uploadToMogilefs(fileKey, file); log.info("mogileFs返回地址:" + resultFileUrl); } catch (Exception e) { log.error("上傳文件失敗:" + e); return ""; } return resultFileUrl; } /** * 顯示圖片 * * @param fileKey */ public String loadImage(String fileKey) { MojiFile mojiFile = moji.getFile(fileKey); String urlStr = ""; try { List<URL> urls = mojiFile.getPaths(); if (urls != null && urls.size() > 0) { urlStr = urls.get(0).toString(); } } catch (IOException e) { return ""; } return urlStr; } /** * 刪除文件 * * @param fileKey */ public int deleteFileByKey(String fileKey) { MojiFile mojiFile = moji.getFile(fileKey); try { mojiFile.delete(); return 1;// 刪除成功 } catch (IOException e) { return 0;// 刪除失敗 } } /** * 從MogileFS下載文件 * @param fileKey MogileFS 文件的Key */ public void download(String fileKey, HttpServletResponse response) { String remoteFilePath = loadImage(fileKey); URL urlfile = null; HttpURLConnection httpUrl = null; BufferedInputStream bis = null; BufferedOutputStream bos = null; try { urlfile = new URL(remoteFilePath); httpUrl = (HttpURLConnection) urlfile.openConnection(); httpUrl.connect(); bis = new BufferedInputStream(httpUrl.getInputStream()); response.setContentType("APPLICATION/DOWNLOAD"); response.setHeader("Content-Disposition", "attachment; filename=" + fileKey); bos = new BufferedOutputStream(response.getOutputStream()); int len = 2048; byte[] b = new byte[len]; while ((len = bis.read(b)) != -1) { bos.write(b, 0, len); } bos.flush(); bis.close(); httpUrl.disconnect(); } catch (Exception e) { e.printStackTrace(); } finally { try { bis.close(); bos.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 上傳文件,並返回長傳後的文件URL信息 * * @param fileKey 地址(在mogile服務器上爲KEY) * @param file * @return 未找到文件路徑返回空串,找到則返回地址 * @throws IOException * @throws Exception */ private String uploadToMogilefs(String fileKey, MultipartFile file) { MojiFile mf = moji.getFile(fileKey); String mogileUrl = ""; //將springmvc接收的multipartfile轉換爲file File winFile = changeMultipartFileToFile(fileKey, file); try { if(winFile.exists()){ moji.copyToMogile(winFile, mf); mf.rename(fileKey); List<URL> urls = mf.getPaths(); if (urls != null && urls.size() > 0) { mogileUrl = urls.get(0).toString(); } }else { throw new MyException("沒法將springmvc接收的multipartfile轉換爲臨時file,請更換一張圖片\n"); } } catch (Exception e) { log.debug("MogileFS異常:", e); return ""; }finally { return mogileUrl; } } /** * 將springmvc接收的multipartfile轉換爲file * @param file springmvc接收的multipartfile * @param fileKey * @return */ private static File changeMultipartFileToFile(String fileKey, MultipartFile file) { CommonsMultipartFile cf = (CommonsMultipartFile) file; DiskFileItem fi = (DiskFileItem) cf.getFileItem(); File winFile = fi.getStoreLocation(); long winFileLength = 0L; // 判斷是否爲圖片Pic try { if (winFile.exists()) { BufferedImage bi = ImageIO.read(winFile); if (bi != null) { winFileLength = winFile.length(); if (winFileLength > UploadFilePath.IMG_SIZE) { // 大小超過800k,則進行壓縮. winFile = ImageUtil.resize(fileKey, winFile, UploadFilePath.IMG_HEIGHT, UploadFilePath.IMG_WIDTH); } } } } catch (IOException e) { e.printStackTrace(); } return winFile; } /** * 將文件批量打成zip包 並下載 * * @param fileKeys * @param request * @param response */ public void downLoadFiles(List<String> fileKeys, HttpServletRequest request, HttpServletResponse response) { try { /** * 建立一個臨時壓縮文件, 咱們會把文件流所有注入到這個文件中 這裏的文件你能夠自定義是.rar仍是.zip */ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); String fileName = sdf.format(new Date()); String tempPath = "D/:\\" + fileName + ".zip"; File file = new File(tempPath); if (!file.exists()) { file.createNewFile(); } // response.reset(); // response.getWriter() // 建立文件輸出流 FileOutputStream fous = new FileOutputStream(file); /** * 打包的方法咱們會用到ZipOutputStream這樣一個輸出流, 因此這裏咱們把輸出流轉換一下 */ ZipOutputStream zipOut = new ZipOutputStream(fous); /** * 這個方法接受的就是一個所要打包文件的集合, 還有一個ZipOutputStream */ zipFile(fileKeys, zipOut); zipOut.close(); fous.close(); downloadZip(file, response); } catch (Exception e) { e.printStackTrace(); } /** * 直到文件的打包已經成功了, 文件的打包過程被我封裝在FileUtil.zipFile這個靜態方法中, * 稍後會呈現出來,接下來的就是往客戶端寫數據了 */ } /** * 把接受的所有文件打成壓縮包 * * @param List<File>; * @param org.apache.tools.zip.ZipOutputStream */ public void zipFile(List<String> fileKeys, ZipOutputStream outputStream) { int size = fileKeys.size(); for (int i = 0; i < size; i++) { String fileKey = fileKeys.get(i); zipFile(fileKey, outputStream); } } public void downloadZip(File file, HttpServletResponse response) { try { // 以流的形式下載文件。 InputStream fis = new BufferedInputStream(new FileInputStream(file.getPath())); byte[] buffer = new byte[fis.available()]; fis.read(buffer); fis.close(); // 清空response response.reset(); OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); response.setContentType("application/octet-stream"); // 若是輸出的是中文名的文件,在此處就要用URLEncoder.encode方法進行處理 response.setHeader("Content-Disposition", "attachment;filename=" + file.getName()); toClient.write(buffer); toClient.flush(); toClient.close(); } catch (IOException ex) { ex.printStackTrace(); } finally { try { File f = new File(file.getPath()); f.delete(); } catch (Exception e) { e.printStackTrace(); } } } /** * 根據輸入的文件與輸出流對文件進行打包 * * @param File * @param org.apache.tools.zip.ZipOutputStream */ public void zipFile(String fileKey, ZipOutputStream ouputStream) { try { String remoteFilePath = loadImage(fileKey); URL urlfile = new URL(remoteFilePath); HttpURLConnection httpUrl = (HttpURLConnection) urlfile.openConnection(); httpUrl.connect(); BufferedInputStream bins = new BufferedInputStream(httpUrl.getInputStream()); // org.apache.tools.zip.ZipEntry ZipEntry entry = new ZipEntry(fileKey); ouputStream.putNextEntry(entry); // 向壓縮文件中輸出數據 int nNumber; byte[] buffer = new byte[512]; while ((nNumber = bins.read(buffer)) != -1) { ouputStream.write(buffer, 0, nNumber); } // 關閉建立的流對象 bins.close(); httpUrl.disconnect(); } catch (Exception e) { e.printStackTrace(); } } }
AppConfig.java
import java.io.IOException; import java.io.InputStream; import java.util.Properties; public enum AppConfig { getInstance; private static String courseTeacher; private static String courseName; public String getCourseTeacher() { return courseTeacher; } public String getCourseName() { return courseName; } static{ Properties p = new Properties(); InputStream in = null; try { //System.out.println("---------加載配置文件-----------"); in = AppConfig.class.getResourceAsStream("AppConfig.properties"); p.load(in); courseName = p.getProperty("courseName"); courseTeacher = p.getProperty("courseTeacher"); } catch (IOException e) { //System.out.println("---------加載配置文件失敗錯誤信息以下:"); e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }
AppConfig.properties
courseTeacher=\u738B\u5E05\u5175 courseName=\u8BBE\u8BA1\u6A21\u5F0F\u2014\u2014\u5355\u4F8B\u6A21\u5F0F
Client.java
public class Client { /** * @Title:main * @Description: TODO(這裏用一句話描述這個方法的做用) * @param:@param args * @return:void * @throws */ public static void main(String[] args) { for (int i = 0; i < 3; i++) { AppConfig app = AppConfig.getInstance; String teacher = app.getCourseTeacher(); String name = app.getCourseName(); System.out.println("app的HashCode:"+app.hashCode()+"teacher:"+teacher+" name:"+name); } } }
MogileFSTool.java
import fm.last.moji.spring.SpringMojiBean; public enum MogileFSTool { mogileFSTool; private static SpringMojiBean moji; public static SpringMojiBean getMoji() { return moji; } static { moji = new SpringMojiBean(); moji.setAddressesCsv(UploadFilePath.MGL_SEVICE); moji.setDomain("file"); moji.initialise(); moji.setTestOnBorrow(true); } }
單例模式的本質是:控制實例數目
若是咱們想控制實例變量,能夠仿照單例去實現,單例只不過是先例了一次而已。