单例模式
特点:
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
- 单例模式的构造器,实例都是私有的。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。总之单例模式就是为了避免不一致状态。
一,懒加载模式
//懒汉式单例类,在第一次调用的时候实例化自己
public class Singleton{
private Singleton(){}
//先创将,但不会实例化,而是等到调用的时候实例化。
private static Singleton single = null;
//静态工厂方法
public static Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过(对外的一个接口)getInstance()方法访问。
(事实上,通过java反射机制是能够实例化构造方法为private的类的,就是可以获得单例模式私有的构造方法和属性,那基本上会使所有的java单例实现失效。)
懒汉式单例的实现没有考虑线程安全问题的,它在多线程的情况下是线程不安全的,并发环境下很可能出现多个Singleton实例,需要实现线程安全,有以下几种方法,保证了懒汉式单例的线程安全。
在getInstance方法上加同步
public class Singleton{
private Singleton(){}
private static Singleton single = null;
public static synchronized Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
}
加锁双重判断,简称DCL懒汉式(在反射机制下还是有问题)
public class Singleton{
private Singleton(){}
//必须加上volatile关键字避免指令重排
private volatile static Singleton single = null;
if(single == null){
synchronized(Singleton.class){
if(single == null){
single = new Singleton();
//分配内存空间
//执行构造方法,初始化对象
//把这个对象指向这个空间
}
}
}
return single;
}
静态内部类实现
public class Holder{
private Holder(){}
public static Holder getInstace(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final HOLDER = new HOLDER();
}
}
二,饿汉式单例模式
//饿汉式单例模式,在第一次加载的时候就实例化对象。
//饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
public class Hungry{
private Hungrt(){}
private static final Hungry hungry = new Hunger();
//静态工厂方法
public static Hungry getInstance(){
return hungry;
}
}
三,枚举本身就是一个单例模式,它是反射机制破坏不了的,枚举本身是一个类其中有一个构造方法,两个参数String和int
工厂模式
作用:
实现了创建者和调用者的分离
详细分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
OOP七大原则
- 开闭原则:一个软件的实体应当对扩展开放,对修改关闭。
- 依赖倒置原则:要针对接口编程,不要针对实现编程。
- 迪米特法则:只与你的朋友交流,而避免和陌生人通信。
核心本质
实例化对象不使用new,用工厂方法代替
将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
三种模式
- 简单工厂模式:用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
- 工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式:围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
简单工厂模式
就是把实例化对象的事情统一交给一个工厂类来做,使用者不需要在去实例化对象了,直接调用这个工厂类就可以获得实例对象。
比如说现在要求买车,而车有很多种。
//先创建一个车的接口
public Interface Car{
void name();
}
//创建一个宝马车继承车的接口
public class Bwm implements Car{
public void name(){
System.out.println("这是一个宝马车");
}
}
//创建一个五菱宏光车继承公共接口
public class Wuling implements Car{
public void name(){
System.out.pringtln("这是一个五菱宏光");
}
}
//创建一个工厂类帮我们实例化这两个对象,这样我们需要用这两个类中的其中一个或者两个时,我们只需要调用工厂类就可以了。
public class CarFactory{
public static Car getCar(String name){
if(name.equals("宝马")){
return new Bwm();
}else if(name.equals("五菱宏光")){
return new Wuling();
}else{
return null;
}
}
}
简单工厂模式有问题,没有满足OOP里面的开闭原则,就是不易于扩展,修改的话需要改动源码,这时可以使用工厂方法模式。
工厂方法模式
每一个人都有自己的工厂,然后去实现总工厂的接口即可,这样我们在扩展的时候添加车类和它单独的工厂类就不需要在其总工厂上进行修改代码了,这样满足了开闭原则。
//先创建一个车的接口
public Interface Car{
void name();
}
//创建一个宝马车继承车的接口
public class Bwm implements Car{
public void name(){
System.out.println("这是一个宝马车");
}
}
//创建一个五菱宏光车继承共同接口
public class Wuling implements Car{
public void name(){
System.out.println("这是一个五菱宏光");
}
}
//创建一个工厂总接口,定义车的方法
public Interface CarFactory{
Car getCar();
}
//定义Bwm的工厂类
public class BwmFactory implements CarFactory{
public Car getCar(){
return new Bwm();
}
}
//定义Wuling的工厂类
public class WulingFactory implements CarFactory{
public Car getCar(){
return new Wuling();
}
}
这样我们想要扩展其他车类型的时候,只需要添加它的类跟工厂就可以了,直接实现接口就不要修改原来的代码,比如说我们要添加一个Benchi车。
//创建一个奔驰车继承车的接口
public class Benchi implements Car{
public void name(){
System.out.println("这是一个奔驰车");
}
}
//定义Benchi的工厂类
public class BenchiFacatory implements CarFactory{
public Car getCar(){
return new Benchi();
}
}
抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者互相依赖对象的接口,无需指定它们具体的类。
适用场景:
- 客户端(应用层)不依赖于产品类,实例如何被创建,实现等细节。
- 强调一系列相关产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现。
优点:
具体产品在应用层隔离的代码隔离,无需关心创建的细节。
将一个系列的产品统一到一起创建。
缺点:
规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难
增加了系统的抽象性和理解难度
- 简单工厂模式(静态工厂模式):虽然某种程度上不符合设计原则,但实际使用最多
- 工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展
- 抽象工厂模式:不可以增加产品,可以增加族
应用场景:
- JDK中的Calendar的getInstance方法
- JDBK中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newInstance方法