用示例代码来帮你了解享元模式
对于“设计模式”这个词大家肯定都不陌生,很多框架也用到了设计模式,但是大部分的开发者应该是没有深入的了解过,我准备硬肝下这23设计模式作为专题文章的开端,一共23种设计模式,我尽量在<23天肝完。
为什么要学习设计模式:https://blog.csdn.net/kaituozhe_sh/article/details/107922339
在我大学四年,对设计模式也没有什么概念,写代码就想着能实现就可以了,不会有设计模式那样的思想,但是当学习到了框架的时候,对于设计模式才有了一些更深入的了解,使用设计模式的代码在扩展性上会比暴力的代码更容易维护,特别是当一个程序猿离职了后,你去接手它的代码,里面是一大堆if else,这样真的会崩溃,修改都不知道从何下手
硬肝系列目录
创建型模式
结构型模式
行为型模式
到目前为止、23种设计模式的创建型模式已经给大家肝完了,现在我们进入到一个全新的章节,结构型模式!!!
什么是享元模式
享元模式,最主要的是利用共享的思想,将系统中有大量的对象、这些对象消耗大量的内存、且创建这些对象会耗费大量的资源,我们就可以用共享的思想来控制这些对象
共享思想:如果们单纯的来一个请求就创建一个对象的话,这样在并发量高的时候可能会造成OOM,为什么我们不把它们放到一个容器中管理呢?比如我们将共同的部分抽象出来,用唯一标识符来表示放到内存中,如果来一个业务请求,我们先去内存查找,如果内存中有这个对象的话直接返回即可,如果没有再去创建新对象
Java应用实例:redis缓存热点数据、数据库的缓冲池、String的常量池
下面我会使用hashmap来储存对象,用一个手机库存的例子来完成享元模式的讲解
上代码,首先我们定义一个统计数量的接口
public interface Count {
int getCount();
}
用一个实现类来实现它,在这个实现类中我们有货物的名称还有它的库存,现实生活中肯定还有一些乱七八糟的属性,在这里我们就简化了一下
@Data
public class Goods implements Count{
private String name;
private int count;
public Goods(String name) {
this.name = name;
}
@Override
public int getCount() {
return count;
}
}
然后这里有一个享元模式的工厂类,也就是我们常说的池,这里面存放的就是我们缓存的东西
public class GoodFactory {
//map池:用来缓存对象
private static final Map<String,Goods> STOCK = new HashMap<>();
private static final String GOODS[] = {"huawei","xiaomi","iphone","oppo","vvio"};
//我们通过货物名称为唯一标识来bie'an
public static Count getGoods(String name){
Goods good = (Goods)STOCK.get(name);
//如果池中不存在此货品,则创建此货品
if(good == null){
good = new Goods(name);
good.setCount((int) (Math.random()*100));
STOCK.put(good.getName(),good);
System.out.println("新加入货物: " + name + " 库存定为: " + good.getCount());
}else{
System.out.println("货物: " + name + " 更新库存: " + good.getCount());
}
return good;
}
}
上面这段代码就代表了享元模式意义,在我们新建一个对象的时候,先去池里找有没有这个对象,有的话直接返回,没有则创建新的对象并放入池中,减少了我们创建对象所耗费的资源以及控制了对象的数量
来验证一下我们的代码
public static void main(String[] args) {
int i = 0;
while(i<20){
Goods goods = (Goods) GoodFactory.getGoods(GOODS[(int) (Math.random()*GOODS.length)]);
goods.setCount((int) (Math.random()*1000));
i++;
}
}
输出结果:
这里我们上一个java实例给大家看看:String常量池原理
我想对于String类型大家肯定都很熟悉,但是大家知道创建一个String类型的对象的过程吗?我们先来看下面的这段代码
public class demo1 {
public static void main(String[] args) {
String s1 = "i'm ALiangX";
String s2 = new String("i'm ALiangX");
String s3 = "i'm ALiangX";
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//true
}
}
有人知道上面这两段代码的结果为什么不一样吗?我先给大家上副图,让大家看看上述代码执行时jvm的内存运行图
当我们使用指令String s1 = "i'm ALiangX"
直接创建出来的字符串他会直接进入堆中的一块区域,称为字符串常量池,而通过String s2 = new String("i'm ALiangX");
这段指令创建出来的字符串呢?他会先在堆中开辟一块新的区域,然后在栈中新建一个对象,这个对象指向的是堆中存放该字符串的地址,而且符号‘==’比较的是两个对象的地址,使用object.equals(object)比较的才是两个对象的内容,看上面的内存模型就知道为什么s1、s2两个对象相比较为false了
在字符串常量池中就是使用了我们享元模式的设计原理,因为对于String类型的字符串,它创建出来是不可改变的,这使得创建一个String对象的成本非常大,当我们创建s3时,他会先去常量池中检查有没有和它一样的对象在池里,如果有则直接返回池中的对象,没有的话才在池中创建一个新的对象
为什么String类型的对象创建之后不可以改变呢?我给大家看看String对象的源码
看到了吗,final修饰的对象创建之后不可以修改,所以String类型的对象创建出来不可以改变
享元模式的缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景:
1、系统有大量相似对象、且对象数量多
2、需要缓冲池的场景
完成:TO: 2021/3/24 22:59