特性
- 设计模式特性:
- 可维护性:不引入新的bug情况下,快速新增和修改。
- 灵活性:能快速集成到代码中。
- 可拓展性:对修改关闭,对拓展开放。
- 可读性:易于别人理解。
- 可复用性:可减少代码编写。
- 简洁性:简单易懂。
- 可测试性。
设计原则:
单一职责:设计的内容粒度小,一个操作控制一个动作(一个接口控制一种实现) 判断标准:属性量的多少;依赖类的数量(只对需要的做依赖);私有方法量;方法尽量不要只对其中几个属性做操作
开闭原则⭐:定义的内容是模板,实现时不能更改模板,而是在模板上做拓展。
里氏替换:在多态背景下,接口/方法能接收的参数可以为所有实现了的类;能够根据接口知道相关实现类要做什么。
接口隔离:接口中的方法根据重要性做分离,避免实现时不必要的功能完成;通过实现多个接口完成实际操作。 减少代码冗余;减少粒度,体现层次;
依赖倒置:提供中间层抽象类,上层与下层内容依赖中间层。手机为上层,电话卡槽为抽象层,电话卡可替换为下层。 做到解耦
迪米特:有关系的类采用合适的内容做联系,没有关系的老死不相往来。
设计模式是设计原则的实现。
创建型
单例模式
单例模式的运用非常广泛。目的为了保证只有一个实例向外提供服务。
单例模式分为【饿汉】与【懒汉】两种方式。
饿汉模式
class HSingleton {
private static HSingleton instance = new HSingleton();
private HSingleton{}
public static HSingleton getInstance(){
return instance;
}
}
饿汉模式虽然能保证实例的创建,在JVM创建对象后该实例就能创建成功,虽然能提高获取速度,但是会拉慢启动速度,且长时间未使用时存在内存空间浪费。
懒汉模式 - SIMPLE
为了完善饿汉模式中未提供延迟加载,懒汉模式中增加在调用时才去创建实例。
class LSingleton{
priavte static LSingleton instance = null;
private LSingleton(){
}
public static LSingleton getInstance(){
if(instance == null){
instance = new LSingleton();
}
return instance;
}
}
通过if(instance == null) 判读后去实例化,调用时发现未实例化后才去实例化对象。但是在并发环境下,多个线程进入后判断后没有控制直接进入,初始化过程中,instance都未完成实例,造成对象多次实例。
最后出现 obj1 == obj2 为 false
懒汉模式 - SYNC
为了保证并发下,每次只有一个线程调用到getInstance()并进行实例化,其他线程进入时将阻塞一直等待。
class LSingleton{
priavte static LSingleton instance = null;
private LSingleton(){
}
public static synchronized LSingleton getInstance(){
if(instance == null){
instance = new LSingleton();
}
return instance;
}
}
锁加载方法上,粒度很大,导致函数并发能力很低,相当于串行化执行,并发情况下也不是可取方案。
懒汉模式 - DCL
class LSingleton{
priavte volatile static LSingleton instance = null;
private LSingleton(){
}
public static LSingleton getInstance(){
if(instance == null){
synchronized(){
if(instance == null){
instance = new LSingleton();
}
}
}
return instance;
}
}
DCL:DOUBLE CHECK LOCK(双检锁)
volatile:JVM中做实例化过程调用时。1、保证修改后被其他线程可见;2、多线程中保证代码执行后的结果,不会出现指令重排
指令重排发生:instance = new LSingleton() 在JVM中做了以下三件事
给instance 分配内存空间
调用instance构造方法,初始化instance
instance 指向分配好的内存空间地址。
实际在分配了内存空间后,可能先初始化,也可能先指向内存地址。
如果先执行分配好的内存地址,instance就不为空了,其他线程等到后使用发现为null,从而发生异常。
静态内部类
静态内部类的特性能保证外部影响不到内部的调用。在调用getInstance()方法时,方法中去获取静态内部类中的实例。而JVM对于静态内部类,只会初始化一次。
class Singleton{
private static class SingletonHandler{
private static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHandler.instance;
}
}
基于反射创建
单例模式中实例的创建由私有方法控制,而反射想要创建实例,则要破坏属性的私有性。
public static void main(String[] args) {
try {
//反射中,欲获取一个类或者调用某个类的方法,首先要获取到该类的Class 对象。
Class<Singleton> clazz = Singleton.class;
//getDeclaredXxx: 不受权限控制的获取类的成员.
Constructor c = clazz.getDeclaredConstructor(null);
//设置为true,就可以对类中的私有成员进行操作了
c.setAccessible(true);
Object instance1 = c.newInstance();
Object instance2 = c.newInstance();
System.out.println(instance1 == instance2); // 返回为false
} catch (Exception e) {
e.printStackTrace();
}
}
为了控制只有一个实例生成,在构造方法中添加对实例的判断,若为null则抛出异常。
private Singleton(){
if(SingletonHandler.instance != null){
throw new RuntimeException("不能多次创建!");
}
}
这样操作后,我只想在做创建操作,结果又多了抛出异常,影响该功能的简洁性。
序列化
序列化本身不能保证一个类的对象只有一个,但是可以通过字节流写出在读取确保单例。
class Singleton implements Serializable {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
@Test
public void test() throws Exception{
//序列化对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file.obj"));
oos.writeObject(Singleton.getInstance());
//序列化对象输入流
File file = new File("file.obj");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Singleton Singleton = (Singleton) ois.readObject();
System.out.println(Singleton);
System.out.println(Singleton.getInstance());
//判断是否是同一个对象
System.out.println(Singleton.getInstance() == Singleton);//false
}
是不是还没解决,发现还是false。
这是因为序列化与反序列化破坏了单例性。
那是谁破坏了单例性呢?
通过方法可知,在Singleton Singleton = (Singleton) ois.readObject();中,按照下面链路可追踪明白
// readObject --> readObject0 --> checkResolve(readOrdinaryObject(unshared)) --> readOrdinaryObject
// --> obj = desc.isInstantiable() ? desc.newInstance() : null;
追踪到最后一个方法后,你会发现
try {
// isInstantiable:是否为serializable类,则可在运行时被实例化。这里为true
// newInstance 最终还是通过反射创建的对象,所以
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}