单例模式(Singleton Pattern)
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式有以下三点注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
单例的好处:
- 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
- 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
- 有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
注意:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
单例模式的实现方式一共有6种,一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式(建议使用这个方法)。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
1、懒汉式,线程不安全
是否 Lazy 初始化:是
是否多线程安全:否
总结:最大的问题就是不支持多线程,
/**
* @ClassName: Singleton1
* @Description: 懒汉式,线程不安全
* @Author: xinyuan
* @CreateDate: 2018/9/24 11:18
*/
public class Singleton1 {
private static Singleton1 instance;
private Singleton1(){}
public static Singleton1 getInstance(){
if(instance == null){
instance = new Singleton1();
}
return instance;
}
public void showMessage(){
System.out.println("hello singleton1");
}
}
/**
* @ClassName: SingletonPatternDemo
* @Description: 测试demo
* @Author: xinyuan
* @CreateDate: 2018/9/24 11:19
*/
public class SingletonPatternDemo {
public static void main(String[] args){
Singleton1 singleton1 = Singleton1.getInstance();
singleton1.showMessage();
}
}
2、懒汉式,线程安全
是否 Lazy 初始化:是
是否多线程安全:是
总结:能进行多线程,但是加锁会导致效率很低,优点就是避免了内存的浪费。这个还是有一个问题的,就是看到别的大神指出,如果A线程进入getInstance函数创建instance实例,不过并没有进行赋值操作,但是B线程这时也要使用这个类创建的唯一实例,但是他发现instance已经建立,但是访问却什么也没有,因而出现问题。
/**
* @ClassName: Singleton2
* @Description: 懒汉式,线程安全
* @Author: xinyuan
* @CreateDate: 2018/9/24 11:28
*/
public class Singleton2 {
private static Singleton2 instance;
private Singleton2(){}
public static synchronized Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
public void showMessage(){
System.out.println("hello singleton2");
}
}
3、饿汉式
是否 Lazy 初始化:否
是否多线程安全:是
总结:它基于 classloader 机制避免了多线程的同步问题,但是缺点就是造成了内存的浪费,没有达到lazy loading的效果。我认为相当于把instance的声明和获取分开了。
/**
* @ClassName: Singleton3
* @Description: 饿汉式
* @Author: xinyuan
* @CreateDate: 2018/9/24 11:37
*/
public class Singleton3 {
private static Singleton3 instance = new Singleton3();
private Singleton3(){}
public static Singleton3 getInstance(){
return instance;
}
public void showMessage(){
System.out.println("hello singleton3");
}
}
4、双检锁/双重校验锁(DCL,即 double-checked locking)
是否 Lazy 初始化:是
是否多线程安全:是
总结:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。(虽然代码也模仿的写出来了,但是感觉还是有点不明白啊)
/**
* @ClassName: Singleton4
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 11:47
*/
public class Singleton4 {
private volatile static Singleton4 instance;
private Singleton4(){}
public static Singleton4 getInstance(){
if(instance == null){
synchronized (Singleton4.class){
if(instance == null){
instance = new Singleton4();
}
}
}
return instance;
}
public void showMessage(){
System.out.println("hello singleton4");
}
}
5、登记式/静态内部类
是否 Lazy 初始化:是
是否多线程安全:是
总结:也是利用了classloader机制保证初始化instance只有一个线程,采用了类里面再单独使用一个类去初始化的方法,可以有效地去避免内存的浪费,还可以保证效率,是一个常用的办法
/**
* @ClassName: Singleton5
* @Description: 登记式/静态内部类
* @Author: xinyuan
* @CreateDate: 2018/9/24 11:54
*/
public class Singleton5 {
private static class SingletonHolder{
private static final Singleton5 instance = new Singleton5();
}
private Singleton5(){}
public static Singleton5 getInstance(){
return SingletonHolder.instance;
}
public void showMessage(){
System.out.println("hello singleton5");
}
}
6、枚举
是否 Lazy 初始化:否
是否多线程安全:是
总结:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。(这个方法还需要再研究啊。。。)
/**
* @ClassName: Singleton6
* @Description: 枚举
* @Author: xinyuan
* @CreateDate: 2018/9/24 12:06
*/
public enum Singleton6 {
INSTANCE;
public void showMessage(){
System.out.println("hello singleton6");
}
}
//方法6:枚举
Singleton6 singleton6 = Singleton6.INSTANCE;
singleton6.showMessage();
建造者模式(Builder Pattern)
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
/**
* @ClassName: Packing
* @Description: 包装类接口
* @Author: xinyuan
* @CreateDate: 2018/9/24 12:30
*/
public interface Packing {
public String pack();
}
/**
* @ClassName: Bottle
* @Description: 包装类接口实体类
* @Author: xinyuan
* @CreateDate: 2018/9/24 12:32
*/
public class Bottle implements Packing{
@Override
public String pack() {
return "bottle";
}
}
/**
* @ClassName: Wrapper
* @Description: 纸质包装实体类
* @Author: xinyuan
* @CreateDate: 2018/9/24 12:31
*/
public class Wrapper implements Packing{
@Override
public String pack(){
return "wrapper";
}
}
/**
* @ClassName: Item
* @Description: 食物条目接口
* @Author: xinyuan
* @CreateDate: 2018/9/24 12:29
*/
public interface Item {
public String name();
public Packing packing();
public float price();
}
/**
* @ClassName: Burger
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 12:33
*/
public abstract class Burger implements Item{
@Override
public Packing packing() {
return new Wrapper();
}
// @Override
// public abstract float price();
}
/**
* @ClassName: ColdDrink
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:11
*/
public abstract class ColdDrink implements Item{
@Override
public Packing packing() {
return new Bottle();
}
}
/**
* @ClassName: VerBurger
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:12
*/
public class VerBurger extends Burger{
@Override
public float price() {
return 25.0f;
}
@Override
public String name() {
return "VerBurger";
}
}
/**
* @ClassName: ChickenBurger
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:14
*/
public class ChickenBurger extends Burger{
@Override
public float price() {
return 50.5f;
}
@Override
public String name() {
return "chickenbuger";
}
}
/**
* @ClassName: Coke
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:15
*/
public class Coke extends ColdDrink{
@Override
public float price() {
return 10.0f;
}
@Override
public String name() {
return "coke";
}
}
/**
* @ClassName: Pepsi
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:16
*/
public class Pepsi extends ColdDrink{
@Override
public float price() {
return 9.0f;
}
@Override
public String name() {
return "pepsi";
}
}
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: Meal
* @Description: 创建一个 Meal 类,带有上面定义的 Item 对象
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:21
*/
public class Meal {
public List<Item> items = new ArrayList<Item>();
public void addItem(Item item){
items.add(item);
}
public float getCost(){
float cost = 0.0f;
for(Item item : items){
cost += item.price();
}
return cost;
}
public void showItems(){
for(Item item : items){
System.out.println("Item:"+item.name());
System.out.println("Packing:"+item.packing().pack());
System.out.println("price:"+item.price());
}
}
}
/**
* @ClassName: MealBuilder
* @Description: 创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:27
*/
public class MealBuilder {
public Meal prepareVegMeal(){
Meal meal = new Meal();
meal.addItem(new VerBurger());
meal.addItem(new Coke());
return meal;
}
public Meal perpareChickenMeal(){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
/**
* @ClassName: BuilderPatternDemo
* @Description: BuiderPatternDemo 使用 MealBuider 来演示建造者模式(Builder Pattern)。
* @Author: xinyuan
* @CreateDate: 2018/9/24 13:31
*/
public class BuilderPatternDemo {
public static void main(String[] args){
MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
System.out.println("veg meal");
vegMeal.showItems();
System.out.println("total cost:" + vegMeal.getCost());
Meal chickMeal = mealBuilder.perpareChickenMeal();
System.out.println("chick meal");
chickMeal.showItems();
System.out.println("total cost:" + chickMeal.getCost());
}
}
原型模式(Prototype Pattern)
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
/**
* @ClassName: Shape
* @Description: 创建一个实现了 Clonable 接口的抽象类。
* @Author: xinyuan
* @CreateDate: 2018/9/24 14:16
*/
public abstract class Shape implements Cloneable{
private String id;
protected String type;
abstract void draw();
public String getType() {
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone(){
Object clone = null;
try{
clone = super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return clone;
}
}
/**
* @ClassName: Square
* @Description: java类作用描述
* @Author: xinyuan
* @CreateDate: 2018/9/24 14:23
*/
public class Square extends Shape{
public Square(){
type = "square";
}
public void draw(){
System.out.println("draw square");
}
}
/**
* @ClassName: Rectangle
* @Description: 拓展抽象类的实体类
* @Author: xinyuan
* @CreateDate: 2018/9/24 14:21
*/
public class Rectangle extends Shape{
public Rectangle(){
type = "Rectangle";
}
public void draw(){
System.out.println("draw rectangle");
}
}
import java.util.Hashtable;
/**
* @ClassName: ShapeCache
* @Description: 创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。
* @Author: xinyuan
* @CreateDate: 2018/9/24 14:55
*/
public class ShapeCache {
private static Hashtable<String,Shape> shapeMap = new Hashtable<String,Shape>();
public static Shape getShape(String shapeId){
Shape cachedShape = shapeMap.get(shapeId);
return (Shape)cachedShape.clone();
}
public static void loadCache(){
Square square = new Square();
square.setId("1");
shapeMap.put(square.getId(),square);
Rectangle rectangle = new Rectangle();
rectangle.setId("2");
shapeMap.put(rectangle.getId(),rectangle);
}
}
/**
* @ClassName: PrototypePatternDemo
* @Description: PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
* @Author: xinyuan
* @CreateDate: 2018/9/24 15:03
*/
public class PrototypePatternDemo {
public static void main(String[] args){
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape: " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape: " + clonedShape2.getType());
}
}