java 单例模式 参数_Java单例模式

article/2025/9/11 16:52:27

再孬再好,就你一个

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

单例类限制类的实例个数,保证类的实例在JVM的世界里只有一个类的实例对象。

单例类必须自己创建自己的唯一实例。

单例类必须提供一个全局性的公共访问方式获取单例类的实例对象

单例类必须给所有其他对象提供这一实例。

介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

应用场景: java.lang.Runtime类、日志、驱动器、缓存、线程池等l

d35c3400c9e30be3c0ddc08bb48f685a.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

实现

我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。

SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo,我们的演示类使用 SingleObject 类来获取 SingleObject 对象。

步骤 1

创建一个 Singleton 类,SingleObject.java

public class SingleObject {

//创建 SingleObject 的一个对象

private static SingleObject instance = new SingleObject(0);

//让构造函数为 private,这样该类就不会被实例化

private SingleObject(int i){

System.out.println("我是一个单例"+i);

}

//获取唯一可用的对象

public static SingleObject getSingletonInstance(){

return instance;

}

public void showMessage(String msg){

System.out.println("我是一个单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

步骤 2

从 singleton 类获取唯一的对象,测试类SingletonPatternDemo.java

/**

*

* @author dgm

* @describe "测试单例模式"

* @date 2020年5月13日

*/

public class SingletonPatternDemo {

public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {

// 不合法的构造函数

// 编译时错误:构造函数 SingleObject() 是不可见的

// SingleObject object = new SingleObject();

// 获取唯一可用的对象

SingleObject object = SingleObject.getSingletonInstance();

// 显示消息

object.showMessage("正常流程");

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

输出效果:

a91a85c013d1c61cb5ebea5ee8554f23.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

4c483c03800726aba8a1e75a2b551fbb.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

我就一一列举:

1.  饿汉式初始化 Eager initialization

在饿汉初始化模式下,单例类的实例是在类加载的时候完成了创建,这是最简单的创建单例的方式,但它有个缺陷就是客户端还没使用已创建好的单例实例。下面就是实现了静态初始化的单例类

/**

*

* @author dgm

* @describe "饿汉式初始化单例"

*/

public class EagerInitializedSingleton {

private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

//注意是私有构造器,防止客户端使用

private EagerInitializedSingleton(){}

//全局访问方式获取单实例

public static EagerInitializedSingleton getSingletonInstance(){

return instance;

}

public void showMessage(String msg){

System.out.println("我是一个饿汉式初始化单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

2. 静态块初始化 Static block initialization

静态块初始化的实现单例和饿汉式初始化类似,但它在创建类实例的时候提供了异常处理

/**

*

* @author dgm

* @describe "静态代码块初始化"

*/

public class StaticBlockSingleton {

private static StaticBlockSingleton instance;

private StaticBlockSingleton(){}

//在静态代码块初始化进行异常处理

static{

try{

instance = new StaticBlockSingleton();

}catch(Exception e){

throw new RuntimeException("创建单实例时发生了异常");

}

}

public static StaticBlockSingleton getSingletonInstance(){

return instance;

}

public void showMessage(String msg){

System.out.println("我是一个静态代码块初始化单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

3. 延迟初始化 Lazy Initialization

延迟初始化意味着创建单例的是时机不是在类加载过程了,而是在全局访问方法里实现单例模式的实例创建,就是需要时再创建

/**

*

* @author dgm

* @describe "延迟初始化实例"

*/

public class LazyInitializedSingleton {

private static LazyInitializedSingleton instance;

private LazyInitializedSingleton(){}

public static LazyInitializedSingleton getSingletonInstance(){

if(instance == null){

instance = new LazyInitializedSingleton();

}

return instance;

}

public void showMessage(String msg){

System.out.println("我是一个延迟初始化单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

此种实现方式在单线程环境下表现很好,但是现实中往往是多线程环境,故有了下面的其他实现方式

4.  线程安全型单例Thread Safe Singleton

有一种简单创建线程安全的单例,就是给全局访问方法加上关键字synchronized (锁的一种),它可以确保只有一个线程执行获取单例的全局访问方法。

/**

*

* @author dgm

* @describe "线程安全的单例"

*/

public class ThreadSafeSingleton {

private static ThreadSafeSingleton instance;

private ThreadSafeSingleton(){}

public static synchronized ThreadSafeSingleton getSingletonInstance(){

if(instance == null){

instance = new ThreadSafeSingleton();

}

return instance;

}

public void showMessage(String msg){

System.out.println("我是一个线程安全的单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

Above implementation works fine and provides thread-safety but it reduces the performance because of the cost associated with the synchronized method, although we need it only for the first few threads who might create the separate instances (Read: Java Synchronization). To avoid this extra overhead every time, double checked locking principle is used. In this approach, the synchronized block is used inside the if condition with an additional check to ensure that only one instance of a singleton class is created.

该实现确实实现了线程安全的单例,可同步方法对性能有影响,可改造为双重检测锁

/**

*

* @author dgm

* @describe "线程安全双重检测的单例"

*/

public class ThreadSafeDoubleCheckSingleton {

private static ThreadSafeDoubleCheckSingleton instance;

private ThreadSafeDoubleCheckSingleton(){}

public static ThreadSafeDoubleCheckSingleton getSingletonInstance(){

if(instance == null){

synchronized (ThreadSafeDoubleCheckSingleton.class) {

if(instance == null){

instance = new ThreadSafeDoubleCheckSingleton();

}

}

}

return instance;

}

public void showMessage(String msg){

System.out.println("我是一个线程安全双重检测单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

5. 内部静态辅助类  Inner Static Helper Class

借助内部静态类创建单例

/**

*

* @author dgm

* @describe "内部静态辅助类"

*/

public class InnerHelperSingleton {

private InnerHelperSingleton(){}

private static class SingletonHelper{

private static final InnerHelperSingleton INSTANCE = new InnerHelperSingleton();

}

public static InnerHelperSingleton getSingletonInstance(){

return SingletonHelper.INSTANCE;

}

public void showMessage(String msg){

System.out.println("我是一个内部静态辅助类的单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

特别注意,当单例类被加载的时候,内部辅助类还没有加载到内存,当有客户端调用公共访问getSingletonInstance方法时内部类才被加载,然后创建单例对象。

一句话:私有内部静态类负责创建单例类的实例对象,且不需要同步机制

7. 序列化单例 Serialization and Singleton

有时在分布式系统中,为了在文件系统中存放单实例状态,我们需要在单例类中实现序列化接口(Serializable interface),下面是一个实现了序列化接口的单实例类:

import java.io.Serializable;

/**

*

* @author dgm

* @describe "序列化单例"

*/

public class SerializedSingleton implements Serializable{

private static final long serialVersionUID = -7604766932017737115L;

private SerializedSingleton(){}

private static class SingletonHelper{

private static final SerializedSingleton instance = new SerializedSingleton();

}

public static SerializedSingleton getSingletonInstance(){

return SingletonHelper.instance;

}

public void showMessage(String msg){

System.out.println("我是一个序列化单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

.序列化的单实例进行反序列化时会出现问题,创建了一个新的类实例对象,如下代码:

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectInputStream;

import java.io.ObjectOutput;

import java.io.ObjectOutputStream;

/**

*

* @author dgm

* @describe "序列化单例测试"

*/

public class SingletonSerializedTest {

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {

SerializedSingleton instanceOne = SerializedSingleton.getSingletonInstance();

ObjectOutput out = new ObjectOutputStream(new FileOutputStream(

"SerializedSingleton.ser"));

out.writeObject(instanceOne);

out.close();

//反序列化对象

ObjectInput in = new ObjectInputStream(new FileInputStream(

"SerializedSingleton.ser"));

SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();

in.close();

System.err.println("相等否: "+(instanceOne==instanceTwo));

System.out.println("instanceOne hashCode="+instanceOne.hashCode());

System.out.println("instanceTwo hashCode="+instanceTwo.hashCode());

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

输出结果:

241b8ebc84216bee92a7b3304176197a.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

可以通过在单实例类的定义中加上

private Object readResolve() throws ObjectStreamException{

//instead of the object we're on,

//return the class valiable INSTANCE

return SingletonHelper.instance;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

注意,反序列化会判断是否有readResolve方法

readResolveMethod = getInheritableMethod(

cl, "readResolve", null, Object.class);

/**

* Returns true if represented class is serializable or externalizable and

* defines a conformant readResolve method. Otherwise, returns false.

*/

boolean hasReadResolveMethod() {

return (readResolveMethod != null);

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

这样当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了, 单例模式规则也就得到了保证.

638183ea7c356f780fd924c354c68a85.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

此时非常好,JVM世界里只有一个单实例对象

8. 枚举单例 Enum Singleton

用枚举的方式实现单例模式也不失为一种好方法,它可以保证任何一个枚举值只被实例化一次

/**

*

* @author dgm

* @describe "枚举单例"

*/

public enum EnumSingleton {

INSTANCE,

OTHER_INSTANCE;

public void showMessage(String msg){

System.out.println("我是一个枚举型单例的方法调用,"+msg);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

测试枚举型单例代码:

public class EnumSingletonTest {

public static void main(String[] args) {

// TODO Auto-generated method stub

EnumSingleton enumSingleton = EnumSingleton.INSTANCE;

EnumSingleton otherEnumSingleton = EnumSingleton.INSTANCE;

EnumSingleton secondEnumSingleton = EnumSingleton.OTHER_INSTANCE;

System.err.println("枚举型单例对象相等否: "+(enumSingleton==otherEnumSingleton));

System.out.println("enumSingleton hashCode="+enumSingleton.hashCode());

System.out.println("otherEnumSingleton hashCode="+otherEnumSingleton.hashCode());

System.err.println("枚举型单例对象相等否: "+(enumSingleton==secondEnumSingleton));

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

输出结果

78c1c0f318f7c56ffe1e5645994139ab.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

9. 使用反射 ,魔方

可以使用反射,注意,它和没有使用readResolve()方法的序列化单例模式类似,会破坏单例模式,JVM的世界里会有多个实例对象

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

/**

*

* @author dgm

* @describe "反射单例"

*/

public class ReflectionSingletonTest {

public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {

// 获取唯一可用的对象

SingleObject object = SingleObject.getSingletonInstance();

// 显示消息

object.showMessage("正常流程");

// 获取Class对象

Class> singltonClass = null;

SingleObject otherObject = null;

try {

singltonClass = Class.forName("code.singleton.SingleObject", false,

SingletonPatternDemo.class.getClassLoader());

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

// 已经确定有Class对象,实际情况需要做判断类加载是否成功,获取所有构造器

Constructor>[] allConstructors = singltonClass

.getDeclaredConstructors();// getConstructors();

System.out.println("所有构造器个数:" + allConstructors.length);

for (Constructor constructor : allConstructors) {

System.out.println("构造器:" + constructor);

if (!constructor.isAccessible()) {

System.out.println("我是一个私有构造器");

constructor.setAccessible(true);

}

// 用获取到的构造器实例化对象

otherObject = (SingleObject) constructor.newInstance(1);

// 通过“参数化”获取类里的方法

Method method = singltonClass

.getMethod("showMessage", String.class);

// 执行方法

method.invoke(otherObject, "不正常流程");

}

System.err.println(object == otherObject);

System.out.println("object hashCode="+object.hashCode());

System.out.println("reflection Object hashCode="+otherObject.hashCode());

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

执行输出结果(好可怕破坏单例模式)

5ac64803ec25c4403dcc6289348c32dc.pngwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

发现两个实例并不相等,反射很强大,被广泛用于像Spring、Hibernate、Mybatis等框架中,普通开发禁止使用。加上我的反射一文,从此以后,你可以光明正大的说,私有属性、私有构造器、私有方法可以访问兼修改了

参考:

4. The "Double-Checked Locking is Broken" Declaration?

https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html


http://chatgpt.dhexx.cn/article/iPbWupoH.shtml

相关文章

java单例接口_JAVA单例模式

一.问题引入 偶然想想到的如果把Java的构造方法弄成private,那里面的成员属性是不是只有通过static来访问呢;如果构造方法是private的话,那么有什么好处呢;如果构造方法是private的话,会不更好的封装该内呢?我主要是应用在使用普通类模拟枚举类型里,后来发现这就是传说中…

java单例模式——详解JAVA单例模式及8种实现方式

本文转载自:java单例模式——详解JAVA单例模式及8种实现方式_EatingSoilLang的博客-CSDN博客_java单例模式 ## 单例模式是最简单也是最基础的设计模式之一,下边一起学习一下单例模式! 一.单例模式的定义: 单例模式确保某个类只有…

JAVA设计模式之单例模式

本文继续介绍23种设计模式系列之单例模式。 概念:   java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。   单例模式有以下特点:   1、单例类…

Java多线程(七):单例模式详解

目录 1. 什么是单例模式 2. 单例模式的特点 3. 单例模式的实现 3.1 单例模式实现步骤 3.2 单例模式实现方式 3.2.1 饿汉方式 3.2.2 懒汉方式 1. 什么是单例模式 java中单例模式是一种常见的设计模式,单例模式确保某个类只有一个实例,而且自行实例…

ARFoundation之路-3D物体识别之一

版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载。 3D物体跟踪技术,是指通过图像处理技术对摄像头中拍摄到的3D物体识别定位并对其姿态进行跟踪的技术。3D物体跟踪技术的基础也是图像识别,但比前述图…

ARFoundation系列讲解 - 57 3D物体识别一

一、介绍 目前在ARFoundation中,3D物体识别只支持A9处理器或更高版本的iOS设备,系统版本要求iOS 12或更高版本,Android不支持此功能 。使用3D物体识别跟踪功能需要预先采集3D物体特征点信息。苹果公司给我们提供了一个采集3D物体特征点工具,我们需要下载并使用Xcode编译成应…

实战三:手把手教你实现物体识别

实战三:手把手教你实现物体识别 一、基于HaadAdaboost实现人脸识别 1.原理介绍(参考下面的博客文章) http://www.cnblogs.com/ello/archive/2012/04/28/2475419.html Haar分类器 Haar-like特征积分图方法AdaBoost级联 Haar分类器算法的…

手把手教你使用LabVIEW OpenCV dnn实现物体识别(Object Detection)含源码

文章目录 前言一、物体识别算法原理概述1、物体识别的概念2、Yolo算法原理概述 二、opencv调用darknet物体识别模型(yolov3/yolov4)1、darknet模型的获取2、python调用darknet模型实现物体识别3、LabVIEW调用darknet模型实现物体识别yolo_example.vi4、L…

颜色和移动物体识别系统

颜色和移动物体识别系统 1.开发工具 Python版本:Anaconda 的python环境3.8版本 开发软件:Pycharm社区版 识别模型:深度学习模型,普通学习模型 相关模块:opencv-python3.4.8.29模块 2.环境搭建 安装Anaconda并将路径添加…

[Vuforia]二.3D物体识别

之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。 首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能…

K210视觉体验—物体识别

K210视觉体验—物体识别 使用设备ZTFR开发板 人脸识别构造函数导入模型 示例代码基础测试 使用设备 ZTFR开发板 人脸识别 首先简单介绍一下 K210 的 KPU。KPU 是 K210 内部一个神经网络处理器,简单来说就是 KPU 能加载和运行各种现成的 AI 算法模型,实现…

tensorflow 物体识别

tensorflow推出的object detection模块,为我们做物体检测提供了极大的便利,如果还没有安装该模块,可以参照 http://blog.csdn.net/hanshuobest/article/details/79222685 下面的示例演示如何用已训练的模型做物体检测 import numpy as np …

计算机视觉物体识别的过程,计算机视觉之从OpenCV到物体识别(二)

前言:上一章节我们学会了图片、视频以及外设获取帧图片的方式,以及OpenCV 的灰度图片处理。计算机不像人眼可以很轻易地辨别物体,本质是将一张图片转换成灰度图,通过一定的模型训练,使得机器能够从0,1(黑与白)中分辨物体的特征,由局部到整体,图片越复杂,所需的训练程度…

unity c# 触摸屏物体识别桌算法

unity源码地址链接:unityc#触摸屏物体识别桌算法-C#文档类资源-CSDN下载 实操的时候出现算出来的坐标不对,没有时间去找问题,优化了,所以代码仅供参考。 使用的是Lean Touch插件,免费的。 模块由三个点构成&#xf…

如何用AR引擎技术, 5步优雅实现物体识别和跟踪

AR技术让应用实现虚拟世界和现实世界的融合,让开发者的创意和脑洞能够拥有更多炫酷有趣的呈现方式。AR拍照、AR购物、AR教育、AR搜索等丰富应用场景,凭借着AR技术的加持,为用户带来更优质的沉浸式体验,为用户创造更多价值。 例如…

物体识别全流程(Ubuntu16.04)结合ROS

物体识别全流程(Ubuntu16.04)结合ROS 1.使用labellmg,标记图片,生成xml标签 在此下载labellmg包 下载之后解压到要放置的目录 推荐使用Python3Qt5 打开labellmg包 在当前目录终端下运行如下命令行 sudo apt-get install pyqt5-…

opencv人脸识别以及自定义物体识别系统

目录 一、简介: 二、软硬件应用: 三、模块介绍: (一)人脸识别: (二)、自定义物体识别: (三)、图片展示: (四&#x…

opencv物体识别-识别水果

前言 玩一玩用opencv做一些简单的物体识别 1.思路讲解 我们基于简单的opencv的阈值分割,通过这个阈值分割,我们能把我们需要识别的物体在二值图里面变成白色,其余的变成黑色。然后对我们分割出来的物体部分提取轮廓,算出覆盖轮…

物体识别

一、物体识别 图像识别:很重要,是很多算法的基础 图像识别定位:识别出来是猫,且定位出猫的位置 物体检测:每个图像中含有多物体,检测出有几个个体,并框出来位置 图像分割:不仅仅框…

【AI技术】物体识别概述1

【AI技术】物体识别概述1 1、背景2、物体识别3、应用 1、背景 主要针对客户以及初学者概述物体识别。 2、物体识别 物体识别又叫目标识别,物体分类,图像分类,习惯性称为图像分类,即对整张图片进行分类。 物体识别针对的是物体分…