单例模式的使用

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. Maven中dependencies和dependencyManagement的区别

    Maven项目中,为了保持引用依赖的一致性,一般会抽出一个parent层,用来管理子项目的maven依赖,对于依赖的管理有两种方式,分别是dependencies以及dependencyManagem ...

  2. 『无为则无心』Python函数 — 27、Python函数的返回值

    目录 1.返回值概念 2.return关键字的作用 3.返回值可以返回的数据类型 4.函数如何返回多个值 5.fn5 和 fn5()的区别 6.总结: 1.返回值概念 例如:我们去超市购物,比如买饮料 ...

  3. PHP安装amqp扩展 出现未装librabbitmq错误

    这错误的原因是因为没有安装amqp的依赖包rabbitmq-c,需要先安装rabbitmq-c. 1.安装rabbitmq-c-0.7.1 没有安装就会提示上面的错误我选择的是最新版本0.7.1 wg ...

  4. nginx 基本配置

    server { listen 80; server_name 域名; #access_log /var/log/nginx/admin.log; index index.html index.htm ...

  5. jar\war\SpringBoot加载包内外资源的方式,告别FileNotFoundException吧

    工作中常常会用到文件加载,然后又经常忘记,印象不深,没有系统性研究过,从最初的war包项目到现在的springboot项目,从加载外部文件到加载自身jar包内文件,也发生了许多变化,这里开一贴,作为自 ...

  6. Helm Template初体验,方便管理多环境

    我最新最全的文章都在南瓜慢说 www.pkslow.com,文章更新也只在官网,欢迎大家来喝茶~~ 1 简介 Helm作为一个优秀的包管理器,这部分我们之前已经做了介绍,文章如下: 用Helm部署Ku ...

  7. File类与常用IO流第九章——转换流

    第九章.转换流 字节编码和字符集 编码:按照某种规则将字符以二进制存储到计算机中. 解码:将存储在计算机中的二进制数按照某种规则解析显示出来. 字符编码:Character Encoding ,就是一 ...

  8. 2021 MySQL安装教程(最新教程)- 含网盘下载

    大家好,我是 我玩亚索我会C.最近电脑重装系统了,然后就想着装个MySQL,由于很久没装过了,于是上网搜索了教程,但是发现现在MySQL安装和之前的不一样了,网上都是旧版的安装教程,所以我就做一篇新版 ...

  9. springMVC-1-servlet回顾

    SpringMVC重点学习 项目目标:SpringMVC+Vue+SpringBoot+SpringCloud+Linux spring:IOC+AOP SpringMVC:SpringMVC的执行流 ...

  10. Kubernetes全栈架构师(基本概念)--学习笔记

    目录 为什么要用Kubernetes? K8s控制节点-Master概念 K8s计算节点-Node概念 什么是Pod? 为什么要引入Pod? 创建一个Pod 零宕机发布应用必备知识:Pod三种探针 零 ...