设计模式——单例模式

       单例模式

这是写的第一个设计模式在讲之前,先介绍一下该如何学习设计模式!掌握住方法之后学习别的设计模式就会很容易。 结合我个人的经验,给出一下学习设计模式的建议:  

  1. 首先要调整好心态,不要指望一蹴而就,不可浮躁。  
  2. 不要指望真正的设计模式的书籍是既简单又有趣的,一看就懂的。  
  3. 自己多去总结。
  4. 理论指导实践,实践反过来加深对理论的理解,如此反复循环,呈螺旋式上升。

 这个图那是我多年前学习HeadFirst设计模式时的海报(强烈推荐大家看一下这本说,老外写的浅显易懂!)大家也可以像我一样总结出一个海报贴到自己书房中反复去看。

  114630_fDor_2505908.png

       讲完这些之后我们开始学习第一个设计模式——单例,我们从定义、结构、使用场景、思考单例入手。

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

结构

115407_Olt1_2505908.png

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

   /**
    * 从时间和空间角度分析:空间换时间
    * 从线程安全角度分析:线程安全
    */
}

饿汉式调用顺序示意图:

130337_SUyB_2505908.png

    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方法都可以去掉,这些跟单例没有关系它们只是用于测试。

懒汉式调用顺序示意图:

130306_H396_2505908.png

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

思考单例模式

 单例模式的本质是:控制实例数目

如果我们想控制实例变量,可以仿照单例去实现,单例只不过是先例了一次而已。

 

转载于:https://my.oschina.net/u/2505908/blog/1477046

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值