1、设计模式
1.1 设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式不是一种方法和技术,而是一种思想。
设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用。
学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成
简单一句话:设计模式就是经验的总结
设计模式的几个要素
- 名字:必须有一个简单,有意义的名字
- 问题描述:在何时使用模式
- 解决方案:描述设计的组成部分以及如何解决问题
- 效果:描述模式的效果以及优缺点设计模式的分类
设计模式的分类
- 创建型模式:对象的创建
- 结构型模式:对象的组成(结构)
- 行为型模式:对象的行为
创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)
结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)
行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备忘录模式、迭代器模式、解释器模式。(10个)
1.2 单例模式详解
1.2.1 什么是单例设计模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1.2.2 单例模式特点
1、单例类只能有一个实例。也就是只有一个对象
2、单例类必须自己创建自己的唯一实例。 写单例构造方法是要私有的
3、单例类必须给所有其他对象提供这一实例。 在该方法中,提供一个方法,用于获取该对象
1.2.3 单例设计模式实现前提条件
私有构造方法
在本类的成员位置,创建出自己类对象
提供公共方法,返回创建的对象 ,该方法必须是静态的
1.2.4 单例模式饿汉式
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:46 * @Description: 饿汉式单例模式 */public class A {// 2.定义变量接收类的对象private static A a = new A();// 1.私有化构造器private A(){}// 3.定义静态方法返回类的对象public static A getInstance(){return a;}}
测试
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:49 * @Description: 饿汉式单例模式测试 */public class ADemo01 {public static void main(String[] args) {A a1 = A.getInstance();A a2 = A.getInstance();System.out.println(a1); // com.suyv.singleton.A@677327b6System.out.println(a2); // com.suyv.singleton.A@677327b6System.out.println(a1 == a2); // true}}
1.2.5 单例模式懒汉式
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:52 * @Description: 懒汉式单例模式 */public class B {// 2.定义静态变量接收创建的对象private static B b;// 1.私有化构造器private B(){}// 3.定义一个静态方法,第一次创建时才创建对象,后面调用都会返回这一个对象public static B getInstance(){if (b == null){b = new B();}return b;}}
测试
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:56 * @Description: 懒汉式单例模式测试 */public class BDemo01 {public static void main(String[] args) {B b1 = B.getInstance();B b2 = B.getInstance();System.out.println(b1); // com.suyv.singleton.B@677327b6System.out.println(b2); // com.suyv.singleton.B@677327b6System.out.println(b1 == b2); // true}}
1.2.6 单例设计模式的线程安全问题
1.2.6.1 饿汉式没有线程安全问题
饿汉式:在类初始化时就直接创建单例对象,而类初始化过程是没有线程安全问题的
1.2.6.2 懒汉式线程安全问题
问题出现:
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:52 * @Description: 懒汉式单例模式 */public class B{// 2.定义静态变量接收创建的对象private static volatile B b;// 1.私有化构造器private B(){}// 3.定义一个静态方法,第一次创建时才创建对象,后面调用都会返回这一个对象public static B getInstance(){if (b == null){try {// 让问题暴露更加明显Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}b = new B();}return b;}}
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:56 * @Description: 懒汉式单例模式线程安全测试 */public class BDemo02 {static B b1 = null;static B b2 = null;public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {b1 = B.getInstance();}};Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {b2 = B.getInstance();}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(b1); // com.suyv.singleton.B@677327b6System.out.println(b2); // com.suyv.singleton.B@14ae5a5System.out.println(b1 == b2); // false}}
解决方案:
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:52 * @Description: 懒汉式单例模式 */public class B{// 2.定义静态变量接收创建的对象private static volatile B b;// 1.私有化构造器private B(){}// 3.定义一个静态方法,第一次创建时才创建对象,后面调用都会返回这一个对象/*public static B getInstance(){if (b == null){try {// 让问题暴露更加明显Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}b = new B();}return b;}*/// 使用双重判断,效率更高public static B getInstance(){if (b == null){synchronized (B.class){if (b == null){b = new B();}}}return b;}}
package com.suyv.singleton;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 10:56 * @Description: 懒汉式单例模式线程安全测试 */public class BDemo03 {static B b1 = null;static B b2 = null;public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {b1 = B.getInstance();}};Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {b2 = B.getInstance();}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(b1); // com.suyv.singleton.B@677327b6System.out.println(b2); // com.suyv.singleton.B@677327b6System.out.println(b1 == b2); // true}}
解决程线安全问题
双层if判断的原因
- 方法中加入同步锁,保证线程安全
- 第二个线程调用方法getInstance()的时候,变量s,已经不是null,被前面的线程new过
- 当已经有对象了,第二个线程没有必要再进入同步了,直接return返回对象即可
单例模式的应用场景和好处
Runtime
任务管理器对象、获取运行时对象
使用单例模式,可以避免资源浪费。
1.3 工厂模式
工厂设计模式,属于创建型,用于对象的创建; 简单来说,就是专门生产对象的
1.3.1 简单工厂模式
package com.suyv.factory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 11:26 * @Description: 动物抽象类 */public abstract class Animal {public abstract void eat();}
package com.suyv.factory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 11:27 * @Description: 猫类 */public class Cat extends Animal{@Overridepublic void eat() {System.out.println("猫吃鱼");}}
package com.suyv.factory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 11:28 * @Description: 狗类 */public class Dog extends Animal{@Overridepublic void eat() {System.out.println("狗吃骨头");}}
package com.suyv.factory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 11:29 * @Description: 工厂设计类,这个类就是专门来生产对象,今后从这个工厂类中可以直接获取类对象 */public class AnimalFactory {private AnimalFactory(){}public static Dog createDog(){return new Dog();}public static Cat creatCat(){return new Cat();}}
package com.suyv.factory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 11:32 * @Description: 工厂模式测试 */public class AnimalTest {public static void main(String[] args) {// 工厂设计模式:// 优点:我可以直接通过工厂来获取对象// 缺点:如果要加对象,需要修改工厂类,不便于后期的维护// 使用工厂模式创建对象Cat cat = AnimalFactory.creatCat();Dog dog = AnimalFactory.createDog();// 调用方法cat.eat();dog.eat();}}
1.3.2 实例工厂模式
package com.suyv.factory1;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:11 * @Description: 创建一个接口 */public interface Product {public void doSomething();}
package com.suyv.factory1;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:12 * @Description: 实现接口的实现类 */public class ConcreteProduct implements Product{@Overridepublic void doSomething() {System.out.println("Doing something in ConcreteProduct");}}
package com.suyv.factory1;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:14 * @Description: 实例工厂类 */public class ProductFactory {public Product createProduct(){return new ConcreteProduct();}}
package com.suyv.factory1;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:15 * @Description: 实例工厂测试类 */public class ProductTest {public static void main(String[] args) {ProductFactory factory = new ProductFactory();Product product = factory.createProduct();product.doSomething();}}
1.3.3 静态工厂模式
package com.suyv.staticfactory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:21 * @Description: 定义一个咖啡类 */public abstract class Coffee {public abstract String getName();//加糖public void addsugar() {System.out.println("加糖");}//加奶public void addMilk() {System.out.println("加奶");}}
package com.suyv.staticfactory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:22 * @Description: 美式咖啡 */public class AmericanCoffee extends Coffee{@Overridepublic String getName() {return "美式咖啡";}}
package com.suyv.staticfactory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:23 * @Description: 拿铁咖啡 */public class LatteCoffee extends Coffee{@Overridepublic String getName() {return "拿铁咖啡";}}
package com.suyv.staticfactory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:29 * @Description: 咖啡工厂类--静态工厂 */public class SimpleCoffeeFactory {public static Coffee createCoffee(String type) {//声明Coffee类型的变量,根据不同类型创建不同的coffee子类对象Coffee coffee = null;if("american".equals(type)) {coffee = new AmericanCoffee();} else if("latte".equals(type)) {coffee = new LatteCoffee();} else {throw new RuntimeException("对不起,您所点的咖啡没有");}return coffee;}}
package com.suyv.staticfactory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:24 * @Description: 咖啡店类 */public class CoffeeStore {public Coffee orderCoffee(String type) {Coffee coffee = SimpleCoffeeFactory.createCoffee(type);//加配料coffee.addMilk();coffee.addsugar();return coffee;}}
package com.suyv.staticfactory;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 12:26 * @Description: 静态工厂类测试 */public class CofferTest {public static void main(String[] args) {//创建咖啡店类对象CoffeeStore store = new CoffeeStore();Coffee coffee = store.orderCoffee("latte");System.out.println(coffee.getName());}}
1.4 模板设计模式
模版方法模式,简称为模板设计模式,它主要思想是:把通用的代码生成一个模板,可以反复的使用
package com.suyv.template;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 14:00 * @Description: 创建一个模板类 */public abstract class GetTime {/** * @description: 获取一段程序的运行时间 * @author: 憨憨浩浩 * @date: 2023/12/27 14:02 * @param: [] * @return: long **/public long getTime(){long start = System.currentTimeMillis();// 程序code();long end = System.currentTimeMillis();return end - start;}public abstract void code();}
package com.suyv.template;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 14:03 * @Description: 用户使用模板类 */public class ForDemo extends GetTime{@Overridepublic void code() {for (int i = 0; i < 100; i++) {System.out.println(i);}}}
package com.suyv.template;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 14:04 * @Description: 模板设计模式测试类 */public class TemplateTest {public static void main(String[] args) {ForDemo forDemo = new ForDemo();System.out.println(forDemo.getTime() + "毫秒");}}
1.5 装饰设计模式
装饰设计模式: 增强原有对象的功能
原本有一个对象,但是这个对象的功能不够强,采用装饰设计模式,对原对象中功能进行增强
回想一下我们当时讲的缓冲流,其实就是装饰设计模式
package com.suyv.decorate;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 14:09 * @Description: TODO */public class Phone {public void call(){System.out.println("手机打电话功能!");}}
package com.suyv.decorate;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 14:10 * @Description: TODO */public class SendMsg {private Phone phone;public SendMsg(Phone phone) {this.phone = phone;}//增强原有手机的功能public void msg(){System.out.println("发彩信");System.out.println("发短信");phone.call();}}
package com.suyv.decorate;/** * @Author: 憨憨浩浩 * @CreateTime: 2023-12-27 14:11 * @Description: TODO */public class PhoneTest {public static void main(String[] args) {SendMsg sendMsg = new SendMsg(new Phone());sendMsg.msg();}}
2、面向对象思想设计原则
面向对象思想设计原则
在实际的开发中,我们要想更深入的了解面向对象思想,就必须熟悉前人总结过的面向对象的思想的设计原则
- 单一职责原则
- 开闭原则
- 里氏替换原则
- 依赖注入原则
- 接口分离原则
- 迪米特原则
2.1 单一职责原则
其实就是开发人员经常说的”高内聚,低耦合”
也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则。能自己完成的事就不要麻烦别人,把一件事细化细化,只做一件事情
能自己完成的事就不要麻烦别人,把一件事细化细化,只做一件事情
2.2 开闭原则 ocp
核心思想是:一个对象对扩展开放,对修改关闭。
其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。
也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的
2.3 里氏替换原则
核心思想:在任何父类出现的地方都可以用它的子类来替代。
其实就是说:同一个继承体系中的对象应该有共同的行为特征。
2.4 依赖注入原则
核心思想:要依赖于抽象,不要依赖于具体实现。
其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
2.5 接口分离原则
核心思想:不应该强迫程序依赖它们不需要使用的方法。
其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中。
2.6 迪米特原则
核心思想:一个对象应当对其他对象尽可能少的了解
其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用