享元模式
1概述
2 結構
public class MyCharacter {
private char mychar;
public MyCharacter(char mychar) {
this.mychar = mychar;
}
public void display() {
System.out.println(mychar);
}
}
public class MyCharacterFactory {
private Map<Character,MyCharacter> pool;
public MyCharacterFactory() {
pool = new HashMap<Character,MyCharacter>();
}
public MyCharacter getMyCharacter(Character character) {
MyCharacter myChar = pool.get(character);
if(myChar == null) {
myChar = new MyCharacter(character);
pool.put(character, myChar);
}
return myChar;
}
}
代碼示例說明
public class Person {
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
public class Teacher extends Person {
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Teacher(String name, int age, String sex,String number) {
super(name, age, sex);
this.number = number;
}
public Teacher() {
super();
}
}
public class TeacherFactory {
private Map<String,Teacher> pool;
public TeacherFactory() {
pool = new HashMap<String,Teacher>();
}
public Teacher getTeacher(String number) {
Teacher teacher = pool.get(number);
if(teacher == null) {
teacher = new Teacher();
teacher.setNumber(number);
pool.put(number, teacher);
}
return teacher;
}
}
public class MainClass {
public static void main(String[] args) {
TeacherFactory factory = new TeacherFactory();
Teacher teacher1 = factory.getTeacher("0102034");
Teacher teacher2 = factory.getTeacher("0102035");
Teacher teacher3 = factory.getTeacher("0102034");
Teacher teacher4 = factory.getTeacher("0102037");
System.out.println(teacher1.getNumber());
System.out.println(teacher2.getNumber());
System.out.println(teacher3.getNumber());
System.out.println(teacher4.getNumber());
if(teacher1 == teacher3) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
JDK源碼中用到的享元模式
String常量池和Integer等包裝類的緩存政策:Integer.valueOf(int i)
1. 首先String不屬于8種基本資料類型
String是一個對象。因為對象的預設值是null,是以String的預設值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性。
2. new String()和new String(“”)都是申明一個新的空字元串,是空串不是null;
3. String str=”kvill”;
String str=new String (“kvill”);的差別:
在這裡,我們不談堆,也不談棧,隻先簡單引入常量池這個簡單的概念。
常量池(constant pool)指的是在編譯期被确定,并被儲存在已編譯的.class檔案中的一些資料。它包括了關于類、方法、接口等中的常量,也包括字元串常量。
String s0=”kvill”;
String s1=”kvill”;
String s2=”kv” + “ill”;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
true
true
首先,我們要知結果為道Java會確定一個字元串常量隻有一個拷貝。
因為例子中的s0和s1中的”kvill”都是字元串常量,它們在編譯期就被确定了,是以s0==s1為true;而”kv”和”ill”也都是字元串常量,當一個字元串由多個字元串常量連接配接而成時,它自己肯定也是字元串常量,是以s2也同樣在編譯期就被解析為一個字元串常量,是以s2也是常量池中”kvill”的一個引用。
是以我們得出s0==s1==s2;
用new String() 建立的字元串不是常量,不能在編譯期就确定,是以new String() 建立的字元串不放入常量池中,它們有自己的位址空間。
String s0=”kvill”;
String s1=new String(”kvill”);
String s2=”kv” + new String(“ill”);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
結果為:
false
false
false
4. String.intern():
再補充介紹一點:存在于.class檔案中的常量池,在運作期被JVM裝載,并且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String執行個體str調用intern()方法時,Java查找常量池中是否有相同Unicode的字元串常量,如果有,則傳回其的引用,如果沒有,則在常量池中增加一個Unicode等于str的字元串并傳回它的引用
String s0= “kvill”;
String s1=new String(”kvill”);
String s2=new String(“kvill”);
System.out.println( s0==s1 );
System.out.println( “**********” );
s1.intern();
s2=s2.intern(); //把常量池中“kvill”的引用賦給s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
結果為
false
**********
false //雖然執行了s1.intern(),但它的傳回值沒有賦給s1
true //說明s1.intern()傳回的是常量池中”kvill”的引用
true
5. 關于equals()和==:
這個對于String簡單來說就是比較兩字元串的Unicode序列是否相當,如果相等傳回true;而==是比較兩字元串的位址是否相同,也就是是否是同一個字元串的引用。
6. 關于String是不可變的
這一說又要說很多,大家隻要知道String的執行個體一旦生成就不會再改變了,比如說:String str=”kv”+"ill"+" "+"ans”;
就是有4個字元串常量,首先”kv”和”ill”生成了”kvill”存在記憶體中,然後”kvill”又和” “ 生成 ”kvill “存在記憶體中,最後又和生成了”kvill ans”;并把這個字元串的位址賦給了str,就是因為String的“不可變”産生了很多臨時變量,這也就是為什麼建議用StringBuffer的原因了,因為StringBuffer是可改變的。
7String 享元模式
享元模式在編輯器系統中大量使用,一個文本編輯器往往會提供很多種字型,而通常的做法就是将每一個字母做成一個享元對象。享元對象的内蘊狀态就是 這個字母,而字母在文本中的位置和字型風等其他資訊則是外蘊狀态,比如字母a可能出現在文本的很多地方,雖然這些字母a的位置和字型風格不同,但是所有這 些地方使用的都是同個字母對象,這樣一來,字母對象就可以在整個系統中共享
在Java語言中,String類型就使用了享元模式.String對象是不變對象,一旦建立出來就不能改變,如果需要改變一個字元串的值,就隻 好建立一個新的String對象,在JVM内部, String對象都是共享的。如果一個系統中有兩個String對象所包含的字元串相同的話,JVM實際上隻建立一個String對象提供給兩個引用,從 而實作String對象的共享,String的inern()方法給出這個字元串在共享池中的唯一執行個體.
享元模式應當在什麼情況下使用
(1)一個系統有大量的對象
(2)這些對象耗費大量的記憶體
(3)這些對象可以按照内蘊狀态分成很多的組,當把外蘊對象從對象中删除時,每一個組都可以僅用一個對象代替
(4)使用享元模式需要維護一個記錄了系統已有的所有享元的表,而這需要耗費資源,是以應當在有足夠多的享元實作可供共享時才值的使用享元模式.
享元模式的優點和缺點
享元模式的優點在于它大幅度地降低記憶體中的對象的數量,但是也為這一點付出了代價:
(1)為了使對象可以共享,需要将一些狀态外部化,這使得程式的邏輯複雜化
(2)享元模式将享元對象的狀态外部化,而讀取外部狀态使得運作時間稍微變長.