《java常用设计模式之----单例模式》
一、简介
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
二、主要特征
1.单例类只能有一个实例(即单例类不能被外部实例化---单例类的构造方法由private修饰)。
2.单例类必须自己创建自己的实例,并且供外部调用(向外部暴露调用其实例的静态方法)。
三、优缺点
1.优点:
1)在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2)不需要去重复的创建实例,避免对资源的多重占用(比如写文件操作)。
2.缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
四、实现
1.场景:一个班级有很多学生,但只有一个班主任
2.代码实现:
1)饿汉式:即类加载时就实例化,相对浪费内存,并且容易产生垃圾对象
public class Teacher {
/**
* 创建自己的实例
*/
private static Teacher teacher = new Teacher(); /**
* 静态构造方法,不允许外部对其实例化
*/
private Teacher(){} /**
* 将自己的实例对象,提供给外部使用
* @return Teacher
*/
public static Teacher getInstance(){
return teacher;
} public void attendClass(){
System.out.println("班主任说:上课");
} public void finishClass(){
System.out.println("班主任说:下课");
}
}
2)懒汉式:第一次调用才实例化,避免浪费内存
public class Teacher {
/**
* 创建自己的实例
*/
private static Teacher teacher; /**
* 静态构造方法,不允许外部对其实例化
*/
private Teacher(){} /**
* 将自己的实例对象,提供给外部使用
* @return Teacher
*/
public static Teacher getInstance(){
if(null == teacher){
teacher = new Teacher();
}
return teacher;
} public void attendClass(){
System.out.println("班主任说:上课");
} public void finishClass(){
System.out.println("班主任说:下课");
}
}
注:懒汉式加载是否为线程安全的区别在于synchronized 关键字,给getInstance()方法加上synchronized 关键字,使得线程安全,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
3)双检锁/双重校验锁(DCL,即 double-checked locking):采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。
public class Teacher {
/**
* 创建自己的实例
*/
private volatile static Teacher teacher = new Teacher(); /**
* 静态构造方法,不允许外部对其实例化
*/
private Teacher(){} /**
* 将自己的实例对象,提供给外部使用
* @return Teacher
*/
public static Teacher getInstance(){
if (teacher == null) {
synchronized (Teacher.class) {
if (teacher == null) {
teacher = new Teacher();
}
}
}
return teacher;
} public void attendClass(){
System.out.println("班主任说:上课");
} public void finishClass(){
System.out.println("班主任说:下课");
}
}
4)登记式/静态内部类:与双检索方式功效一样,但实现更简单;这种方式同样在类加载时不会实例化,只有在外部调用其getInstance()方法时才会被实例化
public class Teacher {
/**
* 静态内部类
*/
private static class TeacherHolder {
/**
* 创建Teacher实例,只有在显示的调用时才会被实例化
*/
private static final Teacher INSTANCE = new Teacher();
}
/**
* 静态构造方法,不允许外部对其实例化
*/
private Teacher (){} /**
* 将单例类,提供给外部使用
* @return Teacher
*/
public static final Teacher getInstance() {
return TeacherHolder.INSTANCE;
} public void attendClass(){
System.out.println("班主任说:上课");
} public void finishClass(){
System.out.println("班主任说:下课");
}
}
5)枚举:实现单例模式的最佳方法,更简洁,自动支持序列化机制,绝对防止多次实例化(目前未被广泛使用)
public enum Teacher {
INSTANCE;
public void whateverMethod() {
System.out.println("枚举下单例模式");
}
}
以上代码讲述了如何创建一个单例类Teacher,下面来看一下如何使用这个单例
public class Test {
public static void main(String[] args) {
//调用单例类供外部调用的方法,获取其实例
Teacher teacher = Teacher.getInstance();
teacher.attendClass();
teacher.finishClass();
}
}
这样一个简单的单例模式的例子就已经完成了,接下来举一个实际应用的例子,例如:jdbc
public class DBUtils {
public static final String URL = "jdbc:mysql://localhost:3306/demo";
public static final String USER = "root";
public static final String PASSWORD = "root";
private static Connection conn = null;
static{
try {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2. 获得数据库连接
conn = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} public static Connection getConnection(){
return conn;
}
}
public class TeacherDB {
private int id;
private String name; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "TeacherDB{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public class TestDB {
public static boolean addTeacher() {
//获取jdbc连接
Connection connection = DBUtils.getConnection();
//sql
String sql = "INSERT INTO teacher(name) values(?)";
PreparedStatement ptmt = null;
try {
//预编译SQL,减少sql执行
ptmt = connection.prepareStatement(sql);
ptmt.setString(1,"john");
return ptmt.execute();
} catch (SQLException e) {
e.printStackTrace();
}
return false;
} public static List<TeacherDB> queryTeacher() {
//获取jdbc连接
Connection connection = DBUtils.getConnection();
//sql
String sql = "select * from teacher";
Statement stmt = null;
ResultSet rs = null;
List<TeacherDB> list = new ArrayList<>();
try {
stmt = connection.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()){
TeacherDB teacherDB = new TeacherDB();
teacherDB.setId(rs.getInt("id"));
teacherDB.setName(rs.getString("name"));
list.add(teacherDB);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
}
return list;
} public static void main(String[] args) {
System.out.println(addTeacher());
List<TeacherDB> list = queryTeacher();
list.stream().forEach(item->{
System.out.println(item);
});
}
}
五、总结
一般情况下,不建议使用懒汉方式,建议使用饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用登记式/静态内部类的方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁/双重校验锁的方式。本篇就到此结束了,谢谢观看
《java常用设计模式之----单例模式》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- .Net Core 中GC的工作原理
前言 .NET 中GC管理你服务的内存分配和释放,GC是运行公共语言运行时(CLR Common Language Runtime)中,GC可以帮助开发人员有效的分配内存和和释放内存,大多数情况下是不 ...
- oracle不足位数补零的实现sql语句
select rpad('AAA',5,'0') from dual; 这样就可以了 [注意] 1.'AAA'为待补字符:5表示补齐后的总字符长度:0表示不足时补什么字符 2.rpad是右侧补0,左侧 ...
- 内存节省机制C演示
编写代码实质是通过指令对计算机内存进行操作,计算机的硬件设备往往十分有限,尤其是内存.如何使有限的存储空间利用效率达到最大,成为了代码优化首先要考虑的事情. 比如,输入三个数比较大小并输出最小值.下面 ...
- Electron: 如何以 Vue.js, Vuetify 开始应用
Electron: 使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架 Vue.js: Web 前端用于构建用户界面的渐进式框架 Vuetify: Vue.js 的 ...
- MBA都需要学习哪些课程
MBA课程内容详解: 1.核心课程: 管理经济学.营销管理.战略管理.组织行为学.会计学.公司财务管理.人力资源管理与开发.管理与沟通.经济法.国际贸易 2.学位课程: 商务英语(一.二).管理伦理学 ...
- 博弈论Nim取子问题,困扰千年的问题一行代码解决
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构专题26篇文章,我们来看看一个新的博弈论模型--Nim取子问题. 这个博弈问题非常古老,延续长度千年之久,一直到20世纪 ...
- 洛谷 P1991 【无线通讯网】
这道题找到关键点那就是模板题了,代码好写哒~ 因为有S个电话,两个电话就可以连通两个块,那是不是我们就可以看做一条无消耗的边,提前连了起来.根据Kruskal的思想,我们每次加入最小的边,直到联通,由 ...
- Jmeter(十四) - 从入门到精通 - JMeter定时器 - 下篇(详解教程)
1.简介 用户实际操作时,并非是连续点击,而是存在很多停顿的情况,例如:用户需要时间阅读文字内容.填表.或者查找正确的链接等.为了模拟用户实际情况,在性能测试中我们需要考虑思考时间.若不认真考虑思考时 ...
- salesman,动态规划带一点点贪心。
题目直接链接 分析一下: 这题题意还是比较明白的(少见的一道中文题),他的意思就是:有这么一个无向图:保证联通且点与点直接有唯一的简单路径(说白了就是棵树,根节点是1),每个节点有一个权值(有正有负) ...
- 洛谷CF997A:Convert to Ones
温馨提示: 本题适合先思考再看题解,相信各位神犇都能轻轻松松过掉它. 题目链接: https://www.luogu.com.cn/problem/CF997A 分析: 首先要读懂题,to ones, ...