前言
本文章为自己复习做的基础记录,总结,新手可以看看,我会在代码中详细介绍代码含义,以及简单结论,一起学习
饿汉式
懒汉式
双重检索懒汉式
volatile 解释
静态内部类方式(了解即可)
跳转:反射破坏单例及扩展
设计模式概述
设计模式(Design Pattern)是对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。
单例设计模式定义和特点:
- 单例设计模式保证一个类只有一个实例,要提供一个访问该类对 象实例的全局访问点(方法)。(静态方法)
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
注意:记住第一条就可以,下面的只是帮助理解
饿汉式
饿汉式(静态常量)
步骤:
构造器私有化(防止new)
类的内部创建对象
向外暴露一个静态的公共方法 getinstance
//饿汉式 静态常量
public class demo1 {
public static void main(String[] args) {
//测试
Singleton singleton=Singleton.getInstance();//拿到实例
Singleton singleton2=Singleton.getInstance();
System.out.println(singleton==singleton2);//测试判断是否相等,结果应为true
System.out.println(singleton.hashCode()==singleton2.hashCode());//测试判断hashcode地址是否相等 结果应为true
}
}
class Singleton{
//构造器私有化,外部不能new
private Singleton() { }
//本类内部创建对象实例
private static final Singleton instance=new Singleton();
//对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
分析优缺点:
优点:写法简单,在类装载时完成实例化,避免线程同步
缺点:因为在类装载时完成实例化,没有Lazy Loading(懒加载)效果,如果一直没有使用该实例,会造成内存浪费,一上来就全部加载
- 饿汉式第二种方式
饿汉式(静态代码块)
//饿汉式 静态代码块
public class demo2 {
public static void main(String[] args) {
//测试
Singleton2 singleton3=Singleton2.getInstance2();//拿到实例
Singleton2 singleton4=Singleton2.getInstance2();
System.out.println(singleton3==singleton4);//测试判断是否相等,结果应为true
System.out.println(singleton3.hashCode()==singleton4.hashCode());//测试判断hashcode地址是否相等 结果应为true
}
}
class Singleton2{
//构造器私有化,外部不能new
private Singleton2() { }
static {//在静态代码块中,创建单例对象
instance2=new Singleton2();
}
//本类内部创建对象实例
private static Singleton2 instance2 ;
//对外提供一个公有的静态方法,返回实例对象
public static Singleton2 getInstance2(){
return instance2;
}
}
懒汉式
调用的时候才加载
懒汉式(线程不安全)
步骤是一样的,注意看注释
//懒汉式,线程不安全方式
public class Layzsingleton {
public static void main(String[] args) {//测试
Singletonlazy singletonlazy=Singletonlazy.getInstance();
Singletonlazy singletonlazy2=Singletonlazy.getInstance();
System.out.println(singletonlazy==singletonlazy2);
System.out.println(singletonlazy.hashCode()==singletonlazy2.hashCode());
}
}
class Singletonlazy{
private Singletonlazy(){}
private static volatile Singletonlazy instance ;
//提供一个静态的公有方法,当时用该方法时,才创建instance;
public static synchronized Singletonlazy getInstance(){
if (instance==null) {
instance=new Singletonlazy();
}
return instance;
}
}
优缺点说明:
起到了Lazy Loading的效果,但只能在单线程使用
如果在多线程下,一个线程进入if(instance==null)的时候,还未来得及执行new对象,另一个线程也通过这个判断语句,就会产生多个实例
第二种懒汉式(线程安全,双重检索)重点
//双重检查
public class lazySungletonave {
public static void main(String[] args) {
SingletonSave s=SingletonSave.getInstancesave();
SingletonSave s2=SingletonSave.getInstancesave();
System.out.println(s==s2);
System.out.println(s.hashCode()==s2.hashCode());
}
}
class SingletonSave {
private SingletonSave(){
}
private static volatile SingletonSave instancesave;//关键字volatile,先初始化完成后再赋值给实例(记住这句话就好了)
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,解决懒加载
public static SingletonSave getInstancesave(){
if (instancesave==null) {//在第一次判断instancesave是不是已经有了,没有就创建,有就直接返回下面return 的方法
synchronized (SingletonSave.class){
if (instancesave==null) {
instancesave=new SingletonSave();
}
}
}
return instancesave;//理解的意思指已经创建过一次实例后,第二次判断,直接返回已经创建好了的方法,
// 第一个已经创建的实例依旧在内存中,直接返回调用
}
}
结论:双重检索double-check概念是多线程开发经常使用的,进行两次if语句的判断,就可以保证线程的安全
到此我们总结两点:
1.饿汉式这种方式加载类对象,我们称作:预先加载方式。
2.懒汉式这种方式加载类对象,我们称作:延迟加载方式。
volatile解释,代码注释DLC懒汉式2:
//懒汉式单例
public class Lazyman {
private Lazyman(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Lazyman lazyman;
public static Lazyman getInstance(){
if (lazyman==null) {
synchronized (Lazyman.class){
if (lazyman==null) {
lazyman=new Lazyman();//不是一个原子性操作
/***
* 1,分配内存空间
* 2,执行构造方法,初始化对象
* 3,把这个对象指向这个空间
*
* 123
* 132 A
* 线程B:会认为lazyman!=null,会直接return lazyman;此时,A还没有完成构造,
* 所以lazyman要避免指令重排的要加上volatile
*/
}
}
}
return lazyman;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
Lazyman.getInstance();
}).start();
}
}
}
静态内部类方式:
//静态内部类方式
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
单例会被破坏,反射与枚举文章开头跳转
到这里结束,觉得用心,点个关注呗