单例模式是类级别的,一个类只能有一个对象实例;
享元模式是对象级别的,可以有多个对象实例,多个变量引用同一个对象实例;
享元模式主要是为了节约内存空间,提高系统性能,而单例模式主要为了可以共享数据;
(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。
(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。
提高了系统复杂性,需要分离出外部状态(k)和内部状态(v),而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱,为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
● 内部状态
内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。
● 外部状态
外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。
public static void main(String[] args) {
String str1 = "法外狂徒";
String str2 = "张三";
String str3 = "法外狂徒张三";
String str4;
str4 = str1 + str2;
System.out.println(str3 == str4);
//如果是String的对象池中有该类型的值,则直接返回对象池中的对象
str4 = (str1 + str2).intern();
System.out.println(str3 == str4);
}
输出结果:false true
在范围[-128, 127]内在Integer缓存中直接取,范围外通过new Integer(i) 生成
//Integer中的缓存数组 cache
Integer[] cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Integer i1 = Integer.valueOf(99);
Integer i2 = Integer.valueOf(99);
System.out.println(i1==i2);
Integer i3 = Integer.valueOf(128);
Integer i4 = Integer.valueOf(128);
System.out.println(i3==i4);
输出结果:true false
public class Singleton {
// 我来写一个单例模式 懒汉式
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getSingleton(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
public static void main(String[] args) {
Singleton instance1 = Singleton.getSingleton();
Singleton instance2 = Singleton.getSingleton();
System.out.println(instance1==instance2);
}
}
定义抽象享元类
/**
* 定义抽象享元类
*/
public abstract class Book {
/**
* 外部状态 书名
*/
public String name;
public Book(String name) {
this.name = name;
}
/**
* 借书动作
*/
public abstract void borrow();
}
定义具体享元类
/**
* 定义具体享元类
*/
public class ConcreteBook extends Book {
/**
* 被借出的书名
*/
public ConcreteBook(String name){
super(name);
}
@Override
public void borrow() {
System.out.println("借了一本书名为:"+this.name);
}
}
享元工厂
/**
* 享元工厂
*/
public class LibraryFactory {
/**
* 图书馆维护一个图书列表
*/
private Map<String, Book> bookPools = new HashMap<>();
private static LibraryFactory factory = new LibraryFactory();
/**
* 图书馆只有一个
*/
public static LibraryFactory getInstance() {
return factory;
}
/**
* 图书馆外借图书
*/
public Book libToBorrow(String bookName) {
Book order;
//如果书架有书,直接借出
if (bookPools.containsKey(bookName)) {
order = bookPools.get(bookName);
} else {
//如果没有,那就拿本新书
//根据外部状态创建享元对象
order = new ConcreteBook(bookName);
//放到池中
bookPools.put(bookName, order);
}
return order;
}
//书架上书的数量
public int getAllBookSize() {
return bookPools.size();
}
}
控制层
@RestController
@RequestMapping("/studentController")
public class StudentController {
/**
* 图书馆书架上的书
*/
private static List<Book> books = new ArrayList<>();
private static LibraryFactory libraryFactory;
/**
* 查询
*/
@PostMapping("/borrow")
@ResponseBody
public void borrow() {
libraryFactory = LibraryFactory.getInstance();
studentBorrow("葵花宝典");
studentBorrow("僻邪剑谱");
studentBorrow("java从入门到放弃");
//把每一本书借出去
for (Book book : books) {
book.borrow();
}
System.out.println("===========================后两本秘籍没学会,又借了一次 ");
studentBorrow("僻邪剑谱");
studentBorrow("java从入门到放弃");
Book piXieJianPu = libraryFactory.libToBorrow("僻邪剑谱");
piXieJianPu.borrow();
Book ruMenFangQi = libraryFactory.libToBorrow("java从入门到放弃");
ruMenFangQi.borrow();
//输出一些学生一共借多少本书
System.out.println("学生一共借了 " + books.size() + " 本书! ");
//输出一下图书馆一共借出多少本书
System.out.println("图书馆实际借出" + libraryFactory.getAllBookSize() + " 本书");
}
/**
* 学生借书,将借的书,记录到图书馆书架列表
*/
private static void studentBorrow(String bookName) {
books.add(libraryFactory.libToBorrow(bookName));
}
}
源码地址SpringBoot2.XLearn 欢迎star
测试结果
在结果中可以看出:其实学生一共借了5次书,但是有两本是重复的,所以对于图书馆来说,其实是借出去了三本书。
参考资料
设计模式—享元模式
设计模式之享元模式
享元模式与单例模式区别