低头需要勇气,抬头需要底气
前几年,我们国家出现了一种新的经济,共享经济。比如说我们常见的:共享单车、共享充电宝、共享汽车等等。他们共同的特点就是对一件物品的重复利用。那么代码世界中,同样存在这种模式——享元模式。
1. 享元模式
定义
享元模式又称为轻量级模式,是对象池的一种实现。类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。提供减少对象数量从而改善应用所需的对象结构的方式。
宗旨:共享细粒度对象,将多个对同一个对象的访问集中起来。
属于结构型模式
适用场景
常常应用于系统底层的开发,以便解决系统的性能问题。
系统有大量相似的对象,需要缓冲池的场景。
优点
减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率
减少内存之外的其他资源占用
缺点
关注内、外部状态、关注线程安全问题
是系统、程序的逻辑复杂化
2. 火车票查询案例
火车票查询系统:我们每次查询出发站到目的站的火车票价,硬座,软座,硬卧等等。如果每个人查的时候都new 一个新的对象,那么对系统内存来讲,在春运的时候是灾难级的,我们可以利用享元模式来解决这个问题。下面看看代码怎么写
public interface ITicket {
void showInfo(String bunk);
}
public class TrainTicket implements ITicket {
private String from;
private String to;
private int price;
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
public void showInfo(String bunk) {
this.price = new Random().nextInt(500);
System.out.println(from + "->" + to + ":" + bunk + "价格:" + this.price);
}
}
public class TicketFactory {
private static Map<String, ITicket> pool = new ConcurrentHashMap<String, ITicket>();
public static ITicket queryTicket(String from, String to) {
String key = from + "->" + to;
if (pool.containsKey(key)) {
System.out.println("使用缓存:" + key);
return pool.get(key);
}
System.out.println("首次查询,创建对象");
ITicket ticket = new TrainTicket(from, to);
pool.put(key, ticket);
return ticket;
}
}
public class Test {
public static void main(String[] args) {
ITicket ticket = TicketFactory.queryTicket("北京西", "武汉");
ticket.showInfo("硬座");
ticket = TicketFactory.queryTicket("北京西", "武汉");
ticket.showInfo("软座");
ticket = TicketFactory.queryTicket("北京西", "武汉");
ticket.showInfo("硬卧");
}
}
首次查询,创建对象
北京西->武汉:硬座价格:125
使用缓存:北京西->武汉
北京西->武汉:软座价格:176
使用缓存:北京西->武汉
北京西->武汉:硬卧价格:305
3. 数据库连接池案例
数据库连接池也是一个很好的案例:每次要用Connection
,如果大量的new,肯定会对系统的性能有一定的影响,如果利用享元模式,那这么写代码呢?
public class ConnectionPool {
private Vector<Connection> pool;
private final String url = "jdbc:mysql://localhost:3306/test";
private final String username = "root";
private final String password = "root";
private final String driverClassName = "com.mysql.jdbc.Driver";
private final int poolSize = 20;
public ConnectionPool() {
this.pool = new Vector<Connection>();
for (int i = 0; i < poolSize; i++) {
try {
Class.forName(driverClassName);
Connection conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized Connection getConnection() {
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
}
return null;
}
public synchronized void release(Connection conn) {
pool.add(conn);
}
}
4. String中的享元模式
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = "he" + "llo";
String s4 = "he" + new String("llo");
String s5 = new String("hello");
String s6 = s5.intern();
String s7 = "h";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2);// true
System.out.println(s1 == s3);// true
System.out.println(s1 == s4);//false
System.out.println(s1 == s5);//false
System.out.println(s4 == s5);//false
System.out.println(s1 == s6);//true
System.out.println(s1 == s9);//false
}
String
本身会在系统运行过程中缓存常量,放在常量池中。直接赋值的方式初始化一个String
,new 一个字符串的方式会在内存中新开辟一段空间,所以new的对象都不相等。s6
是s5.intern()
方法返回值,是一个常量,所以s1 = s6
,s9 = s7 + s8
,是s7
和s8
相加,并不是常量相加。所以s1 != s9
。
5. Integer中的享元模式
public static void main(String[] args) {
Integer a = Integer.valueOf(100);
Integer b = 100;
System.out.println(a == b); // true
Integer c = Integer.valueOf(1000);
Integer d = 1000;
System.out.println(c == d); //false
}
同样的代码,只是100换成了1000,结果就完全不相等。原理就是Integer
的valueOf
方法是用的缓存,缓存范围就是-128-127之间。这就是享元模式。
感谢您阅读本文,如果您觉得文章写的对您有用的话,请您点击上面的“关注”,点个赞,这样您就可以持续收到《JAVA架构师之路》的最新文章了。文章内容属于自己的一点点心得,难免有不对的地方,欢迎在下方评论区探讨,你们的关注是我创作优质文章的动力。