一、 Java设计模式分为哪几类?
- 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
- 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
- 行为型模式(11种):策略模式,模板方法模式,观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
二、设计模式遵循的原则是哪几个?
有6个,分别是:
-
开闭原则(Open Close Principle)
对扩展开放,对修改关闭。 -
里氏代换原则(Liskov Substitution Principle)
只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。 -
依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。 -
接口隔离原则(Interface Segregation Principle)
使用多个隔离的借口来降低耦合度。 -
迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 -
合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。
三、说一下单例模式?
单例模式分为两种:懒汉模式和饿汉模式,
1. 懒汉模式:就是等到有线程调用getInstance这个方法时,才来创建对象实例。与懒汉模式相反的是饿汉模式。
双重锁检测机制确保线程安全的写法:
public class Singleton{
private static Singleton instance=null;
//私有构造函数
private Singleton(){};
//双重锁检测机制
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
进阶版:通过vlatile关键字来保证指令重排的问题
public class Singleton {
private static volatile Singleton instance = null;
//私有构造函数
private Singleton(){};
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2. 饿汉模式:所谓饿汉式,就是一开始把对象实例创建出来,而不是等getInstance这个方法被调用才来创建对象。
-
代码如下:
public class Singleton2 { private static Singleton2 instance = new Singleton2(); //私有构造器 private Singleton2(){}; public static Singleton2 getInstance() { return instance; } }
采用静态内部类的写法:
public class Singleton3 {
//静态内部类
private static class LazyHolder{
private static Singleton3 instance = new Singleton3();
}
//私有构造器
private Singleton3(){};
public static Singleton3 getInstance() {
return LazyHolder.instance;
}
}
由于外部类无法访问静态内部类,因此只有当外部类调用Singleton.getInstance()方法的时候,才能得到instance实例。
并且,instance实例对象初始化的时机并不是在Singleton被加载的时候,而是当getInstance()方法被调用的时候,静态内部类才会被加载,这时instance对象才会被初始化。并且也是线程安全的。
所以,与饿汉式相比,通过静态内部类的方式,可以保证instance实例对象不会被白白浪费。但是,它仍然存在反射问题。
采用枚举的方法:
public enum Singleton4 {
//一般用大写的了,不过为了和前面的统一
//我就用小写的了
instance;
}
不过和饿汉式一样,由于一开始instance实例就被创建了,所以有可能出现白白浪费的情况。
但是,通过枚举的方式,不仅代码简单,线程安全,而且JVM还能阻止反射获取枚举类的私有构造器。
这种枚举的方式可以说的用的最多的一种方式了,唯一的缺点就是对象一开始就被创建,可能出现白白浪费没有用到对象的情况。不过,总体上,还是推荐采用枚举的方式来写.