java设计模式
前言
最近在了解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