前言:
java语言基础部分告一段落了,接下来就进入java进阶篇:设计模式+框架。
简单的来说:设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
1、开闭原则(Open Close Principle)
对扩展开放,对修改关闭。
2、里氏代换原则(Liskov Substitution Principle)
对实现抽象化的具体步骤的规范,(子类尽量不要重写和重载父类方法)
3、依赖倒转原则(Dependence Inversion Principle)
对象接口编程,依赖于抽象。
4、接口隔离原则(Interface Segregation Principle)
4、接口隔离原则(Interface Segregation Principle)
降低类之间的耦合度。拆出子类不需要的抽象方法。
5、迪米特法则,又称最少知道原则(Demeter Principle)
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。(封装)
6、合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。
根据模式的目的(用于完成什么样的工作)可以将23种设计模式分为以下3类:
具体分类如下所示:
类型 | 具体设计模式 |
---|---|
创建型模式 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
用一幅经典图来描述设计模式之间的关系。
对于任何一种设计模式的学习,我们主要掌握以下几点:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决当一个全局使用的类频繁的创建与销毁这类问题。
结构如图:
优点
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
对于单例模式来说,具体实现方式有很多种。具体可以归纳为以下7种:
核心:类被加载时,单例对象已经创建 (类实例化前已经创建)
缺点:不可控,可能造成极大的内存浪费(单例对象较多时),增大内存开销
优点:线程安全
class HungrySingleton{
//类被加载时,单例对象已经创建 (类实例化前已经创建)
private static final HungrySingleton instance =new HungrySingleton();
//构造方私有化法
private HungrySingleton() {}
//提供一个全局访问点
public static HungrySingleton getInstance () {return instance;}
}
饿汉式的一个简单扩展,将类的实例化放在静态代码块中。
class HungrystaticSingleton{
//类被加载时,单例对象已经创建
private static final HungrystaticSingleton instance;
static {
instance =new HungrystaticSingleton();
}
//构造方私有化法
private HungrystaticSingleton() {}
//提供一个全局访问点
public static HungrystaticSingleton getInstance () {return instance;}
}
延迟加载,用的时候在创建。启动速度快。
优点 :解决了资源浪费问题
缺点:增加了个Instance()方法的逻辑复杂性,可能出现线程安全问题(线程不安全,在多线程情况下出现多个实例)
class LazySimpleSingleton{
private static LazySimpleSingleton instance =null;
private LazySimpleSingleton () {}
public static LazySimpleSingleton getInstance () {
if(instance==null)
instance=new LazySimpleSingleton();
return instance;
}
}
增加同步锁(同步方法),解决多线程下的线程安全问题。
缺点:性能下降。
class LazySimpleSingleton01{
private static LazySimpleSingleton01 instance =null;
private LazySimpleSingleton01() {}
public synchronized static LazySimpleSingleton01 getInstance () {
if(instance==null)
instance=new LazySimpleSingleton01();
return instance;
}
}
同步代码块(创建实例部分),提升了部分性能。
class LazyDoubleCheckSingleton{
private static LazyDoubleCheckSingleton instance =null;
private LazyDoubleCheckSingleton() {}
public static LazyDoubleCheckSingleton getInstance() {
//第二次访问可以优化
if(instance==null) {
//阻塞,没有彻底解决
synchronized(LazyDoubleCheckSingleton.class) { //同步锁
if(instance==null)
instance=new LazyDoubleCheckSingleton();
}
}
return instance;
}
}
采用双重检查锁
class LazyDoubleCheckSingleton01{
// 注意:这里有 volatile 关键字修饰
private static volatile LazyDoubleCheckSingleton instance =null;
private LazyDoubleCheckSingleton() {}
public static LazyDoubleCheckSingleton getInstance() {
//第二次访问可以优化
if(instance==null) {
//阻塞,没有彻底解决
synchronized(LazyDoubleCheckSingleton.class) { //同步锁
if(instance==null)
instance=new LazyDoubleCheckSingleton();
}
}
return instance;
}
}
优点:线程安全;延迟加载;效率较高。
由于 JVM 具有指令重排的特性,在多线程环境下可能出现 instance已经赋值但还没初始化的情况,导致一个线程获得还没有初始化的实例。volatile 关键字的作用:
class LazyInnerClassSingleton{
private LazyInnerClassSingleton() {
if(LazyHolder.instance!=null) {
throw new RuntimeException("");
}
}
public static LazyInnerClassSingleton getInstance() {
return LazyHolder.instance;
}
public static class LazyHolder{
private static final LazyInnerClassSingleton instance=new LazyInnerClassSingleton();
}
}
优点:避免了线程不安全,延迟加载,效率高。
静态内部类的方式利用了类装载机制来保证线程安全,只有在第一次调用getInstance方法时,才会装载SingletonInstance内部类,完成Singleton的实例化,所以也有懒加载的效果。
采用枚举的方式,放在map容器里面,注册一个放一个。(JDK中使用的就是注册式单例)
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
enum EnumSingleton{
INSTANCE;
private Object data;
public Object getData() {return data;}
public void setData(Object data) {this.data=data;}
public static EnumSingleton getInstance() {return INSTANCE; }
}
AbstractFactoryBean 类源码如下:
public final T getObject() throws Exception {
if (this.isSingleton()) {
return this.initialized ? this.singletonInstance : this.getEarlySingletonInstance();
} else {
return this.createInstance();
}
}
private T getEarlySingletonInstance() throws Exception {
Class<?>[] ifcs = this.getEarlySingletonInterfaces();
if (ifcs == null) {
throw new FactoryBeanNotInitializedException(this.getClass().getName() + " does not support circular references");
} else {
if (this.earlySingletonInstance == null) {
// 通过代理创建对象
this.earlySingletonInstance = Proxy.newProxyInstance(this.beanClassLoader, ifcs, new AbstractFactoryBean.EarlySingletonInvocationHandler());
}
return this.earlySingletonInstance;
}
}
ErrorContext 类,通过 ThreadLocal 管理单例对象,一个线程一个ErrorContext对象,ThreadLocal可以保证线程安全
public class ErrorContext {
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
private ErrorContext() {
}
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
//...
}
采用单例模式构建,确保只有一个SqlSession对象存在。
/**
*构建SqlSessionFactory和获取SqlSession对象工具类
* 采用单例模式构建,确保只有一个SqlSession对象存在。
*/
public class SqlSessionFactoryUtils {
private final static Class<SqlSessionFactoryUtils> LOCK=SqlSessionFactoryUtils.class;
private static SqlSessionFactory sqlSessionFactory=null;
/**
*构造方法添加private关键字,确保唯一性,使其它类或者方法不能通用new来创建该类对象。
*/
private SqlSessionFactoryUtils(){}
public static SqlSessionFactory getSqlSessionFactory(){
synchronized (LOCK){
if(sqlSessionFactory!=null){
return sqlSessionFactory;
}
String resource="mybatis_cfg.xml";
InputStream inputStream=null;
try {
inputStream= Resources.getResourceAsStream(resource);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sqlSessionFactory;
}
}
public static SqlSession openSqlSession(){
if(sqlSessionFactory==null){
getSqlSessionFactory();
}
return sqlSessionFactory.openSession();
}
}
修行之路艰辛,与君共勉
2020年3月 成都