设计模式分为三种类型,共23种
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于 创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类
只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
当您想控制实例数目,节省系统资源的时候
比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session
对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个
SessionFactory就够,这是就会使用到单例模式。
方式 | 是否推荐 |
---|---|
1 饿汉式(静态常量) | 可用 |
2 饿汉式(静态代码块) | 可用 |
3 懒汉式(线程不安全) | 禁止 |
4 懒汉式(线程安全,同步方法) | 不推荐 |
5 懒汉式(线程安全,同步代码块) | 错误 |
6 双重检查 | 推荐 |
7 静态内部类 | 推荐 |
8 枚举 | 推荐 |
特点 | |
---|---|
是否 Lazy 初始化 | 否 |
是否多线程安全 | 是 |
实现难度 | 易 |
描述:
代码实现:
package 设计模式.单例模式;
/**
* 静态常亮
*
* @author 孙一鸣 on 2020/2/4
*/
public class 饿汉式1 {
public static void main(String[] args) {
Singleton instance=Singleton.getSingleton();
Singleton instance2=Singleton.getSingleton();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton{
//1.构造器私有化
private Singleton() {
}
//2.类内部创建对象实例
//static修饰的属性强调它们只有一个,final修饰的属性表明是一个常数(创建后不能被修改)。static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问。
private final static Singleton SINGLETON = new Singleton();
//3.提供一个共有的静态方法,返回实例
public static Singleton getSingleton(){
return SINGLETON;
}
}
优缺点说明:
代码分析:
本次饿汉式代码实现围绕着静态常亮四字来展开,说到静态常亮就得说说关键字
static 和 final
static:关键字可以用来修饰代码块表示静态代码块,修饰成员变量表示全局静态成员变量,修饰方法表示静态方法。
静态是相对于动态的,动态是指Java程序在JVM上运行时,JVM会根据程序的需要动态创建对象并存储对象(分配内存),对象使命结束后,对象会被垃圾回收器销毁,即内存回收由JVM统一管理并分配给其他新创建的对象;静态是指Java程序还没有运行时,JVM就会为加载的类分配空间存储,被static关键字修饰的内容;如静态成员变量,Java类加载到JVM中,JVM会把类以及类的静态成员变量存储在方法区,我们知道方法区是线程共享且很少发生GC的区域,所以被static关键字修饰的内容都是全局共享的,且只会为其分配一次存储空间。
实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)” ,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口interface)的一个实现,jdk1.7的版本中,已经将原本放在永久代的字符串常量池移走。
JDK1.8以后 。永久代被移除,转换为元空间。
final: final修饰的属性表明是一个常数(创建后不能被修改)
特点 | |
---|---|
是否 Lazy 初始化 | 否 |
是否多线程安全 | 是 |
实现难度 | 易 |
描述:
代码实现:
package 设计模式.单例模式;
/**
* 静态代码块
*
* @author 孙一鸣 on 2020/2/4
*/
public class 饿汉式2 {
public static void main(String[] args) {
Singleton2 instance=Singleton2.getSingleton();
Singleton2 instance2=Singleton2.getSingleton();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton2{
//1.构造器私有化
private Singleton2() {
}
//2.类内部创建对象实例
//static修饰的属性强调它们只有一个,static 可以通过类名访问。
private static Singleton2 SINGLETON ;
static {
SINGLETON = new Singleton2();
}
//3.提供一个共有的静态方法,返回实例
public static Singleton2 getSingleton(){
return SINGLETON;
}
}
优缺点说明:
特点 | |
---|---|
是否 Lazy 初始化 | 是 |
是否多线程安全 | 否 |
实现难度 | 易 |
代码实现:
package 设计模式.单例模式;
/**
* 线程不安全
*
* @author 孙一鸣 on 2020/2/4
*/
public class 懒汉式3 {
public static void main(String[] args) {
Singleton3 instance=Singleton3.getSingleton();
Singleton3 instance2=Singleton3.getSingleton();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton3{
private static Singleton3 instance;
private Singleton3(){};
//提供一个静态的公有方法,使用到该方法时才去创建对象
public static Singleton3 getSingleton(){
if (instance == null){
instance = new Singleton3();
}
return instance;
}
}
优缺点说明:
特点 | |
---|---|
是否 Lazy 初始化 | 是 |
是否多线程安全 | 是 |
实现难度 | 易 |
代码实现:
package 设计模式.单例模式;
/**
* 线程安全
*
* @author 孙一鸣 on 2020/2/4
*/
public class 懒汉式4 {
public static void main(String[] args) {
Singleton4 instance=Singleton4.getSingleton();
Singleton4 instance2=Singleton4.getSingleton();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton4{
private static Singleton4 instance;
private Singleton4(){};
//提供一个静态的公有方法,使用到该方法时才去创建对象
public synchronized static Singleton4 getSingleton(){
if (instance == null){
instance = new Singleton4();
}
return instance;
}
}
优缺点说明:
代码实现:
package 设计模式.单例模式;
/**
* 同步代码块 错误案例
*
* @author 孙一鸣 on 2020/2/4
*/
public class 饿汉式5 {
public static void main(String[] args) {
Singleton5 instance=Singleton5.getSingleton();
Singleton5 instance2=Singleton5.getSingleton();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton5{
private static Singleton5 instance;
private Singleton5(){};
//提供一个静态的公有方法,使用到该方法时才去创建对象
public static Singleton5 getSingleton(){
if (instance == null){
synchronized (Singleton.class){
instance = new Singleton5();
}
}
return instance;
}
}
缺点说明:
特点 | |
---|---|
是否 Lazy 初始化 | 是 |
是否多线程安全 | 是 |
实现难度 | 难 |
代码实现:
package 设计模式.单例模式;
/**
*
*
* @author 孙一鸣 on 2020/2/4
*/
public class 双重检查 {
public static void main(String[] args) {
Singleton6 instance=Singleton6.getSingleton();
Singleton6 instance2=Singleton6.getSingleton();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton6{
private static volatile Singleton6 instance;
private Singleton6(){};
//提供一个静态的公有方法,使用到该方法时才去创建对象
public static Singleton6 getSingleton(){
if (instance == null){
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton6();
}
}
}
return instance;
}
}
优缺点说明:
代码分析:
双重检查使用了多线程中常用关键字 volatile
1.volatile是JAVa虚拟机提供的轻量级的同步机制,有三大特点:
1.1保证可见性 1.2 不保证原子性 1.3禁止指令重排
特点 | |
---|---|
是否 Lazy 初始化 | 是 |
是否多线程安全 | 是 |
实现难度 | 一般 |
代码实现:
package 设计模式.单例模式;
/**
* @author 孙一鸣 on 2020/2/4
*/
public class 静态内部类7 {
public static void main(String[] args) {
Singleton8 instance=Singleton8.getInstance();
Singleton8 instance2=Singleton8.getInstance();
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode="+ instance2.hashCode());
}
}
class Singleton8{
//构造器私有化
private Singleton8(){};
//写一个静态内部类,该类有静态属性Singleton
private static class SingletonInstance{
private static final Singleton8 SINGLETON_8= new Singleton8();
}
//提供静态公有方法
public static synchronized Singleton8 getInstance(){
return SingletonInstance.SINGLETON_8;
}
}
代码分析:
静态内部类: 使用它的时候才会加载
静态变量,静态方法,静态块等都是类级别的属性,而不是单纯的对象属性。他们在类第一次被使用时被加载(记住,是一次使用,不一定是实例化)。我们可以简单得用 类名.变量 或者 类名.方法来调用它们。与调用没有被static 修饰过变量和方法不同的是:一般变量和方法是用当前对象的引用(即this)来调用的,静态的方法和变量则不需要。从一个角度上来说,它们是共享给所有对象的,不是一个角度私有。这点上,静态内部类也是一样的。
静态内部类的加载过程:
静态内部类的加载不需要依附外部类,在使用时才加载。不过在加载静态内部类的过程中也会加载外部类。以上花了很多功夫来说明了
特点 | |
---|---|
是否 Lazy 初始化 | 否 |
是否多线程安全 | 是 |
实现难度 | 易 |
代码实现:
package 设计模式.单例模式;
/**
* @author 孙一鸣 on 2020/2/4
*/
public class 枚举8 {
public static void main(String[] args) {
Singleton9 instance=Singleton9.INSTACNCE;
Singleton9 instance2=Singleton9.INSTACNCE;
System.out.println(instance == instance);
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode="+ instance2.hashCode());
instance.sayOK();
}
}
enum Singleton9{
INSTACNCE;
public void sayOK(){
System.out.println("ok~");
}
}
分析:
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。