设计模式面试题
1、请列举出在 JDK 中几个常用的设计模式?
单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.valueOf,观察者模式(Observer pattern)被用于 Swing 和很多的事件监听中。装饰器设计模式(Decorator design pattern)被用于多个 Java IO 类中。
2、Java 中什么叫单例设计模式
单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。Java.lang.Runtime 是单例模式的经典例子。从 Java 5 开始你可以使用枚举(enum)来实现线程安全的单例。
饿汉式
public class Singleton1 implements Serializable {
private Singleton1() {
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("private Singleton1()");
}
private static final Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
public Object readResolve() {
return INSTANCE;
}
}
- 构造方法抛出异常是防止反射破坏单例
readResolve()
是防止反序列化破坏单例
枚举饿汉式
public enum Singleton2 {
INSTANCE;
private Singleton2() {
System.out.println("private Singleton2()");
}
@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public static Singleton2 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
- 枚举饿汉式能天然防止反射、反序列化破坏单例
懒汉式
public class Singleton3 implements Serializable {
private Singleton3() {
System.out.println("private Singleton3()");
}
private static Singleton3 INSTANCE = null;
// Singleton3.class
public static synchronized Singleton3 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton3();
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
- 其实只有首次创建单例对象时才需要同步,但该代码实际上每次调用都会同步
- 因此有了下面的双检锁改进
双检锁懒汉式
public class Singleton4 implements Serializable {
private Singleton4() {
System.out.println("private Singleton4()");
}
private static volatile Singleton4 INSTANCE = null; // 可见性,有序性
public static Singleton4 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton4.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
为何必须加 volatile:
INSTANCE = new Singleton4()
不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造- 如果线程1 先执行了赋值,线程2 执行到第一个
INSTANCE == null
时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象
内部类懒汉式
public class Singleton5 implements Serializable {
private Singleton5() {
System.out.println("private Singleton5()");
}
private static class Holder {
static Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance() {
return Holder.INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
- 避免了双检锁的缺点
JDK 中单例的体现
- Runtime 体现了饿汉式单例
- Console 体现了双检锁懒汉式单例
- Collections 中的 EmptyNavigableSet 内部类懒汉式单例
- ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
- Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例
3、什么叫观察者设计模式(observer design pattern)?
观察者模式是基于对象的状态变化和观察者的通讯,以便他们作出相应的操作。简单的例子就是一个天气系统,当天气变化时必须在展示给公众的视图中进行反映。这个视图对象是一个主体,而不同的视图是观察者。
4、什么是工厂模式
工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建过程封装在一个工厂类中,通过调用工厂类的方法来获取对象,从而实现了对象的解耦和复用。
工厂模式包括三种常见的形式:简单工厂模式、工厂方法模式和抽象工厂模式。
- 简单工厂模式:简单工厂模式又称为静态工厂模式,它通过一个工厂类来创建不同类型的对象,根据不同的参数类型返回不同的实例对象。简单工厂模式适用于对象类型比较少,且对象的创建过程比较简单的场景。
- 工厂方法模式:工厂方法模式将对象的创建过程抽象成一个工厂接口和多个具体工厂实现类,每个具体工厂实现类负责创建一类对象,通过工厂接口来获取对象。工厂方法模式适用于需要创建多种类型的对象,且对象的创建过程比较复杂的场景。
- 抽象工厂模式:抽象工厂模式将对象的创建过程抽象成一个抽象工厂和多个具体工厂实现类,抽象工厂负责创建一组相关的对象,每个具体工厂实现类负责创建一类对象。抽象工厂模式适用于需要创建多组相关的对象,且对象的创建过程比较复杂的场景。
工厂模式的优点是可以将对象的创建和使用分离,降低系统的耦合度和复杂度,同时提高了系统的可维护性和可扩展性。工厂模式可以使得系统更加灵活,可以根据具体的业务需求来选择合适的对象类型,从而提高了系统的性能和效率。
5、什么是模板模式
算法的复用和扩展。
模板模式包含两类角色:抽象类和具体实现类。其中,抽象类定义了一组算法步骤和公共的方法,具体实现类继承抽象类并实现其中的抽象方法,从而完成对算法步骤的具体实现。
模板模式的核心思想是通过抽象类封装算法的公共步骤,将具体的实现细节留给具体实现类来完成,从而实现了算法的复用和扩展。模板模式常用于框架和类库的设计中,也可以在复杂的业务逻辑中使用,从而提高代码的复用性和可维护性。
模板模式的优点包括:
- 提高了代码的复用性和可维护性,避免了重复的代码实现;
- 提高了代码的可扩展性,允许子类对算法的某些步骤进行重写,从而实现了算法的定制化;
- 通过抽象类封装算法的公共步骤,提高了代码的可读性和可理解性。
模板模式的缺点是:
- 可能会造成类的数量增加,对于简单的算法,使用模板模式可能会增加代码的复杂度;
- 模板模式的扩展性有限,如果需要对算法的整个流程进行修改,可能需要重构整个模板模式的代码。