单例模式的使用

jdk和Spring都有实现单例模式,这里举的例子是JDK中Runtime这个类

Runtime的使用

通过Runtime类可以获取JVM堆内存的信息,还可以调用它的方法进行GC。

public class Test {
public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime();
runtime.gc();
//jvm的堆内存总量
System.out.println("堆内存总量" + runtime.totalMemory()/1024/1024 + "MB");
//jvm视图使用的最大堆内存
System.out.println("最大堆内存" + runtime.maxMemory()/1024/1024 + "MB");
//jvm剩余可用的内存
System.out.println("可用的内存" +runtime.freeMemory()/1024/1024 + "MB"); Runtime runtime1 = Runtime.getRuntime(); System.out.println(runtime == runtime1);
}
}

这里创建了两个对象,通过等于号判断,两个引用来自同一个对象,确实是单例模式

Runtime的定义

这个类是介绍是:每一个Java应用有一个Runtime的实例,可以获取应用运行时的环境属性,当前的实例通过

getRuntime方法获取 。应用程序不能创建这个类的实例。

这差不多包含了单例类的定义,然后看一下这个类的内部实现

很明显是一个标准的单例模式的(饿汉)实现,首先使用static修饰实例对象,所以类加载的时候就会创建实例,然后调用方法返回这个实例,使用private修饰构造函数。

反射破坏单例模式

Runtime类将构造函数私有化,就是不想让人创建它的实例,但是我们却可以使用反射来创建对象

public class Test {
public static void main(String[] args) throws Exception { Class<?> clazz = Runtime.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object o1 = constructor.newInstance();
Object o2 = Runtime.getRuntime();
System.out.println(o1.getClass().getSimpleName());
System.out.println(o2.getClass().getSimpleName());
System.out.println(o1 == o2); }
}

通过运行结果可以看到,已经成功的创建了两个Runtime对象

至于破坏Runtime类的单例有什么坏处我也不知道,毕竟我是不会用反射去破坏它的,总之应该是有坏处的,下面看一下不能被反射破坏的单例模式实现

单例模式的实现

枚举类实现

使用枚举实现是因为JDK底层保护我们的枚举类不被反射,就解决了单例被反射破坏的问题

EnumSingleton.java

在枚举类中放了一个内部类(其实不放内部类也行)

public enum EnumSingleton {

    INSTANCE;
class MyRuntime{ public void hello(){
System.out.println("hello");
}
} private MyRuntime myRuntime; EnumSingleton(){
myRuntime = new MyRuntime();
} public MyRuntime getData(){
return myRuntime;
} public static EnumSingleton getInstance(){
return INSTANCE;
}
}

下面测试一下这个单例

public class Test {
public static void main(String[] args) throws Exception { EnumSingleton.MyRuntime myRuntime = EnumSingleton.INSTANCE.getData();
myRuntime.hello();
EnumSingleton.MyRuntime myRuntime1 = EnumSingleton.getInstance().getData();
System.out.println(myRuntime == myRuntime1);
}
}

结果显而易见,单例模式已经成功实现

至于使用反射测试枚举类,可以直接看一下JDK对枚举类的一个保护

使用反射创建对象,即调用Construct类的newInstance方法,这个方法里面已经定义了枚举对象不能被创建

使用枚举实现单例的坏处有

  • 因为很少使用枚举类,所以用枚举创建单例感觉挺奇怪的。
  • 虽然它可以防止被反射破坏,但是它确实复杂。

像上面Runtime类那样的单例实现就差不多了,有一个缺点是,Runtime在类加载的时候就创建对象了

如果有很多类似的单例实现,在类加载时就创建了很多不需要的对象,会很占用资源

下面写一个懒汉式静态内部类单例实现(调用时才创建对象)

public class LazyInnerClassSingleton {

    static {
System.out.println("加载静态代码块");
} private LazyInnerClassSingleton(){ System.out.println("创建对象成功"); } public static void hello(){
System.out.println("hello");
}
/*
在调用getInstance方法时InnerLazy类被加载的才会初始化对象
*/
public static LazyInnerClassSingleton getInstance(){
return InnerLazy.LAZY;
} private static class InnerLazy{ private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); }
}

这种实现的要点在与

  • 外部类构造方法私有化,无法创建外部类
  • 内部类的静态变量LAZY一直到调用外部类的getInstance方法时才会被加载,然后LAZY对象才会被创建,实现了懒加载
  • 注意内部类只是提供实例的一个工具,这里的单例对象是外部类

测试一下是不是真的

public class Test {

    public static void main(String[] args) throws Exception {

        LazyInnerClassSingleton.hello();
System.out.println("开始创建对象实例");
LazyInnerClassSingleton.getInstance();
}
}

