java设计模式学习之【单例模式】

news2024/11/13 3:53:19

文章目录

  • 引言
  • 单例模式简介
    • 定义与用途
    • 实现方式:
      • 饿汉式
      • 懒汉式
    • UML
  • 使用场景
  • 优势与劣势
  • 单例模式在spring中的应用
  • 饿汉式实现
  • 懒汉式实现
  • 数据库连接示例
  • 代码地址

引言

单例模式是一种常用的设计模式,用于确保在一个程序中一个类只有一个实例,并且提供一个全局访问点。这种模式在需要严格控制资源访问和分配的情况下非常有用。

单例模式简介

定义与用途

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这个模式经常用于控制资源的访问,如数据库连接或文件系统。

实现方式:

单例模式通常涉及以下几个关键步骤:

  • 将构造函数设置为私有,防止外部通过new关键字创建实例。
  • 在类内部创建一个类的实例。
  • 提供一个公开的静态方法,供外部获取这个唯一的实例。

饿汉式

在类加载时就创建实例。这种方式是线程安全的,但可能会增加内存负担,因为实例不管是否需要都会被创建。

懒汉式

在第一次需要实例时才创建。这种方式可以节省资源,但需要考虑多线程环境下的线程安全问题。

UML

在这里插入图片描述

使用场景

  • 资源共享控制:如数据库连接池或配置管理。
  • 单一责任对象:当一个对象需要负责系统中的重要操作时。
  • 节约资源:避免频繁地创建和销毁对象。

优势与劣势

  • 优势
    资源节约:减少了对象创建和销毁的开销。
    全局访问:提供全局的访问点,易于管理。
  • 劣势
    全局依赖:过度使用可能导致代码之间的紧密耦合。
    多线程问题:在多线程环境下可能会面临同步问题。

单例模式在spring中的应用

单例模式在Spring框架中的应用是其核心功能之一,特别体现在Spring的Bean容器管理中。在Spring框架中,单例模式的应用主要集中在以下几个方面:

Bean的默认作用域:
在Spring中,默认情况下,所有在Spring配置文件中定义的Bean都是以单例模式创建的。这意味着无论应用中有多少次对特定Bean的请求,Spring容器都会返回同一个Bean实例。
这种方式有助于节省资源,因为相同的Bean不会被多次创建。

BeanFactory和ApplicationContext:
Spring的BeanFactory和ApplicationContext提供了Bean的创建和管理机制。它们作为Bean的容器,负责实例化、配置和组装Bean。
这些容器自身也是以单例模式运行的,确保整个应用中有一个统一的Bean管理中心。

单例Bean的线程安全:
虽然单例Bean在Spring中只实例化一次,但Spring并不保证单例Bean是线程安全的。这意味着Bean的线程安全性依赖于其自身的实现。
在设计单例Bean时,需要考虑其在多线程环境下的行为,确保状态管理的正确性。

单例模式与依赖注入(DI):
Spring使用依赖注入(DI)机制来管理Bean之间的依赖关系。在单例模式下,依赖注入确保相同的Bean实例被注入到其他需要它的Bean中。
这种机制简化了对象之间的关系,并提高了代码的可测试性和可维护性。

配置与管理:
Spring的单例模式允许集中管理Bean的配置。由于每个Bean只有一个实例,因此其配置和属性只需要设置一次,而无需在每次使用时重新配置。
这对于管理大型应用中的配置和属性尤其有用,可以提高效率和一致性。

饿汉式实现

public class A {

    private static A obj = new A(); // 类加载时即创建实例

    private A() {} // 私有构造函数

    public static A getA() {
        return obj;
    }

    public void doSomething() {
        // 方法实现
    }

}

懒汉式实现

public class A {
    private static A obj;

    private A() {} // 私有构造函数

    public static synchronized A getA() {
        if (obj == null) {
            obj = new A(); // 第一次调用时创建实例
        }
        return obj;
    }

    public void doSomething() {
        // 方法实现
    }
}

数据库连接示例

