享元模式(Flyweight Pattern)
享元模式:主要用于减少创建对象的数量,以减少内存占用和提高性能;String常量池以及数据库连接池是享元模式的应用实例
含义: 享元模式会尝试重用现有的同类对象,如果未找到匹配的对象,才会进行创建对象的操作。
应用场景: 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
优点: 大大减少对象的创建,降低系统的内存,使效率提高
缺点: 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
代码演示
public class TestString {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
// abc 存在于常量池中,s1 s2都是指向常量池中abc的引用
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3 == s4);
System.out.println(s3.intern() == s1);
System.out.println(s3.intern() == s4.intern());
}
}
验证结果
true
false
false
true
true
s3.intern() : 在常量池中尝试获取是否存在s3对应值的引用,如果常量池中存在,则返回该引用
代码演示:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
class Bullet{
public UUID id = UUID.randomUUID();
boolean living;
@Override
public String toString() {
return "Bullet{" +
"id=" + id +
'}';
}
}
public class BulletPool {
List<Bullet> bullets = new ArrayList<>();
{
for(int i=0; i < 5; i++) {
bullets.add(new Bullet());
}
}
public Bullet getBullet() {
Random random = new Random();
int i = random.nextInt(bullets.size());
Bullet b = bullets.get(i);
if(i % 2 == 0){
b.living = false;
} else {
b.living = true;
}
if(!b.living) {
return b;
} else {
return new Bullet();
}
}
public static void main(String[] args) {
BulletPool bp = new BulletPool();
System.out.println(bp.bullets.toString());
System.out.println("==========================");
for(int i=0; i<5; i++) {
Bullet b = bp.getBullet();
System.out.println(b);
}
}
}
测试结果
[Bullet{id=08453500-8d78-4728-8211-218621de8dae},
Bullet{id=da537e07-4157-4892-b257-f1902ebec213},
Bullet{id=cfc2ce30-9bef-4586-be46-ec82a542e1a7},
Bullet{id=17e3a563-aec5-444c-b8ba-6bf0d96c0891},
Bullet{id=35388758-3af1-4419-b190-1a3a36ff7de2}]
==========================
Bullet{id=08453500-8d78-4728-8211-218621de8dae}
Bullet{id=08453500-8d78-4728-8211-218621de8dae}
Bullet{id=35388758-3af1-4419-b190-1a3a36ff7de2}
Bullet{id=cfc2ce30-9bef-4586-be46-ec82a542e1a7}
Bullet{id=a0478bd3-aa22-4ae6-a019-ae9c787b3aff}
注意事项:
1,实例需要注意划分内部状态和外部状态,否则可能会引起线程安全问题
2,这些类必须有一个工厂类加以控制