持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情
设计模式的理解
设计模式时程序员在面对同类软件工程设计问题所总结出来的有用的经验模式并不是代码,而某类问题的解决的通用方案,设计模式代表了最佳的实践。当然这些解决方案是众多软件开发人员的试验和实践而总结出来。
设计模式的本质就是提高软件的维护性,通用性和扩展性,并降低软件的复杂度。当然设计模式并不局限于某种语言,java,php,c++ 等都有设计模式。当前所公认的为23中设计模式,当然实际开发中并不止这些。
设计模型分为三大类型今天我们就先使用 创建型模式中的 单例模式
创建型模式包含哪些
- 单例模式
- 抽象工厂模式
- 原型模式(深copy和浅copy)
- 建造者模式
- 工厂模式
1.单例模式
从设计层面来讲采取一定的方法使得该类在整个项目或者工程中只能存在一个对象对象,并且不能再次创建且只提供一个获取该对象实例的方法。
2.单例模式的把种方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
3.懒汉式 静态常量方式
public class SingletonTest1 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//通过查看两个对象的hashCode
System.out.println("instance2 = " + instance2.hashCode());
System.out.println("instance = " + instance.hashCode());
}
}
class Singleton{
//构造方法私有化 ,让不能从外部new 实例
private Singleton(){
}
// 类内部创建实例对象
private final static Singleton instance = new Singleton();
//外部提供一个静态公共方法,返回对象实例
public static Singleton getInstance(){
return instance;
}
}
结果输出:可以看到通过静态方法获取到的两个对象是一致的。
instance2 = 1144748369
instance = 1144748369
3.1 总结
优点:写法简单,在类加载的时候就完成实例化,避免了线程同步的问题。
缺点:在类加载的时候就完成了实例化,并没有达到懒加载的效果,如果这个实例对象从始至终都没有
使用过那麽这个对象就会造成内存的浪费。
4 饿汉模式 (静态代码块)
public class SingletonTest1 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("instance2 = " + instance2.hashCode());
System.out.println("instance = " + instance.hashCode());
}
}
class Singleton{
//构造方法私有化 ,让不能从外部new 实例
private Singleton(){
}
private static Singleton instance;
static {
//静态代码块中创建实例对象
instance=new Singleton();
}
//外部提供一个静态公共方法,返回对象实例
public static Singleton getInstance(){
return instance;
}
}
这种方式和 上面的饿汉式方式是一样的都是 在类加载是完成实例,都会造成内存浪费但是可用。
5 懒汉式(线程不安全)
public class SingletonTest2 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
Singleton2 instance2 = Singleton2.getInstance();
System.out.println("instance2 = " + instance2.hashCode());
System.out.println("instance = " + instance.hashCode());
}
}
class Singleton2{
private static Singleton2 instance;
//构造方法私有化 ,让不能从外部new 实例
private Singleton2(){
}
//懒汉式 用到的时候在去创建
public static Singleton2 getInstance(){
if(instance ==null){
instance= new Singleton2();
}
return instance;
}
}
输出结果:
instance2 = 1121172875
instance = 1121172875
5.1 总结
优点:起到了懒加载的效果,但是只能在单线程下使用。
缺点:多线程情况下,会产生多个实例,因为在if判断时多线程情况会出现A线程在判断为空B线程已经
进去了并且创建成功但是 A也进来了创建了实例。所以只能在单线程下使用
6 懒汉式(线程安全,同步方法)
使用 synchronized 修饰 getInstance方法解决多线程创建多个实例
public class SingletonTest2 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
Singleton2 instance2 = Singleton2.getInstance();
System.out.println("instance2 = " + instance2.hashCode());
System.out.println("instance = " + instance.hashCode());
}
}
class Singleton2{
private static Singleton2 instance;
//构造方法私有化 ,让不能从外部new 实例
private Singleton2(){
}
//懒汉式
public static synchronized Singleton2 getInstance(){
if(instance ==null){
instance= new Singleton2();
}
return instance;
}
}
6.1 总结
优点:解决了线程不安全问题。
缺点:使用了synchronized 效率大大降低,每一次使用getInstance获取对象时都要进行同步。然
而这个实例化只需要执行一次。
7 双重检查
public class SingletonTest2 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
Singleton2 instance2 = Singleton2.getInstance();
System.out.println("instance2 = " + instance2.hashCode());
System.out.println("instance = " + instance.hashCode());
}
}
class Singleton2{
//volatile 修改的属性立刻更新
private static volatile Singleton2 instance;
//构造方法私有化 ,让不能从外部new 实例
private Singleton2(){
}
// 使用双重检查的
public static synchronized Singleton2 getInstance(){
if(instance ==null){
synchronized (Singleton2.class){
if(instance ==null){
instance= new Singleton2();
}
}
}
return instance;
}
}
7.1 总结
优点:线程安全;懒加载;效率高;双重检查是多线程开发中经常使用到的,上面代码中进行了两次 if 判断。这样可以保证线程的安全。同时提高了效率,且实例化的代码只执行一次,避免了返回进行方法的同步。
8 静态内部类
public class SingletonTest2 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
Singleton2 instance2 = Singleton2.getInstance();
System.out.println("instance2 = " + instance2.hashCode());
System.out.println("instance = " + instance.hashCode());
}
}
class Singleton2{
//构造方法私有化 ,让不能从外部new 实例
private Singleton2(){
}
//静态内部类,在该类中有一个静态属性 Singleton2
public static class SingletonInit{
private static final Singleton2 INSTANCE= new Singleton2();
}
//获取实例的方法直接返回静态内部类的属性Singleton2
public static Singleton2 getInstance(){
return SingletonInit.INSTANCE;
}
}
8.1 总结
这种方式采用了类装载的机制来保证了初始化实例时只有一个线程,且Singleton2在装载时不会装载SingletonInit类,只有调用getInstance 方法时才会去装载SingletonInit类,来完成Singleton2 的实例化。而且类的静态属性只会在第一次加载类的时候初始化。即jvm帮助我们保证了线程的安全性,类在初始化时是安全的。 避免了线程的不安全同时实现了懒加载效率高。实际开发中推荐使用。
9 枚举
public class SingletonTest2 {
public static void main(String[] args) {
Singleton2 singleton2= Singleton2.INSTANCE;
singleton2.todoOut();
Singleton2 singleton3= Singleton2.INSTANCE;
singleton3.todoOut();
System.out.println("instance2 = " + singleton2.hashCode());
System.out.println("instance = " + singleton3.hashCode());
}
}
enum Singleton2{
INSTANCE;
public void todoOut(){
System.out.println("初始化");
}
}
9.1 总结
这种方式借助JDK 1.5 中添加的枚举来实现单例模式,不仅避免了多线程同步问题,而且还能防止反序列化重新创建对象。 推荐使用
实践是检验真理的唯一方法! 明天见🥰🥰🥰