设计模式
单例设计模式
概述:一个类只有一个实例,无法 new 出一个新实例(构造器被 private 修饰),一般通过 getInstance 方法获取这个实例。
实现方式有以下五种:
- 饿汉式
- 懒汉式
- 静态内部类
- 枚举
- 双重校验(也属于懒汉式的一种)
饿汉式
/**
* 二话不说,直接 new 一个实例;
* 私有化构造函数;
* 实现一个 getInstance 方法返回实例;
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
懒汉式
/**
* 私有化构造函数;
* 实现 getInstance 方法,该方法被 synchronized 修饰(保证线程安全)。
*/
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
静态内部类
/**
* 私有化构造函数;
* 创建一个静态内部类,内部类里定义一个被 static 和 final 修饰的该类的实例对象;
* 实现 getInstance 方法返回静态内部类中的实例对象。
*/
public class singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
双重校验
/**
* 私有化构造函数;
* 定义一个被 static 和 volatile 修饰的对象;
* 实现 getInstance 方法,在调用该方法时,先判断对象为 null,
* 不为 null 则锁住当前类对象,再次判断对象是否为 null(避免在获取锁之前已被其他线程创建而重复创建),
* 为 null 则创建对象并返回。
* 使用 volatile 修饰对象是为了使所有线程对该对象有可见性,并且使指令不可重排,
* 不加 volatile 可能会造成对象还没完成初始化时被其他线程调用,获取到未完全初始化的数据。
* new 对象分三步:
* 1. jvm 分配一个内存空间
* 2. 给对象赋默认值,对象类型为null
* 3. 再将对象指向内存,初始化完成。
*/
public class Singleton {
// volatile 修饰是为了避免指令重排
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
总结:一般情况下饿汉式或静态内部类的实现方式比较常见,也足以应付大多数情况;
双重校验的方法面试时问得会比较多,需要深入理解。
观察者模式
概述:观察者模式一般由三个元素构成:事件源(一),事件,观察者(多),对象间有一对多的依赖关系下,当一个事件源发生事件,观察者会随之发生变化,这种模式称之为观察者模式!
举例:
事件源:孩子
事件:哭
观察者:父母
当孩子哭的时候,会通知父母来安抚,喂奶等一系列操作!孩子维护着一个观察者集合(父母),当发生哭这个方法的时候遍历观察者集合执行喂奶方法!
下面是代码示例:
/**
* 事件源:孩子
* 事件:哭
* 观察者集合:父母类 list
*
* 当孩子哭时,调用父母的喂奶方法!
*
*/
public class Children {
List<Parent> parents = new ArrayList<>();
void addParent(Parent parent) {
this.parents.add(parent);
}
/**
* 孩子哭:当孩子哭时调用监护他的父母的喂奶方法!
*/
public void cry() {
parents.forEach(Parent::feedMilk);
}
}
/**
* 父母接口
*/
public interface Parent {
/**
* 喂奶方法
*/
void feedMilk();
}
/**
* 观察者:妈妈类
*/
public class Mother implements Parent {
@Override
public void feedMilk() {
System.out.println("妈妈来喂奶啦!");
}
}
/**
* 观察者:爸爸类
*/
public class Father implements Parent {
@Override
public void feedMilk() {
System.out.println("爸爸来喂奶啦!");
}
}
装饰器模式
概述:向一个现有的类添加新功能,同时不改变其结构。
角色:
被装饰者
装饰者
举例:奶茶加料
被装饰者:奶茶
装饰者:珍珠、奶盖、椰果、红豆等
被装饰者和装饰者一同实现一个饮料接口。
接口有一个 花费方法(cost),有一个price,自身价格
装饰者持有一个被装饰者对象,在调用cost方法时返回被装饰者的cost+装饰者的price。
java 中实例: FilterInputStream,FilterOutputStream,BufferedInputStream,BufferedOutputStream
// 饮料接口,有一个cost方法。(如果有比较多相同元素,可以使用抽象类,减少代码量)
public interface Beverage {
// 计算价格的抽象方法!
Double cost();
}
// 奶茶:被修饰类,实现饮料接口
public class MilkTea implements Beverage{
private String name = "奶茶";
private Double price = 10.0;
public MilkTea() {
}
@Override
public Double cost() {
return price;
}
}
// 珍珠类:修饰类,持有被修饰的饮料引用。
public class Pearl implements Beverage {
private String name = "珍珠";
private Double price = 3.0;
private Beverage beverage;
public Pearl(Beverage beverage) {
this.beverage = beverage;
}
public Pearl() {
}
@Override
public Double cost() {
return beverage.cost() + price;
}
}
// 双份珍珠的奶茶价格!3+3+10=16
public static void main(String[] args) {
Beverage beverage = new Pearl(new Pearl(new MilkTea()));
System.out.println(beverage.cost());
}
// 打印结果
16.0
Process finished with exit code 0
适配器模式
概述:作为两个不兼容的接口之间的桥梁。将自己无法完成的事通过接口调用能解决该事的另一个对象来处理。
举例:手机充电需要20V的电压,但中国家用电压为220V,这时需要一个变压器,将220V的电压转化为20V供手机充电。
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
VoltageAdapter adapter = new VoltageAdapter();
phone.setAdapter(adapter);
phone.charge();
}
}
// 手机类
class Phone {
public static final int V = 220;// 正常电压220v,是一个常量
private VoltageAdapter adapter;
// 充电
public void charge() {
adapter.changeVoltage();
}
public void setAdapter(VoltageAdapter adapter) {
this.adapter = adapter;
}
}
// 变压器
class VoltageAdapter {
// 改变电压的功能
public void changeVoltage() {
System.out.println("正在充电...");
System.out.println("原始电压:" + Phone.V + "V");
System.out.println("经过变压器转换之后的电压:" + (Phone.V - 200) + "V");
}
}
运行结果: