java设计模式

文章介绍了Java设计模式中的单例模式,包括饿汉式、懒汉式和DCL懒汉式,以及工厂模式和OOP的七大原则。此外,还讨论了如何通过静态内部类实现线程安全的单例,并提到了反射对单例模式的破坏。文章最后提到了简单工厂模式的概念和作用。
摘要由CSDN通过智能技术生成

前言

最近在了解spring的控制反转中可能设计到一些设计模式,做个笔记以防万一
大部分跟着狂神去学的,也有一些查了资料
其中设计模式会设计到oop(面向对象编程)的七大原则,在我们处理某一个功能的时,
绝大多数不能保证代码能够合理运用,IOC中依赖注入类似于工厂模式。
注:更新可能有点慢

设计模式是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

OOP七个原则

开闭原则

对扩展开放,对修改关闭(是最终目的)

里氏替换原则

继承必须确保超类所拥有的性质在子类中仍然成立
尽量不要重写父类(正方形不是长方形)

依赖倒置原则

要面向接口编程,不要面向实现编程
抽象不依赖于细节,细节依赖于抽象。降低耦合性

单一职责原则

控制类的粒度大小、将对象解藕、提高其内聚性
一个方法尽量负责一件事情

接口隔离原则

要为各个类建立它们需要的专用接口

迪米特法则

只与你的直接朋友交谈,不跟“陌生人”说话

合成复用原则

尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

创建型模式

单例模式

单例模式一般指一个类只能有一个实例,这个时候需要去绕过构造器来保证一个类中只包含一个实例。
应用场景:
1、资源共享的情况下,避免由于资源操作时导致的性能或损耗等。应用配置、日志操作。
2、控制资源的情况下,方便资源之间的互相通信。池化技术。

1、饿汉式单例模式

public class Hungry {
    private Hungry() {}
    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}

该单例模式会造成空间浪费的情况,在该基础上进行修改——懒汉式单例模式。

2、懒汉式单例模式

public class LazyMan {
    private LazyMan(){}
    private static LazyMan lazyMan;
    public static LazyMan getInstance(){
        if (lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

该单例模式在单线程的情况下可以正常运行,如果遇到并发情况会出现问题,偶尔会出现多个线程,在该情况下可以做一下修改(DCL)

2-1、DCL懒汉式单例

public class LazyMan {
    private LazyMan(){}
    private static LazyMan lazyMan;
    public static LazyMan getInstance(){
        if (lazyMan == null){
            synchronized (LazyMan.class){
                if (lazyMan == null){
                    lazyMan = new LazyMan();// 不是一个原子性操作
                    /**
                    * 1.分配内存空间
                    * 2.执行构造方法,初始化对象
                    * 3.把这个对象指向这个空间
                    */
                }
            }
        }
        return lazyMan;// 有可能执行132,还没完成初始化
    }
}

该方法也会出现一些问题
修改方法如下:

private static LazyMan lazyMan;
//修改为
private volatile static LazyMan lazyMan;

3、静态内部类

public class Holder {
    private Holder(){ }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

注意事项

以上的几种单例模式都是不安全的,单例模式情况可以通过反射的方式去进行暴力破坏。

    // 懒汉式单例下反射破解
    public static void main(String[] args) throws Exception {
        LazyMan lazyMan = LazyMan.getInstance();
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan lazyMan1 = constructor.newInstance();
        System.out.println(lazyMan);
        System.out.println(lazyMan1);
    }

结果
可以看出通过反射方式根据构造器创建的话破坏了单例模式。
解决方式也可以在构造器中去判断一下。

private LazyMan(){
        synchronized (LazyMan.class){
            if(lazyMan != null){
                throw new RuntimeException("反射构造");
            }
        }
}

但是问题来了,如果是两个都是反射创建的话,他还是会正常执行。为什么会出现这种情况?
我的个人理解是,通过构造器创建的话,它的lazyMan值都为null,两次创建都不会通过判断。其实getInstance方法获取到的lazyMan对象才是主要,如果在构造创建之前通过getInstance方法去获取对象是,该方法可以判断是否为反射创建。
解决方法:

// 创建一个隐藏变量去做判断
	private static boolean hide = false;

    private LazyMan(){
        synchronized (LazyMan.class){
            if (hide == false){
                hide = true
            }else {
                throw new RuntimeException("反射构造");
            }
        }
    }

每天一个入狱小技巧:如果我们知道这个隐藏变量通过反射去修改呢?
反射源码
狂神说了:通过源码分析怎么去解决,如果是枚举类型不能用反射来破坏,枚举自带单例模式。
枚举本身也是一个class类

public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

工厂模式

作用:实现了创建者和调用者分离
详细分类: 简单工厂模式、工厂方法模式、抽象工厂模式
实现原则:开闭原则、依赖倒转原则、迪米特法则
核心本质:实例化对象不使用new,用工厂方法代替;将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解藕

1、简单工厂模式(静态工厂模式)

用来生产同一等级结构中的任意产品(对于增加的新产品,覆盖已有的代码)

狂神的例子:买车!
创建一个汽车的接口,Car

public interface Car {
    void name();
}

wuling

public class wuling implements Car{
    @Override
    public void name() {
        System.out.println("五菱宏光");
    }
}

tesla

public class tesla implements Car{
    @Override
    public void name() {
        System.out.println("Tesla");
    }
}

一般我们买车需要什么车就买什么车?
创建一个main方法

public class Customer {
    public static void main(String[] args) {
        Car car1 = new wuling();
        Car car2 = new tesla();
        car1.name();
        car2.name();
    }
}

但是呢,我们买车的话可以直接去从工厂去买。
造一个工厂

// 使用工厂创建,不需要关心细节
public class CarFactory {
    public static Car getCar(String car){
        if(car.equals("wuling")){
            return new wuling();
        } else if (car.equals("tesla")){
            return new tesla();
        }else {
            return null;
        }
    }
}

public static void main(String[] args) {
        Car car1 = CarFactory.getCar("wuling");
        Car car2 = CarFactory.getCar("tesla");
        car1.name();
        car2.name();
}

工厂不需要关注创建时大量的参数

但是如果新增一个品牌的车类,这时候会在原代码上去修改,这就不满足开闭原则了,这个时候需要去做一些优化。

// 增加一个新的产品,不修改原有的代码是做不到的

2、工厂方法模式

用来生产同一等级结构中的固定产品(支持增加任意产品)

创建一个车工厂

public interface CarFactory {
    Car getCar();
}

创建一个五菱工厂类

public class wulingFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new wuling();
    }
}

创建一个tesla工厂类

public class TeslaFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new tesla();
    }
}

如果我们新增一个产品的时候我们直接创建一个工厂就可,不需要去修改任何一段代码。更加复杂了?如果产品更多,更多的附加值。这时候需要用到抽象工厂模式。一般生产环境选择简单工厂模式就可以。

应用场景:Spring的IOC容器创建管理Bean对象

抽象工厂模式

围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

使用场景

  • 客户端不依赖于产品类实例如何被创建、实现等细节
  • 强调一系列相关产品的对象,一起使用创建对象需要大量的重复代码
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

建造者模式

原型模式

结构型模式

适配器模式

桥接模式

装饰模式

组合模式

外观模式

享元模式

Integer中的享元模式

代理模式

行为型模式

模版方法模式

命令模式

迭代器模式

观察者模式

中介者模式

备忘录模式

解释器模式

状态模式

策略模式

职责链模式

访问者模式

一些小技巧

1、反编译:javap -p class
2、jad -sjava class
3、枚举中有参构造 string, int
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值