设计模式:单例模式的使用和实现(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 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ... 
随机推荐
- 资源:mysql下载路径
			mysql的下载路劲 https://dev.mysql.com/downloads/mysql/ 
- Result Maps collection already contains value for cn.itcast.ssm.mapper.CompetesMapperCustom.baseMap
			在使用ssm时出现的错误: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: java.lang ... 
- 使用Hugo框架搭建博客的过程 - 页面模板
			前言 最初在制作友链界面时,没有学习Hugo框架,一头雾水.网上有关的教程甚少,只能去学一遍Hugo. 在学习Hugo的过程中,了解了列表模板,分类模板.开发了几个功能页面,如:留言板,友链,记忆分类 ... 
- git常用命令自己梳理总结
			一.新建代码库 # git-init - 创建一个空的 Git 存储库或重新初始化一个现有的存储库 $ git init # 在本地新建一个repo,进入一个项目目录,执行git init,会初始化一 ... 
- Python小白的数学建模课-B4. 新冠疫情 SIR模型
			Python小白的数学建模课-B4. 新冠疫情 SIR模型 传染病的数学模型是数学建模中的典型问题,常见的传染病模型有 SI.SIR.SIRS.SEIR 模型. SIR 模型将人群分为易感者(S类). ... 
- 「CF526F」 Pudding Monsters
			CF526F Pudding Monsters 传送门 模型转换:对于一个 \(n\times n\) 的棋盘,若每行每列仅有一个棋子,令 \(a_x=y\),则 \(a\) 为一个排列. 转换成排列 ... 
- sql 建立新表
			USE [exam]GO /****** Object: Table [dbo].[tx] Script Date: 12/13/2020 22:19:59 ******/SET ANSI_NULLS ... 
- [源码解析] 深度学习分布式训练框架 horovod (19) --- kubeflow MPI-operator
			[源码解析] 深度学习分布式训练框架 horovod (19) --- kubeflow MPI-operator 目录 [源码解析] 深度学习分布式训练框架 horovod (19) --- kub ... 
- 【剑指offer】28. 对称的二叉树
			剑指 Offer 28. 对称的二叉树 知识点:二叉树:递归 题目描述 请实现一个函数,用来判断一棵二叉树是不是对称的.如果一棵二叉树和它的镜像一样,那么它是对称的. 示例 输入:root = [1, ... 
- 网络损伤仪WANsim中关于丢包的介绍
			网络损伤仪WANsim中的4种丢包模型 丢包是指在网络上传输的数据包无法到达指定目的地.丢包在广域网中是一个很常见的问题.想要模拟出真实的广域网环境,对丢包的精确模拟是必不可少的. 在网络损伤仪WAN ... 