由运行结果看到,它只有在调用getInstance方法时才会创建对象,在加载外部类时是不会加载内部类的

为了让它不被反射破坏,在构造方法上多加一个判断

无论是使用new关键字还是反射,都会调用类的构造方法,所以外部类使用这两种方式字创建实例,不然就会把异常抛出
因为if语句永远为true,虽然在执行if语句之前,InnerLazy.LAZY为null,但是只要使用了这个变量,就会去加载内部类
加载完内部类,InnerLazy.LAZY就不为null,于是抛出异常

因为我没有过破坏单例模式的经历,所以也不知道为什么要搞这么复杂,只能说是很神奇。

设计模式:单例模式的使用和实现(JAVA)的更多相关文章

  1. java设计模式单例模式 ----懒汉式与饿汉式的区别

    常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...

  2. 最简单的设计模式——单例模式的演进和推荐写法(Java 版)

    前言 如下是之前总结的 C++ 版的:软件开发常用设计模式—单例模式总结(c++版),对比发现 Java 实现的单例模式和 C++ 的在线程安全上还是有些区别的. 概念不多说,没意思,我自己总结就是: ...

  3. Java设计模式の单例模式

    -------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...

  4. JAVA设计模式-单例模式(Singleton)线程安全与效率

    一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...

  5. Java设计模式 - - 单例模式 装饰者模式

    Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...

  6. Java基础知识之设计模式--单例模式

    Java设计模式--单例模式 声明:本文根据慕课网汤小洋老师的精品课程整理来的:慕课网 什么是设计模式(Design Pattern)? 设计模式是一套被反复使用,多数人知晓的,经过分类编目的,代码设 ...

  7. 【设计模式】Java设计模式 - 单例模式

    [设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...

  8. java设计模式——单例模式(一)

    一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...

  9. JavaScript设计模式-单例模式、模块模式(转载 学习中。。。。)

    (转载地址:http://technicolor.iteye.com/blog/1409656) 之前在<JavaScript小特性-面向对象>里面介绍过JavaScript面向对象的特性 ...

  10. 设计模式 单例模式(Singleton) [ 转载2 ]

    设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...

随机推荐

  1. Docker安装rabbitMQ主从

    环境准备 Centos 7.5虚拟机三台: 192.168.102.128 192.168.102.130 192.168.102.131 以上虚拟机统一安装docker环境 三台机器分别配置如下所示 ...

  2. kafka 安装和配置

    转载自:https://www.cnblogs.com/heijinli/p/13545182.html 下载及安装  第一步:进入kafka官网  按照自己的需求选择版本,我这里选择 最新版的 2. ...

  3. 【BZOJ 4771】七彩树

    一直TLE的原因竟然是数组开太大了导致\(memset\)清空耗时超限,亏我还调了1天啊(T^T) 题目大意 给定一颗树,每个节点都有一个颜色,要求多次询问某个节点\(x\)的子树中深度不超过\(d\ ...

  4. Java基础00-运算符4

    1. 算术运算符 1.1 运算符和表达式 1.2 算数运算符 余数的计算取余数是指整数除法中被除数未被除尽部分,且余数的取值范围为0到除数之间(不包括除数)的整数 ,例如27除以6,商数为4,余数为3 ...

  5. SpringMVC中@Controller和@RequestMapping用法

    一.简介 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Mo ...

  6. [005] - JavaSE面试题(五):String类

    第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [005] - JavaSE面试题(五):String类 第1问:String.StringBuf ...

  7. 如何热更新长缓存的 HTTP 资源

    前言 HTTP 缓存时间一直让开发者头疼.时间太短,性能不够好:时间太长,更新不及时.当遇到严重问题需紧急修复时,尽管后端文件可快速替换,但前端文件仍从本地缓存加载,导致更新长时间无法生效. 对于这个 ...

  8. ODOO里视图开发案例---定义一个像tree、form一样的视图

    odoo里视图模型MVC模式: 例子:在原来的视图上修改他: var CustomRenderer = KanbanRenderer.extend({ ....});var CustomRendere ...

  9. 开源基于docker的任务调度器pipeline,比`quartzs` 更强大的分布式任务调度器

    pipeline 分布式任务调度器 目标: 基于docker的布式任务调度器, 比quartzs,xxl-job 更强大的分布式任务调度器. 可以将要执行的任务打包为docker镜像,或者选择已有镜像 ...

  10. 01MATLAB导论

    MATLAB语言的主要功能 数值计算 符号计算 图形绘制 程序流程控制 工具箱 课程的学习目标 要求理解MATLAB功能实现的数学背景与算法原理 掌握利用MATLAB进行问题求解的基本规律 能够利用M ...