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

文章目录

  • 引言
  • 单例模式简介
    • 定义与用途
    • 实现方式:
      • 饿汉式
      • 懒汉式
    • 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【蓝桥杯选拔赛真题71】Scratch绘制彩虹 少儿编程scratch图形化编程 蓝桥杯创意编程选拔赛真题解析

目录 scratch绘制彩虹 一、题目要求 编程实现 二、案例分析 1、角色分析

C陷阱与缺陷——第8章建议

不要说服自己相信”皇帝的新装“,有些看上去正确的语句实际是有问题的直接了当地表明意图,当你写的代码有可能被误解成其他含义时,可以通过加注释等方式让你的意图更加清晰;有些错误可以做到事先预防 4. 考察最简单的特例&#xf…

修改el-table表头样式

<style lang"scss" scoped> ::v-deep .el-table {.el-table__header-wrapper, .el-table__fixed-header-wrapper {th {word-break: break-word;background-color: #f8f8f9;color: #515a6e;height: 40px;font-size: 13px;}} } </style>

光伏电站卫星测绘的优缺点有哪些?

由于环境保护和能源安全的重要性日益凸显&#xff0c;光伏发电作为一种清洁、可再生、分布式的能源形式&#xff0c;已经成为全球能源领域的热门话题。 光伏电站&#xff0c;是指一种利用太阳光能、采用特殊材料诸如晶硅板、逆变器等电子元件组成的发电体系&#xff0c;与电网相…

电话号码的字母组合(C++实现)

电话号码的字母组合 题目代码思路及代码讲解代码&#xff08;有注释版&#xff09; 题目 电话号码的字母组合 代码 class Solution {const char* numsStrArr[10]{"","","abc","def","ghi","jkl","mno"…

使用调研工具做好问卷调查的方法与策略

提起问卷调查大家应该都不陌生&#xff0c;学校会使用问卷调查收集学生信息或意见、企业使用问卷调查了解市场、深入用户。和其他的调查方式相比&#xff0c;问卷调查更能贴近被调查者真实想法&#xff0c;反馈真实数据。而互联网的崛起也使得大家纷纷从线下问卷转战到线上问卷…

ubantu配置网卡ip

1.ifconfig查看网卡 2. vi /etc/network/interfaces auto ens33 # 网卡名 iface ens33 inet static # 注意网卡名 address 192.168.43.10 # 配置ip地址 netmask 255.255.255.0 # 掩码 gateway 192.168.43.1 # 网关 3.重启网卡 ifconfig ens33 down ifco…

GoLong的学习之路,进阶,微服务之原理,RPC

其实我早就很想写这篇文章了&#xff0c;RPC是一切现代计算机应用中非常重要的思想。也是微服务和分布式的总体设计思想。只能说是非常中要&#xff0c;远的不说&#xff0c;就说进的这个是面试必问的。不管用的上不&#xff0c;但是就是非常重要。 文章目录 RPC的原理本地调用…

【JavaScript】3.4 JavaScript在现代前端开发中的应用

文章目录 1. 用户交互2. 动态内容3. 前端路由4. API 请求总结 JavaScript 是现代前端开发的核心。无论是交互效果&#xff0c;还是复杂的前端应用&#xff0c;JavaScript 都发挥着关键作用。在本章节中&#xff0c;我们将探讨 JavaScript 在现代前端开发中的应用&#xff0c;包…

【PixPin】媲美QQ/Snipaste截图贴图OCR工具

PixPin PixPin是一款截图工具&#xff0c;它集成了截图、长截图、贴图、标注、OCR识别等众多功能&#xff0c;软件体积小巧&#xff0c;使用简单&#xff0c;是一款非常棒的截图工具。之前使用过Snipaste工具的小伙伴用起来应该是得心应手。 从左往右的功能依次是&#xff1a;…

你好!插值查找【JAVA】

1.初次相识 插值查找&#xff08;interpolation search&#xff09;是一种根据待查找关键字在有序数组中的大致位置决定查找范围的查找算法。插值查找与二分查找类似&#xff0c;区别在于插值查找对于待查找关键字在数组中的位置进行估计&#xff0c;从而更精准地定位到待查找关…

数据结构-03-栈

1-栈的结构和特点 先进后出&#xff0c;后进先出 是栈的特点&#xff1b; 从图中&#xff0c;我们看到A入栈先放入底部&#xff0c;然后依次B和C&#xff1b;出栈的顺序依次是C-B-A&#xff1b;这种结构只能在一端操作。所以当某个数据集合只涉及在一端插入和删除数据&#xf…

0-1背包问题详解

0-1背包问题 部分一&#xff1a;问题描述 0-1背包问题是一类经典的组合优化问题&#xff0c;它出现在很多实际生活和工业环境中。问题描述如下&#xff1a; 假设你是一个冒险家&#xff0c;带着一个可承重的背包&#xff0c;面对一堆宝物。每件宝物都有自己的价值&#xff0…

新闻宣传稿怎么写?手把手教你!

一篇高质量的的新闻宣传稿是如何炼成的&#xff1f;本文伯乐网络传媒将带你走进这个神秘的领域&#xff0c;揭秘爆款文案背后的秘密。在这里&#xff0c;你将学到一系列实用技巧&#xff0c;让你的新闻宣传稿更具吸引力、独特见解和深度。 一、选题与立意&#xff1a;抓住热点&…

数据结构---顺序表

文章目录 线性表线性表的定义线性表分类 顺序表顺次表的存储结构实现顺序表的主要接口函数初始化顺序表顺序表尾插顺序表尾删顺序表头插顺序表头删在指定位置插入数据在指定的位置删除数据头插&#xff0c;头删&#xff0c;尾插&#xff0c;尾删新写法打印顺序表销毁顺序表 线性…

使用yolov7进行多图像视频识别

1.yolov7你可以让你简单的部署,比起前几代来说特别简单 #下面是我转换老友记的测试视频,可以看到几乎可以准确预测 2.步骤 1.在github官网下载代码 https://github.com/WongKinYiu/yolov7 2.点击下载权重文件放到项目中 3.安装依赖,我的python版本是3.6的 pip install -r requ…

每日一练2023.11.27——考试座位号【PTA】

题目链接&#xff1a;L1-005 考试座位号 题目要求&#xff1a; 每个 PAT 考生在参加考试时都会被分配两个座位号&#xff0c;一个是试机座位&#xff0c;一个是考试座位。正常情况下&#xff0c;考生在入场时先得到试机座位号码&#xff0c;入座进入试机状态后&#xff0c;系…

Adobe Indesign操作

Indesign 界面 图像描摹 在Indesign中打开图片&#xff0c;选择图像描摹&#xff0c;鼠标点击。 然后&#xff0c;出现如下效果。 在此基础上&#xff0c;可以对图片进行进一步操作。 【心得】

【前端】多线程 worker

VUE3 引用 npm install worker-loader 在vue.config.js文件的defineConfig里加上配置参数 chainWebpack: config > {config.module.rule(worker-loader).test(/\.worker\.js$/).use({loader: worker-loader,options: {inline: true}}).loader(worker-loader).end()}先在…

循环单向链表(详解)

循环单向链表原理 循环单项链表项目结构 头文件RecycleLinlList.h 头文件具体代码 #ifndef RECYCLRLINKLIST #define RECYCLRLINKLIST#include <stdio.h> #include <stdlib.h>// 宏定义 #define CIRCLELINKLIST_TRUE 1 #define CIRCLELINKLIST_FALSE 0 // 链表…