我们将通过创建一个JDBCSingleton类来展示单例设计模式的一个实际应用。这个类将被用于数据库操作,保证整个应用中只有一个数据库连接实例。
JDBCSingleton 类设计
JDBCSingleton类将包含:

  • 一个私有构造函数,防止外部直接实例化。
  • 一个私有静态实例jdbc,这是其自身的单一实例。
  • 一个公共静态方法,允许外部世界获取这个静态实例。
    在这里插入图片描述
    假设我们已经在MySQL数据库中创建了一个名为userdata的表,它包含三个字段:uid、uname和upassword。数据库名称为ashwinirajput,用户名为root,密码为ashwini。

JDBCSingleton

public class JDBCSingleton {

    // 静态成员仅持有JDBCSingleton类的一个实例
    private static JDBCSingleton jdbc;

    // 私有构造器防止其他类实例化
    private JDBCSingleton() { }

    // 提供全局访问点
    public static JDBCSingleton getInstance() {
        if (jdbc == null) {
            jdbc = new JDBCSingleton();
        }
        return jdbc;
    }

    // 获取连接以进行插入、查看等操作
    private static Connection getConnection() throws ClassNotFoundException, SQLException {
        Connection con = null;
        Class.forName("com.mysql.jdbc.Driver");
        con = DriverManager.getConnection("jdbc:mysql://localhost:3306/ashwanirajput", "root", "ashwani");
        return con;
    }

    // 向数据库中插入记录
    public int insert(String name, String pass) throws SQLException {
        Connection c = null;
        PreparedStatement ps = null;
        int recordCounter = 0;

        try {
            c = getConnection();
            ps = c.prepareStatement("insert into userdata(uname, upassword) values(?, ?)");
            ps.setString(1, name);
            ps.setString(2, pass);
            recordCounter = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
            if (c != null) {
                c.close();
            }
        }
        return recordCounter;
    }

    // 从数据库中查看数据
    public void view(String name) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            con = this.getConnection();
            ps = con.prepareStatement("select * from userdata where uname = ?");
            ps.setString(1, name);
            rs = ps.executeQuery();
            while (rs.next()) {
                System.out.println("姓名: " + rs.getString(2) + "\t" + "密码: " + rs.getString(3));
            }
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (ps != null) {
                ps.close();
            }
            if (con != null) {
                con.close();
            }
        }
    }

    // 更新给定用户名的密码
    public int update(String name, String password) throws SQLException {
        Connection c = null;
        PreparedStatement ps = null;
        int recordCounter = 0;

        try {
            c = this.getConnection();
            ps = c.prepareStatement("update userdata set upassword = ? where uname = '" + name + "'");
            ps.setString(1, password);
            recordCounter = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
            if (c != null) {
                c.close();
            }
        }
        return recordCounter;
    }

    // 从数据库删除数据
    public int delete(int userid) throws SQLException {
        Connection c = null;
        PreparedStatement ps = null;
        int recordCounter = 0;

        try {
            c = getConnection();
            ps = c.prepareStatement("delete from userdata where uid = '" + userid + "'");
            recordCounter = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
            if (c != null) {
                c.close();
            }
        }
        return recordCounter;
    }

}

JDBCSingletonDemo

public class JDBCSingletonDemo {
    static int count = 1;
    static int choice;

