常用设计模式及其在jdk中案例总结

观察者模式

在这里插入图片描述

多个观察者监听同一个主题对象,是一种一对多的依赖关系,当主题对象发生变化时,会通知所有观察者对象。

java.util.EventListener
javax.servlet.http.HttpSessionBindingListener
javax.servlet.http.HttpSessionAttributeListener

装饰者模式

在这里插入图片描述

Component 是一个接口或者是抽象类,在装饰者模式中,必然有一个被提取出来最核心、最原始、最基本的接口或抽象类。

Decorator 一般是一个抽象类,在它的属性里必然有一个 private 变量指向 Component。

动态的给一个对象附加额外的功能,这也是子类的一种替代方式。

装饰原有对象、在不改变原有对象的情况下扩展增强新功能/新特征.。

java.io.BufferedInputStream(InputStream)
java.io.DataInputStream(InputStream)
java.io.BufferedOutputStream(OutputStream)
java.util.zip.ZipOutputStream(OutputStream)
java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap

建造者模式

在这里插入图片描述

定义了一个新的类来构建另一个类的实例,以简化复杂对象的创建

建造者模式使用的场景,一是产品类非常的复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式是非常合适

java.lang.StringBuilder#append()
java.lang.StringBuffer#append()
java.sql.PreparedStatement

适配器模式

在这里插入图片描述

主要用于接口转换

java.util.Arrays#asList()
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)

简单工厂模式

在这里插入图片描述

定义一个创建类的接口,让类的实例化延迟到子类中

java.lang.Proxy#newProxyInstance()
java.lang.Object#toString()
java.lang.Class#newInstance()
java.lang.reflect.Array#newInstance()
java.lang.reflect.Constructor#newInstance()
java.lang.Boolean#valueOf(String)
java.lang.Class#forName()

抽象工厂模式

在这里插入图片描述

在简单工厂模式的基础上,对工厂进一步抽象为一个接口

java.util.Calendar#getInstance()
java.util.Arrays#asList()
java.sql.DriverManager#getConnection()
java.sql.Connection#createStatement()
java.sql.Statement#executeQuery()
java.text.NumberFormat#getInstance()

单例模式

在这里插入图片描述

1、非线程安全懒汉模式

public class SingletonDemo {
    
    
	private static SingletonDemo instance;
	private SingletonDemo(){
    
    
	}
	public static SingletonDemo getInstance(){
    
    
		if(instance==null){
    
    
		instance=new SingletonDemo();
		}
		return instance;
	}
}

2、 线程安全懒汉模式

public class SingletonDemo {
    
    
	private static SingletonDemo instance;
	private SingletonDemo(){
    
    
	}
	public static synchronized SingletonDemo getInstance(){
    
    
		if(instance==null){
    
    
		instance=new SingletonDemo();
		}
		return instance;
	}
}

3、饿汉模式

直接在运行这个类的时候进行一次 loading,之后直接访问。显然,这种方法没有起到lazy loading 的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。

public class SingletonDemo {
    
    
	private static SingletonDemo instance=new SingletonDemo();
	private SingletonDemo(){
    
    
	}
	public static SingletonDemo getInstance(){
    
    
		return instance;
	}
}

4、静态类内部加载

使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用 getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的。

public class SingletonDemo {
    
    
	private static class SingletonHolder{
    
    
		private static SingletonDemo instance=new SingletonDemo();
	}
	private SingletonDemo(){
    
    
		System.out.println("Singleton has loaded");
	}
	public static SingletonDemo getInstance(){
    
    
		return SingletonHolder.instance;
	}
}

5、双重锁校验模式

public class SingletonDemo {
    
    
	private volatile static SingletonDemo instance;
	private SingletonDemo(){
    
    
		System.out.println("Singleton has loaded");
	}
	public static SingletonDemo getInstance(){
    
    
		if(instance==null){
    
    
			synchronized (SingletonDemo.class){
    
    
				if(instance==null){
    
    
					instance=new SingletonDemo();
				}
			}
		}
		return instance;
	}
}

接下来我解释一下在并发时,双重校验锁法会有怎样的情景:

  • STEP 1. 线程 A 访问 getInstance()方法,因为单例还没有实例化,所以进入了锁定块。
  • STEP 2. 线程 B 访问 getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程 1锁定。
  • STEP 3. 线程 A 进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。
  • STEP 4. 线程 B 进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。
  • STEP 5. 线程 A 初始化并获取到了单例实例并返回,线程 B 获取了在线程 A 中初始化的单例。

理论上双重校验锁法是线程安全的,并且,这种方法实现了 lazyloading。

7、懒汉模式与饿汉模式区别

饿汉模式是最简单的一种实现方式,饿汉模式在类加载的时候就对实例进行创建,实例在整个程序周期都存在。它的好处是只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。 它的缺点也很明显 , 即使这个单例没有用到也会被创建 , 而且在类加载之后就被创建,内存就被浪费了。

9、双重校验锁方法与线程安全的懒汉模式区别

可以看到上面在同步代码块外多了一层 instance 为空的判断。由于单例对象只需要创建一次,如果后面再次调用 getInstance()只需要直接返回单例对象。因此,大部分情况下,调用 大部分情况下,调用 getInstance()都不会执行到同步代码 都不会执行到同步代码块, ,从而提高了程序性能。不过还需要考虑一种情况,假如两个线程 A、B,A 执行了 if (instance == null)语句,它会认为单例对象没有创建,此时线程切到 B 也执行了同样的语句,B 也认为单例对象没有创建然后 两个线程依次执行同步代码块 , 并分别创建了一个单例对象 。为了解决这个问题,还需要在同步代码块中增加 if(instance == null)语句,也就是上面看到的代码 2。11. 单例模式与静态变量区别首先解决引用的唯一实例可能被重新赋值的问题,单例模式中的 getInstance()静态方法实现时,1.采用懒汉式创建一个对象(当然这只是创建方式的一种),规避了这一风险,无则创建,有则跳过创建。2.其次,getInstance()静态方法定义在该类的内部,获取该类对象的引用位置非常明确,无需额外的沟通商定,团队成员拿起即用。最后一个区别并不是很明显,声明一个静态变量,4.实际上,我们会直接对其进行初始化赋值,这样,在内存占用上,所占用的内存为该初始化赋值对象实际的内存。而单例模式可以通过懒汉创建法延迟该内存的占用,要知道,当一个静态变量只进行声明,而不进行初始化时,实际的内存占用只有 4 个字节

代理模式

指目标对象给定代理对象,并由代理对象代替真实对象控制客户端对真实对象的访问。、

java.lang.reflect.Proxy

java.rmi.*

模版方法模式

在这里插入图片描述

让子类可以重写方法的一部分,而不是整个重写,你可以控制子类需要重写那些操作。

其中 TemplateMethod 就是模板方法,operation1 和 operation2 就是基本方法,模板方法是通过汇总或排序基本方法而产生的结果集。

•java.io.InputStream、OutputStream、Reader、Writer的所有非抽象方法

•java.util.AbstractList、AbstractSet、AbstractMap的所有非抽象方法

•javax.servlet.http.HttpServlet#doXXX()

猜你喜欢

转载自blog.csdn.net/qq_43186095/article/details/103836017