设计模式:单例模式的使用和实现(JAVA)
单例模式的使用
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)的更多相关文章
- java设计模式单例模式 ----懒汉式与饿汉式的区别
常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...
- 最简单的设计模式——单例模式的演进和推荐写法(Java 版)
前言 如下是之前总结的 C++ 版的:软件开发常用设计模式—单例模式总结(c++版),对比发现 Java 实现的单例模式和 C++ 的在线程安全上还是有些区别的. 概念不多说,没意思,我自己总结就是: ...
- Java设计模式の单例模式
-------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- Java设计模式 - - 单例模式 装饰者模式
Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...
- Java基础知识之设计模式--单例模式
Java设计模式--单例模式 声明:本文根据慕课网汤小洋老师的精品课程整理来的:慕课网 什么是设计模式(Design Pattern)? 设计模式是一套被反复使用,多数人知晓的,经过分类编目的,代码设 ...
- 【设计模式】Java设计模式 - 单例模式
[设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...
- java设计模式——单例模式(一)
一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...
- JavaScript设计模式-单例模式、模块模式(转载 学习中。。。。)
(转载地址:http://technicolor.iteye.com/blog/1409656) 之前在<JavaScript小特性-面向对象>里面介绍过JavaScript面向对象的特性 ...
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
随机推荐
- 锁分析 Lock
锁分析 Lock NonReentrantLock ReadLock 共享锁 ReentrantLock 重入锁 排他锁 sync.lock 返回值为void,表示如无异常发生都认为锁获取成功 Fai ...
- 【保姆级】Python项目(Flask网页)部署到Docker的完整过程
大家好,我是辰哥~ 前提:相信看到这篇文章的读者应该已经学会了Docker的安装以及Docker的基本使用,如果还不会的可以参考我之前的文章进行详细学习! 1.安装版:2300+字!在不同系统上安装D ...
- Kong Admin API — 核心对象
目录 Service API详解 1. 添加服务 2. 列出service列表 3. 查找service 按条件查找service 查找与指定route关联的service 查找与指定Plugin关联 ...
- sort,wc,uniq 排序与统计命令
sort命令 语法:sort [parameter] [file or stdin]参数: -f:忽略大小写的差异,例如A和a视为编码相同 -b:忽略最前面的空格符部分 -n:使用"纯数字& ...
- Automation Framework Design 自动化框架设计思想
从2007年到2017年,十年内自动化测试工具层出不穷,各种工具在运用一段时间之后,各个公司都会有测试架构师对于目前的自动化测试工具进行框架定制设计. 从惠普2007年GDCC推出的的WebDrivi ...
- 做词云时报错cannot import name ‘WordCloud‘ from partially initialized module ‘wordcloud‘的解决办法
问题: 在做词云时,运行时出现该问题,wordcloud安装成功,但运行出错,错误提示是:cannot import name 'WordCloud' from partially initializ ...
- [刘阳Java]_Spring相关配置介绍_第5讲
这一节我们介绍一下Spring框架的相关常用配置 Spring依赖注入的两种方式(构造方法注入和setter方式注入) p-namespace方式配置 properties属性文件配置方式 集合对象配 ...
- C++11 左值引用和右值引用与引用折叠和完美转发
1.左值与右值 最感性的认识. 当然,左值也是可以在右边的. 左值是可以被修改的,右值不能. 当然取地址也是. 生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短. 下面 ...
- 【16位RAW图像处理三】直方图均衡化及局部直方图均衡用于16位图像的细节增强。
通常我们生活中遇到的图像,无论是jpg.还是png或者bmp格式,一般都是8位的(每个通道的像素值范围是0-255),但是随着一些硬件的发展,在很多行业比如医疗.红外.航拍等一些场景下,拥有更宽的量化 ...
- js里的发布订阅模式及vue里的事件订阅实现
发布订阅模式(观察者模式) 发布订阅模式的定义:它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布订阅模式在JS中最常见的就是DOM的事件绑定与触发 ...