    public static void main(String[] args) throws IOException {
        JDBCSingleton jdbc = JDBCSingleton.getInstance();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        do {
            System.out.println("数据库操作");
            System.out.println("---------------------");
            System.out.println("1. 插入");
            System.out.println("2. 查看");
            System.out.println("3. 删除");
            System.out.println("4. 更新");
            System.out.println("5. 退出");

            System.out.print("\n请输入你想在数据库中执行的操作: ");
            choice = Integer.parseInt(br.readLine());

            switch (choice) {
                case 1: {
                    System.out.print("输入要插入数据库的用户名: ");
                    String username = br.readLine();
                    System.out.print("输入要插入数据库的密码: ");
                    String password = br.readLine();

                    try {
                        int i = jdbc.insert(username, password);
                        if (i > 0) {
                            System.out.println("第 " + (count++) + " 条数据已成功插入");
                        } else {
                            System.out.println("数据未插入");
                        }
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    System.out.println("按回车键继续...");
                    System.in.read();
                    break;
                }
                case 2: {
                    System.out.print("输入要查看的用户名: ");
                    String username = br.readLine();

                    try {
                        jdbc.view(username);
                    } catch (SQLException e) {
                        System.out.println(e);
                    }
                    System.out.println("按回车键继续...");
                    System.in.read();
                    break;
                }
                case 3: {
                    System.out.print("输入要删除的用户ID: ");
                    int userid = Integer.parseInt(br.readLine());

                    try {
                        int i = jdbc.delete(userid);
                        if (i > 0) {
                            System.out.println("第 " + (count++) + " 条数据已成功删除");
                        } else {
                            System.out.println("数据未删除");
                        }
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    System.out.println("按回车键继续...");
                    System.in.read();
                    break;
                }
                case 4: {
                    System.out.print("输入要更新的用户名: ");
                    String username = br.readLine();
                    System.out.print("输入新密码: ");
                    String password = br.readLine();

                    try {
                        int i = jdbc.update(username, password);
                        if (i > 0) {
                            System.out.println("第 " + (count++) + " 条数据已成功更新");
                        }
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    System.out.println("按回车键继续...");
                    System.in.read();
                    break;
                }
                default:
                    return;
            }
        } while (choice != 5);
    }
}

这个示例展示了如何在实际应用中使用单例模式来创建和管理数据库连接。通过这种方式,可以确保应用程序中的所有组件都使用相同的数据库连接实例,从而提高了效率和一致性。同时,这也减少了数据库连接的开销,因为连接实例只被创建一次并在整个应用中复用。

代码地址

23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1261506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不小心删除了短信,如何在 Android 上恢复已删除的短信

不小心删除了文字消息在 Android 手机上使用可能会是一种令人痛苦的体验。这些消息可能包含有价值的信息、珍贵的回忆或重要的细节。幸运的是,您可以探索多种方法来恢复这些丢失的消息。在本文中,我们将深入研究可用于检索已删除短信的选项,并…

vue3中readonly和shallowReadonly

readonly: 深度只读数据 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。 只读代理是深层的:访问的任何嵌套 property 也是只读的。 shallowReadonly 浅只读数据 创建一个代理,使其自身的 property 为只读,但不执行…

WhatsApp API号解封教程(内含图片指引和申诉模板)

WhatsApp API 是专门为中大型企业设置的WhatsApp APP页面,API号并不像WhatsApp个人号和企业号一样可以直接从App Store 或Google Play 下载,而是需要对接官方来连接API。 虽然申请WhatsApp API号的程序和手续比较复杂,但是这个操作对于企业来…

算法通关村第二关—手写链表反转(青铜)

链表反转的三种方式 一、建立虚拟头结点辅助反转 为了方便反转,可以创建一个ans结点,让ans.next head,然后后面的结点一次插入到ans.next 在下图中,对(1->2->3->4->5)进行反转,可以创建ans&…

easyexcel指定sheet页动态给行列加背景色

easyexcel,有多个sheet页,某些sheet页的行、列动态需要加背景色 import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.m…

Spring Cloud,注册中心,配置中心,原理详解

文章目录 Spring Cloud,注册中心,配置中心,原理详解谈谈我个人对 spring Cloud 的理解 注册中心Eureka:服务搭建小结 Ribbo - 负载均衡1. 负载均衡流程2. 负载均衡策略 nacos注册中心1. 配置集群1. 创建 namespace2. 配置命名空间…

MATLAB实战 | 不同形式的三维曲面图

通常,MATLAB中绘制三维曲面图,先要生成网格数据,再调用mesh函数和surf函数绘制三维曲面。若曲面用含两个自变量的参数方程定义,则还可以调用fmesh函数和fsurf函数绘图。若曲面用隐函数定义,则可以调用fimplicit3函数绘…

RuoYi若依前后端分离框架二次开发基础项目

一键改标题:点击ruoyi-ui(ctrlshiftr)改完重启项目 连接本地数据库 全智能生成开发 新建maven项目 菜单创建---栏目创建 无权限删除时:去掉要删除的菜单权限 子菜单:新增 代码生成器cv进去 后端: 前端: 完成&#x…

人工智能与供应链行业融合:预测算法的通用化与实战化

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 让我们一起深入探索人工智能与供应链的融合,以及预测算法在实际应用中的价值!🔍🚀 文章目录 前言供应链预测算法的基本流程统计学习模型与机…

顺子日期(14)

顺着日期 public class Main {public static void main(String[] args) {int res 0;//2022年int[] days new int[] {31,28,31,30,31,30,31,31,30,31,30,31};//31,28,31,30,31,30,31,31,30,31,30,31//一三五七八十腊//构造2022年每一天的日期yyyymmddStringBuffer date new…

现在的教师工资这么低了?

最近刷v站看到个帖子t/991351 ,程序员转行回乡当老师月薪才2000,现在的教师工资这么低了?月薪3000都不到。 搜索了下,现在有些地方开始#教师全员竞聘上岗# https://weibo.com/1784473157/NpUd3esCv ,曾经的铁饭碗也可能…

【开发问题解决方法记录】02.dian

想重命名表名,但是失败了,提示[0A000][3001] ORA-03001: 未实施的功能 Position: 0. 获取到USER_ID和ROLE_ID但是无法新增成功 问题出在哪里捏? 报错:ORA-06502: PL/SQL: 数字或值错误 : 字符到数值的转换错误 展示的是…

不受平台限制,Sketch 网页版震撼登场

Sketch 是一种基于 Mac 的矢量图形编辑器,可用于数字设计。其主要功能包括无损矢量编辑、完美像素精度和数百个插件同步功能,可导出预设和代码。它是目前流行的页面交互协作设计工具。但是 Sketch 最大的缺点是对 Windows/PC 用户不友好。严格来说&#…

笔记十七、认识React的路由插件react-router-dom和基本使用

react-router 分类 web使用 react-router-dom native使用 react-router-native anywhere(使用麻烦) react-router 安装 yarn add react-router-dom main.jsx import React from "react"; import ReactDOM from "react-dom/client"…

宋仕强论道之华强北曼哈商城(十二)

宋仕强论道之华强北曼哈商城(十二): 讲了华强北万佳百货以后要讲旁边配套的商场。在1995年,在万佳百货旁边开了一个曼哈商城,名字叫曼哈是因为老板是美国纽约曼哈顿留学回来的,是一个叫张虹女孩子。华强北曼…

DS图—图的最短路径/Dijkstra算法【数据结构】

DS图—图的最短路径/Dijkstra算法【数据结构】 题目描述 给出一个图的邻接矩阵,输入顶点v,用迪杰斯特拉算法求顶点v到其它顶点的最短路径。 输入 第一行输入t,表示有t个测试实例 第二行输入顶点数n和n个顶点信息 第三行起,每行…

C/C++字节对齐

C/C字节对齐 C/C字节对齐1.G_PACKED2.1 pack(push)2.2 pack(1) 全部例子 C/C字节对齐 1.G_PACKED #ifdef __GNUC__#define G_PACKED( __Declaration__ ) __Declaration__ __attribute__((packed)) #else#define G_PACKED( __Declaration__ ) __pragma( pack(push,1)) __Decla…

万宾科技第四代可燃气体监测仪的作用

燃气作为一种重要的能源已在居民生活、工业生产和商业活动等领域得到了广泛的应用。但是与之而来的便是各种各样的燃气管网的安全问题,其中燃气管网泄漏成为了城市生命线建设中亟待解决的安全隐患。因此采取切实有效的措施来保障燃气管网的安全运行,应用…

龙芯loongarch64服务器编译安装pyarrow

1、简介 pyarrow是一个高效的Python库,用于在Python应用程序和Apache Arrow之间进行交互。Arrow是一种跨语言的内存格式,可以快速高效地转移大型数据集合。它提供了一种通用的数据格式,将数据在内存中表示为表格,并支持诸如序列化和分布式读取等功能。 龙芯的Python仓库安…

Python爬虫之代理IP与访问控制

目录 前言 一、代理IP 1.1.使用代理IP的步骤 1.2.寻找可用的代理IP 1.3.设置代理IP 1.4.验证代理IP的可用性 二、访问控制 2.1.遵守Robots协议 2.2.设置访问时间间隔 2.3.多线程爬取 总结 前言 在进行Python爬虫过程中,代理IP与访问控制是我们经常需要处…