設計模式——單例模式

       單例模式

這是寫的第一個設計模式在講以前,先介紹一下該如何學習設計模式!掌握住方法以後學習別的設計模式就會很容易。 結合我我的的經驗,給出一下學習設計模式的建議:  java

  1. 首先要調整好心態,不要期望一蹴而就,不可浮躁。  
  2. 不要期望真正的設計模式的書籍是既簡單又有趣的,一看就懂的。  
  3. 本身多去總結。
  4. 理論指導實踐,實踐反過來加深對理論的理解,如此反覆循環,呈螺旋式上升。

 這個圖那是我多年前學習HeadFirst設計模式時的海報(強烈推薦你們看一下這本說,老外寫的淺顯易懂!)你們也能夠像我同樣總結出一個海報貼到本身書房中反覆去看。web

  

       講完這些以後咱們開始學習第一個設計模式——單例,咱們從定義、結構、使用場景、思考單例入手。spring

定義

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。apache

結構

Singleton:   負責建立Singleton類本身的惟一實例,並提供一個getInstance的方法,讓外部來訪問這個類的惟一實例。編程

場景問題

在一個系統運行期間,咱們要求某個類只須要實例一次,此時就能夠了,eg: 對配置文件、封裝MySQL數據源、HttpClient客戶端的封裝等。設計模式

代碼演示

    1)餓漢式**

/**
 * 
 * @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;
   }

   /**
    * 從時間和空間角度分析:空間換時間
    * 從線程安全角度分析:線程安全
    */
}

餓漢式調用順序示意圖:緩存

    2)懶漢式**

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方法均可以去掉,這些跟單例沒有關係它們只是用於測試。安全

懶漢式調用順序示意圖:服務器

    3)懶漢式——線程安全 ***

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) 緩存思想

        若是某些資源或者數據會被頻繁的使用,能夠把這些數據緩存到內存裏面,每次操做的時候,先到內存裏面找,看有沒有這些數據,若是有,那麼就直接使用,若是沒有那麼就獲取它,並設置到緩存中,下一次訪問的時候就能夠直接從內存中獲取了。從而節省大量的時間,固然,緩存是一種典型的空間換時間的方案。

咱們再開發過程當中,這兩個思想會在咱們的項目中常用,咱們能夠借鑑懶漢式去寫本身的緩存來提升性能 。

    4)靜態內部類****

    

/**
 * 
 * @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;
   }
   /**
    * 從時間和空間角度分析:空間換時間
    * 從線程安全角度分析:線程安全
    */
}

想深刻了解靜態內部類爲何能達到單例效果的朋友,能夠關注我博客,後期會將虛擬機知識,到時候再具體講。

    5)枚舉*****

/**
 * 
 * @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
}

你們重點看編譯後的紅色部分,這些不正是單例的結構嗎?

  枚舉的做用:  

  1. Java的枚舉類型實質上是功能齊全的類,所以能夠有本身的屬性和方法enum。  
  2. Java枚舉類型的基本思想:經過公有的靜態final域爲每一個枚舉常量導出實例的類。
  3. 抽象類不能被實例化,因此咱們在java程序中不能使用new關鍵字來聲明一個Enum。   
  4. 枚舉不只能避免多線程同步問題,並且還能防止反序列化從新建立新的對象。(想了解序列化與反序列化請查看【https://my.oschina.net/u/2505908/blog/edit】這篇文章)

    對於枚舉不太熟悉的朋友能夠看一下枚舉的知識,更多枚舉的使用方法請參看Java編程入門資料和《Effective Java中文版 第2版》。

    6)靜態內部類實戰*****

    實戰一:讀配置文件

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();
      }
   }
}

    7)枚舉實戰*****

    實戰一:讀配置文件

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

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);
   }
}

思考單例模式

 單例模式的本質是:控制實例數目

若是咱們想控制實例變量,能夠仿照單例去實現,單例只不過是先例了一次而已。

相關文章
相關標籤/搜索