这是写的第一个设计模式在讲以前,先介绍一下该如何学习设计模式!掌握住方法以后学习别的设计模式就会很容易。 结合我我的的经验,给出一下学习设计模式的建议: 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); } }
单例模式的本质是:控制实例数目
若是咱们想控制实例变量,能够仿照单例去实现,单例只不过是先例了一次而已。