享元模式(Flyweight Pattern) 前言 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。
定义 享元模式,又称为轻量级模式,运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、 避免大量相似类的开销,从而提高系统资源的利用率。它是对象池的一种实现,类似于线程池,线程池可以避免不停的创建个销毁多个对象,消耗性能(用户态和内核态);【宗旨】 :共享细粒度对象,将多个对同一对象的访问集中起来;属于结构性模式;
内部状态和外部状态 在共享对象的过程中,又两种状态:内部状态: 内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
就是对象所共有的信息,比如火车票,多有从北京-上海的对象可以共用一个,而不是有1000张票,创建1000个对象;
外部状态: 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享;
也是不叫好理解的,当火车票被你购买之后,就和你的身份证号(唯一)绑定了,这属于外部状态;
使用场景 1、常常应用于系统底层的开发,以便解决系统的性能问题; 2、系统有大量相似对象,需要缓冲池的地方;
举个例子🌰 享元实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Examination { private String name; private String phone; private String subject; public Examination (String subject) { super (); this .subject = subject; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getPhone () { return phone; } public void setPhone (String phone) { this .phone = phone; } @Override public String toString () { return "Examination [name=" + name + ", phone=" + phone + ", subject=" + subject + "]" ; } }
享元工厂 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class ExaminationFactory { private static Map<String, Examination> pool = new HashMap<>(); public static Examination getexExamination (String name, String phone, String subject) { if (pool.containsKey(subject)) { Examination examination = pool.get(subject); examination.setName(name); examination.setPhone(phone); System.out.println("在缓存池取用对象:" + examination.toString()); return examination; } else { Examination examination = new Examination(subject); pool.put(subject, examination); examination.setName(name); examination.setPhone(phone); System.out.println("新建对象:" + examination.toString()); return examination; } } }
测试类 1 2 3 4 5 6 7 8 9 10 11 12 public class Client { public static void main (String[] args) { Examination A = ExaminationFactory.getexExamination("A" , "13812345678" , "软件工程" ); Examination B = ExaminationFactory.getexExamination("B" , "13812341234" , "软件工程" ); Examination C = ExaminationFactory.getexExamination("C" , "13812341328" , "电子信息工程" ); Examination D = ExaminationFactory.getexExamination("D" , "13812345111" , "桥梁工程工程" ); Examination E = ExaminationFactory.getexExamination("E" , "13812345444" , "软件工程" ); System.err.println(A.hashCode()); System.err.println(B.hashCode()); } }
测试结果 1 2 3 4 5 6 7 新建对象:Examination [name=A, phone=13812345678 , subject=软件工程] 在缓存池取用对象:Examination [name=B, phone=13812341234 , subject=软件工程] 新建对象:Examination [name=C, phone=13812341328 , subject=电子信息工程] 新建对象:Examination [name=D, phone=13812345111 , subject=桥梁工程工程] 在缓存池取用对象:Examination [name=E, phone=13812345444 , subject=软件工程] 1528902577 1528902577
实际应用 1、JDK – String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void main (String[] args) { String s1 = "hello" ; String s2 = "hello" ; String s3 = "he" + "llo" ; String s4 = "hel" + new String("lo" ); String s5 = new String("hello" ); String s6 = s5.intern(); String s7 = "h" ; String s8 = "ello" ; String s9 = s7 + s8; System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" ); System.out.println("s1 " + System.identityHashCode(s1)); System.out.println("s2 " + System.identityHashCode(s2)); System.out.println("s3 " + System.identityHashCode(s3)); System.out.println("s4 " + System.identityHashCode(s4)); System.out.println("s5 " + System.identityHashCode(s5)); System.out.println("s6 " + System.identityHashCode(s6)); System.out.println("s7 " + System.identityHashCode(s7)); System.out.println("s8 " + System.identityHashCode(s8)); System.out.println("s9 " + System.identityHashCode(s9)); System.out.print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" ); System.out.println(s1==s2); System.out.println(s1==s3); System.out.println(s1==s4); System.out.println(s1==s9); System.out.println(s4==s5); System.out.println(s1==s6); }
2、JDK – Integer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private static class IntegerCache { static final int low = -128 ; static final int high; static final Integer cache[]; static { int h = 127 ; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high" ); if (integerCacheHighPropValue != null ) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127 ); h = Math.min(i, Integer.MAX_VALUE - (-low) -1 ); } catch ( NumberFormatException nfe) { } } high = h; cache = new Integer[(high - low) + 1 ]; int j = low; for (int k = 0 ; k < cache.length; k++) cache[k] = new Integer(j++); assert IntegerCache.high >= 127 ; } private IntegerCache () {} }
3、线程池 4、Tomcat连接池
享元模式的优点✅ 1、减少对象的创建,降低内存中对象的数量,降低系统的内存使用,提升效率; 2、减少内存之外的其他资源占用,IO,带宽等;
享元模式的缺点 1、关注内部,外部状态,关注程序线程安全问题; 2、使系统、程序的逻辑变得复杂;
参考资料 1、[设计模式] - 享元模式 2、设计模式-享元模式 3、设计模式-享元设计模式