23中模式分类:
创建型模式(关注对象的创建过程):
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
结构型模式(关注对象和类的组织):
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式(关注对象之间的交互,研究运行时对象之间的通信协作):
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、
策略模式、职责链模式、访问者模式。
1,单例模式(最常用):
核心作用:
保证一个类有且只有一个实例(对象),并且提供一个访问该实例的全局方法。
使用场景:
数据库连接池的设计,Spring中每个bean默认就是单例,Spring MVC框架中,控制器对象也是单例。
优点:
因为单例只生成一个实例(对象),减少系统性能开销,是一种共享的资源。
常见五种单例模式:
主要:
1,饿汉式(优点:线程安全,调用效率高,但是,不能延时加载。 缺点:如果不调用单例方法,则会一直浪 费该单例资源。)
/** * 饿汉式单例模式 */ public class SingletonDemo1 { //类初始化时,立即加载类对象(即:非延迟加载)。 private static /*final*/ SingletonDemo1 instance = new SingletonDemo1(); //私有构造器 private SingletonDemo1() {}; // 线程安全,虚拟机只会装在一次该类,肯定不会并发访问,可以忽略 synchronized // 由于不需要:synchronized,所以调用效率高 public static SingletonDemo1 getInstance() { return instance; } //测试 public static void main(String[] args){ SingletonDemo1 s1 = SingletonDemo1.getInstance(); SingletonDemo1 s2 = SingletonDemo1.getInstance(); System.out.println(s1); System.out.println(s2); System.out.println(s1 == s2); } }
,2,懒汉式(线程安全,调用效率不高,可以延时加载)
/** * 懒汉单例模式 */ public class SingletonDemo2 { private static /*final*/ SingletonDemo2 instance; //私有构造器 private SingletonDemo2() {}; //必须加上 synchronized 实现单线程访问,保证线程安全 // 加上 synchronized 避免了并发访问时实例化多个对象违反单例模式设计 //要点:实现了懒加载(延迟加载),资源利用率高,但是并发效率低了 public static synchronized SingletonDemo2 getInstance() { //如果不加 synchronized 很容易在这一步出现并发访问造成实例化多个对象 if (instance == null) { instance = new SingletonDemo2(); } return instance; } };
其他:
3,双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)
4,静态内部类式(线程安全,调用效率高,可以延时加载)
/** * 静态内部类模式 */ public class SingletonDemo3 { private SingletonDemo3(){}; //私有的静态内部类 private static class SingletonClassInstance{ private static final SingletonDemo3 instance = new SingletonDemo3(); } // 外部类没有static属性,不会像饿汉式那样立即加载对象。 //只有调用 getInstance() 方法才加载静态内部类,线程安全 // instance是static final类型,保证内存中只有一个实例存在,并且只能赋值一次 //兼备并发高效调用和延迟加载的优势 public static SingletonDemo3 getInstance(){ return SingletonClassInstance.instance; } }
5,枚举单例(线程安全,调用效率高,不能延时加载,天然防止反射反序列化漏洞)
/** * 枚举模式(没有懒加载效果) * 优势:避免反射反序列化漏洞 */ public enum SingletonDemo4 { //这个枚举,本身就是单例对象 INSTANE; public void singletonOperation(){ // TODO 可对枚举对象进行操作 } }
如何选用:
占用 资源少,不需要 延迟加载: 枚举式 好于 饿汉式
占用 资源少,需要 延迟加载: 静态内部类式 好于 懒汉式
(小记:懒加载(延迟加载):使用单例对象时才new出来对象。 非懒加载:类加载以后单例对象既已存在。)
扩展知识:通过反射和反序列化破解单例模式实现(不包括枚举式单例模型):
以饿汉式模型举例:
通过反射:
//测试 public static void main(String[] args) throws Exception { //正常情况下调用 SingletonDemo1 s1 = SingletonDemo1.getInstance(); SingletonDemo1 s2 = SingletonDemo1.getInstance(); System.out.println(s1); System.out.println(s2); System.out.println("-------------------------"); //通过反射破解单例模式 //通过反射加载类对象 Class<SingletonDemo1> aClass = (Class<SingletonDemo1>) Class.forName("com.panger.singleton.SingletonDemo1"); //获取无参构造器 Constructor<SingletonDemo1> c = aClass.getDeclaredConstructor(null); //跳过 定义了 private 的成员 权限检查设置 c.setAccessible(true); SingletonDemo1 s3 = c.newInstance(); SingletonDemo1 s4 = c.newInstance(); System.out.println(s3); System.out.println(s4); // TODO 运行结果可发现 s3 != s4 }
避免这种情况的发生,可在构造方法中进行校验:
通过反序列化:
//测试 public static void main(String[] args) throws Exception { //正常情况下调用 SingletonDemo1 s1 = SingletonDemo1.getInstance(); SingletonDemo1 s2 = SingletonDemo1.getInstance(); System.out.println(s1); System.out.println(s2); System.out.println("-------------------------"); //通过反序列化方式破解单例: //序列化对象 FileOutputStream fos = new FileOutputStream("d:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); //反序列化对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt")); SingletonDemo1 s3 = (SingletonDemo1) ois.readObject(); System.out.println(s3); // TODO 运行发现 被序列化的对象 s1 反序列化变为s3后 s1 != s3 }
避免这种情况的发生:
2,工厂模式:
核心作用:
帮助我们new对象出来,实现创建者和调用者分离。
常见的工厂模式:
1,简单工厂模式(常用,也叫静态工厂模式,传递参数不同通过静态类方法返回不同的对象实例 缺点:不好扩展)
/** * 简单工厂模式 * */ public class FactoryDemo1 { public static Fruits create(int type) { if (type == 1) { return new Apple(); } else { return new Banana(); } } //调用工厂模式 public static void main(String[] args) { FactoryDemo1.create(1); FactoryDemo1.create(2); } } class Fruits { } class Apple extends Fruits { public Apple() { System.out.println("苹果。。。"); } } class Banana extends Fruits { public Banana() { System.out.println("香蕉。。。"); } }
2,工厂方法模式 (相当于一个实例创建了对应的工厂 可扩展性好 缺点:工厂类过多)
1、 public interface Fruit { public void print(); } 2、 public class Apple implements Fruit { @Override public void print() { System.out.println("苹果。。。。"); } } 3、 public class Banana implements Fruit { @Override public void print() { System.out.println("香蕉。。。"); } } 4、工厂父类创建 public interface FruitFactory { Fruit getFruits(); } 5、apple工厂类创建 public class AppleFactory implements FruitFactory { public Fruit getFruits() { return new Apple(); } } 6、banana工厂类创建 public class BananaFactory implements FruitFactory { public Fruit getFruits() { return new Banana(); } } 7、 /** * 工厂方法模式 */ public class FactoryDemo2 { public static void main(String[] args){ Fruit apple = new AppleFactory().getFruits(); Fruit banana = new BananaFactory().getFruits(); apple.print(); banana.print(); } }
3,抽象工厂模式(生产不同产品族的全部产品 ,不可增加产品,但可以增加产品族)
应用场景:
- JDK中Calendar的getInstance方法。
- JDBC中的Connection对象的获取。
- Spring中的IOC容易创建管理bean对象。
- 反射中Class对象的newInstance()......
3,建造者模式:
应用场景:
当你需要创建一个复杂型的产品时(比如:一辆汽车需要按着步骤组装起来,比如SpringBuilder的append方法),我们所需要的对象构建也很复杂,有很多步骤并且还需要按一定顺序进行处理时。
举一个例子(假如要构建一个航天飞船):
1、 /** * 宇宙飞船类 */ public class AirShip { private OrbitalModule orbitalModule; //轨道舱 private Engine engine; //发动机 private EscapeTower escapeTower; //逃生舱 public OrbitalModule getOrbitalModule() { return orbitalModule; } public void setOrbitalModule(OrbitalModule orbitalModule) { this.orbitalModule = orbitalModule; } public Engine getEngine() { return engine; } public void setEngine(Engine engine) { this.engine = engine; } public EscapeTower getEscapeTower() { return escapeTower; } public void setEscapeTower(EscapeTower escapeTower) { this.escapeTower = escapeTower; } public void launch(){ System.out.println("飞船发射!"); } } //轨道类 class OrbitalModule{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } //发动机类 class Engine{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } //逃生舱类 class EscapeTower{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } 2、 /** * 飞船组件提供接口 */ public interface AirShipBuilder { Engine builderEngine(); OrbitalModule builderOrbitalModule(); EscapeTower builderEscapeTower(); } 3、 /** * 飞船组件实例化类 */ public class SxtAirShipBuilder implements AirShipBuilder { public Engine builderEngine() { System.out.println("构建发动机"); return new Engine(); } public OrbitalModule builderOrbitalModule() { System.out.println("构建轨道舱"); return new OrbitalModule(); } public EscapeTower builderEscapeTower() { System.out.println("构建逃生舱"); return new EscapeTower(); } } 4、 /** * 创建飞船接口 */ public interface AirShipDirector { AirShip directAirShip(); } 5、 /** * 装配者 */ public class SxtAirShipDirector implements AirShipDirector { private AirShipBuilder airShipBuilder; //当前类加载时 创建 AirShipBuilder 对象 public SxtAirShipDirector(AirShipBuilder airShipBuilder){ this.airShipBuilder = airShipBuilder; } // 将飞船组件进行装配并返回飞船 public AirShip directAirShip() { AirShip airShip = new AirShip(); airShip.setEngine(airShipBuilder.builderEngine()); airShip.setEscapeTower(airShipBuilder.builderEscapeTower()); airShip.setOrbitalModule(airShipBuilder.builderOrbitalModule()); System.out.println("飞船创建成功:"+airShip); return airShip; } public static void main(String[] args){ //进行组件装配 SxtAirShipDirector sxtAirShipDirector = new SxtAirShipDirector(new SxtAirShipBuilder()); //获取飞船 AirShip airShip = sxtAirShipDirector.directAirShip(); airShip.launch(); } }
4,原型模式(prototype):
应用场景:
简单来说就是克隆或者拷贝一个已经实例化并且属性赋值的 对象 给另一个新的对象使用。
原型模式实现:
1,创建原型类:
/** * 要想使用该类的原型模式,必须实现 Cloneable 接口 */ public class Sheep implements Cloneable { private String name; private Date birthday; public Sheep(String name, Date birthday) { this.name = name; this.birthday = birthday; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); return obj; } }
2,浅克隆:
/** * 浅克隆:当源对象中的属性 birthday 的时间被修改了,那么克隆对象的 birthday也会被修改 */ public static void main(String[] args) throws CloneNotSupportedException { Date date = new Date(23123124324L); Sheep s1 = new Sheep("原型羊", date); System.out.println("源对象实例化赋值:"+s1.birthday); //进行克隆 克隆后 name birthday 的值和s1一样 Sheep s2 = (Sheep) s1.clone(); System.out.println("克隆出 s2 之后:"+s2.birthday); //重新给 s1 的属性赋值以后 会发现即便 s2已经被克隆出来 但是其属性跟s1也一样会变化 date.setTime(2345678945645L); System.out.println("对源对象的属性修改值以后的s1:"+s1.birthday); System.out.println("对源对象的属性修改值以后的s2:"+s2.birthday); // TODO 可对s2的属性再次进行修改,不会影响s1的属性值 }
3,深度克隆:
1、 // 修改 Sheep 中的代码 @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); // 若想使用深度克隆:将源对象的属性也进行克隆以保证 源对象和克隆对象不会公用一个属性值 Sheep s = (Sheep) obj; // 对 birthday 进行克隆 s.birthday = (Date) this.birthday.clone(); return obj; } 2、 /** * 深度克隆:当源对象中的属性 birthday 的时间被修改了,克隆对象的属性值不变 */ public static void main(String[] args) throws CloneNotSupportedException { Date date = new Date(23123124324L); Sheep s1 = new Sheep("原型羊", date); System.out.println("源对象实例化赋值:"+s1.birthday); //进行克隆 克隆后 name birthday 的值和s1一样 Sheep s2 = (Sheep) s1.clone(); System.out.println("克隆出 s2 之后:"+s2.birthday); date.setTime(2345678945645L); System.out.println("对源对象的属性修改值以后的s1:"+s1.birthday); System.out.println("对源对象的属性修改值以后的s2:"+s2.birthday); // TODO 可对s2的属性再次进行修改 }
4,还可使用序列化和反序列化进行深度克隆,此处不再实现,有兴趣的朋友可百度。