笔记1:设计模式 笔记1 | 五个创建型模式 | Java 语言实现 | 工厂方法设计模式 | 抽象工厂模式 |建造者模式 | 单例模式 | 原型模式_程序喵 尤Ni的博客-CSDN博客
创建型设计模式 主要有五种,分别是:
接下来用简要的一句话概括这些设计模式的定义和主要组成部分:
定义:提供符合开闭原则的工厂对象,支持获取同一类型的产品。
特点:工厂和产品都有抽象和具体实现两种。
主要组成:
定义:为访问类提供一组相关的接口,支持获取多种类型的产品。
特点:工厂方法的升级版,符合开闭原则。
主要组成:
定义:将创建对象的过程根据其属性分成多个步骤,每一个步骤支持单独处理部分的属性。
特点:可以和工厂方法模式结合使用,封装性好
主要组成:
其中抽象建造者和具体建造者可以合并,直接放在产品角色中,即静态内部类的方式实现建造者模式,这种方式比较常用,其组成图如下:
定义:只有一个单例对象,通常使用 static 关键字实现
特点:分为懒汉式 和 饿汉式两种,前者在多线程环境下需要用到 synchronized 同步,防止多线程抢占资源
主要组成:只有一个单例对象,贴一个不使用 synchronized ,而用 Java类加载机制完美实现懒汉式的代码:
public class Singleton {
private Singleton(){} // 私有化无参构造
private static class Holder{ // 静态内部类
// 根据类加载特性, 仅使用Singleton类时,不会对静态内部类进行初始化
private final static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return Holder.INSTANCE; // 直接获取内部类的静态实例
}
}
定义:用已创建的对象实例作为原型,通过复制该原型对象来创建一个和原型相同货类似的新对象。
特点:分为浅克隆和深克隆两种,Object类的clone() 属于 浅克隆,比直接new快
主要组成:
参考笔记:https://blog.csdn.net/Unirithe/article/details/125911608
logback** **是一种日志管理框架,由 log4j (流行的日志管理框架)创始人推出,是 log4j 的升级版,同时也是 SpringBoot 默认的日志管理框架
日志级别分类以及优先级为:ERROR > WARN > INFO > DEBUG >TRACE
logback 架构:
主要的三个接口:
使用到工厂模式的类:LoggerFactory.java
public final class LoggerFactory {
private LoggerFactory() {} // 构造方法私有化
public static Logger getLogger(Class<?> clazz) {
...
}
}
LoggerFactory 是 logback-classic 提供的 日志工厂类,我们一般是通过这个工厂里的方法来获取日志对象 Logger 的。
getLogger 是 日志工厂提供的静态方法,是重载的方法,最常见的有 String 和 Class两个参数:
public static Logger getLogger(Class<?> clazz) {
// 获取字节码的名称,调用重载方法获取 Logger
Logger logger = getLogger(clazz.getName());
...
return logger;
}
public static Logger getLogger(String name) {
// 获取工厂实现类
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static ILoggerFactory getILoggerFactory() {...}
通过源码可以看出,getLogger 是日志工厂里可以获取日志的方法,,其本质吊用了日志工厂对应的抽象工厂,即 iLoggerFactory 接口的 getLogger方法。
参考笔记:
Spring 是 Java EE 编程领域的一个轻量级开源框架,集成了各类型的工具,通过核心的 BeanFactory 实现了底层的类的实例化和生命周期的管理
Spring特点:IOC 控制反转,DI 依赖注入,AOP 面向切面编程
Spring中的抽象工厂模式:
BeanFactory 接口声明了一系列关于获取 Bean 的方法:
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
// Bean 自带的前缀
String FACTORY_BEAN_PREFIX = "&";
// 1. 根据名称获取 Bean
Object getBean(String name) throws BeansException;
// 2. 根据名称获取 Bean, 并转化为指定的对象
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
// 3. 根据名称获取 Bean, 并传入构造该Bean的参数,用于有参构造
Object getBean(String name, Object... args) throws BeansException;
// 4. 根据字节码获取 Bean
<T> T getBean(Class<T> requiredType) throws BeansException;
// 5. 根据字节码获取 Bean,并传入构造该Bean的参数,用于有参构造
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 6. 根据字节码获取指定 Bean 的提供者
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
// 7. 根据指定的解析类型获取 Bean 的提供者
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
// 8. 判断工厂是否包含 Bean
boolean containsBean(String name);
// 9. 根据名称判断 Bean 是否为单例的对象
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 10. 根据名称判断 Bean 是否为原型对象
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// 11. 根据名称判断 Bean 是否能被指定的可解析类型解析
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
// 12. 根据名称判断 Bean 是否被指定的字节码对应的类进行解析
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// 13. 根据名称获取 Bean 的类型对应的字节码
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 14. 根据名称获取 Bean 的类型对应的字节码,并设置是否允许Bean初始化
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
// 15. 获取 Bean的别名
String[] getAliases(String name);
}
Spring 提供的 BeanFacory接口,具有多个抽象的实现类,可视为抽象工厂:
其中比较眼熟的就是 AbstractApplicatoinContext,我们获取 Spring 中容器里的 Bean时就是通过这个应用上下文的实现类,其定义为:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
}
传统方式获取 Spring 容器中的 Bean:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean(User.class);
其中 ClassPathXmlApplicationContext 类就是之前 AbstractApplicationContext 的实现子类,不过并不是直接的实现子类
而 ApplicationContext 其实就是 BeanFactory 的子接口
所以之前获取 Bean 的方法getBean(User.class),本质上就是调用了 BeanFactory 接口声明的方法。
User user = (User) context.getBean(User.class);
总结:
Spring 底层定义了 BeanFactory 工厂接口,用于声明关于获取 Bean 的方法,该接口有若干个 AbastractBeanFactory 抽象工厂的子类,包括AbstractApplicationContext 子类,这个子类实现了 ApplicationContext接口, 而后者这个接口就是 BeanFactory 的子接口,即 AbstractApplicationContext间接实现了 BeanFactory 接口。
以上的设计模式是标准的抽象工厂方法的设计模式。
java.lang.StringBuilder
是 JDK 源码提供的支持 String 类型一系列操作的类
在创建 StringBuilder() 对象时,可以使用 append 方法添加字符串内容,也可以做创建后调用 append() ,例如:
String s = new StringBuilder()
.append("hello, ")
.append("Wolrd!")
.toString();
toString() 可视为建造者的 build () 方法
StringBuilder 拓展: 部分源码
public final class StringBuilder
extends AbstractStringBuilder
implements Serializable, CharSequence
{
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
...
@Override
public String toString() {
// 创建拷贝对象, 不分享内部的字符数组
return new String(value, 0, count);
}
}
StringBuilder 内部类存储字符串的变量为 char[] value
这个字符串数组是存在其父类 AbstactStringBuilder
中的,在 StringBuilder 的无参构造中,初始化该字符数组的长度为 16
public StringBuilder() {
super(16);
}
SpringBuilder 是有扩容机制的,初始化的字符长度为16,当长度超过这个值时,则会调用 ensureCapacityInternal() 方法来进行扩容,这个方法是在其抽象父类 AbstractStringBuilder中实现的:
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
接下来可以通过调试观察其扩容过程:
测试代码:
String s = new StringBuilder()
.append("1234567890")
.append("1234567")
.toString();
调试过程:
Mybatis 提供了 SqlSessionFactoryBuilder 类用于创建 SqlSessionFactory,即创建 Sql 会话工厂对象。这里是建造者模式和工厂模式的综合使用
public class SqlSessionFactoryBuilder {
...
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
build () 方法根据 Configuration 配置对象创建一个默认的工厂对象。
在 SqlSessionFactoryBuilder 类中有几个重载的 build 方法,其中主要的实现为:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建建造者 XMLConfigBuilder的 实例
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// XMLConfigBuilder 的 parse() 创建 Configuration 实例
return build(parser.parse());
} catch (Exception e) {
// 捕获异常
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
// 重置错误
ErrorContext.instance().reset();
try {
// 释放资源
inputStream.close();
} catch (IOException e) {
// IO 错误不进行处理
}
}
}
}
这里的 XMLConfigurBuilder 是用于创建 Configuration 的建造者,可以视为建造者模式中的具体建造者。
java.lang.Runtime
是从 JDK 1.0 开始出现的,用于启动应用程序的类
【例1】使用 Runtime 打开系统的记事本
Runtime.getRumtime().exec("notepad");
【例2】使用 Runtime 实现 10秒后 自动关机
Runtime.getRuntime().exec("shut down -s -t 10")
在 Runtime 源码实现中,使用了 饿汉式的单例模式,部分源码如下:
public class Runtime{
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime(){
return currentRuntime;
}
private Rumtime(){}
...
}
Java 提供了 boolean、byte、int 、float、double、char[] 等基本类型对应的引用类型,分别是 Boolean、Byte、Integer、Float、Double、String,本次只用了解 Integer
JDK 8 中 Integer 是 int 类型的引用类型,它们之间可以互相转换,这个过程称作自动装箱和自动拆箱,例:
public class Demo{
public static void main(String[] args) {
Integer a = 10;
int b = a;
System.out.println(a + "=" + b);
}
}
Integer ->int 称为 自动拆箱,本质上调用的是 Integer 的 intValue() 方法
public int intValue() {
return value;
}
int -> Integer 称为自动装箱,本质上调用的是 Integer 的 valueOf () 方法:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Integer 默认提供了 [ -128 , 127 ] 数据之间的 IntegerCache 缓存数组
当我们定义的 Integer 型变量属于这个范围时,valueOf () 方法会直接返回 IntegerCache数组里的 Integer 对象
IntegerCache是 Integer 的静态内部类
package java.lang;
public final class Integer extends Number implements Comparable<Integer> {
...
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
}
通过源码可以看出,IntegerCache 是单例模式中饿汉式的实现,其主要特点有:
Spring 中,Bean 一般有两种定义方式,分别是 prototype(支持多例)和 singleton (单例)
接下来,通过几个例子体会其中的单例模式
【例1】在 SpringBoot 中,往 IOC 容器注册单例模式的 Bean
@SpringBootApplication
public class WebDemoApplication {
@Bean
@Scope("singleton")
public String uuid(){
return UUID.randomUUID().toString();
}
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(WebDemoApplication.class, args);
String id1 = context.getBean(String.class);
String id2 = context.getBean(String.class);
System.out.println("id1:" + id1);
System.out.println("id2:" + id2);
}
}
运行结果: 输出了相同的id
id1:8b58cfd1-ba2c-4759-96d9-fbc44f8a66b5
id2:8b58cfd1-ba2c-4759-96d9-fbc44f8a66b5
【例2】SpringBoot 往 IOC 容器注册多例模式 的 Bean
@Bean
@Scope("prototype")
public String uuid(){
return UUID.randomUUID().toString();
}
Spring 注入 Bean 时默认是 单例模式
java.util.ArrayList
中重写了 祖先类 Object 的 clone() 方法,但本质上还是浅克隆
package java.util;
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
public Object clone() {
try {
// 1. 直接克隆整个列表
ArrayList<?> v = (ArrayList<?>) super.clone();
// 2. 调用 Ararys.copyof 克隆列表的每个元素
v.elementData = Arrays.copyOf(elementData, size);
// 3. 初始化modCount, 表示列表在结构上被修改的次数
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
}
ArrayList 虽然重写了 clone方法,但是依然无法克隆列表里的引用类型,这里推荐使用下面的流处理方式来实现深克隆,使用这种方式要求列表里的元素都实现 Serializable 接口
// 1. 创建字节数组输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 2. 转化为对象输出流
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 3. 将深克隆的对象写入到对象输出流
oos.writeObject(src);
// 4. 将 字节数组输出流 读取为 字节数组输入流
ByteArrayInputStream bai = new ByteArrayInputStream(bos.toByteArray());
// 5. 转化为对象输入流
ObjectInputStream ois = new ObjectInputStream(bai);
// 6. 读取对象
return (List) ois.readObject();
工厂方法设计模式 logback
抽象工厂模式 Spring
建造者模式 StringBuilder、Mybatis
单例模式 Runtime、Integer、Spring
原型模式 Object.clone() ,