前言:
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。用於解決特定環境下、重複出現的特定問題的解決方案。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。(引用---點擊打開鏈接)
一、單例模式(SingletonPattern)
a、單例模式:是一種常見的設計模式。爲了保證一個類在內存中只能有一個對象。
思路:1、如果其他程序能夠隨意用new創建該類對象,那麼就無法控制個數。因此,不讓其他程序用new創建該類的對象。
2、既然不讓其他程序new該類對象,那麼該類在自己內部就要創建一個對象,否則該類就永遠無法創建對象了。
3、該類將創建的對象對外(整個系統)提供,讓其他程序獲取並使用。
步驟:1、將該類中的構造函數私有化。
2、在本類中創建一個本類對象。
3、定義一個方法,返回值類型是本類類型。讓其他程序通過該方法就可以獲取到該類對象。
單例模式中又有:餓漢式和懶漢式兩種
1、餓漢式:不管你需不需要,都在類內部直接先生成一個對象。因爲構造方法爲私有的,所以外面無法new新的對象,只能通過getInstance來獲得這個唯一的對象,從而實現單例模式
/*餓漢式*/
public class Single {
private static final Single s=new Single();
private Single(){
}
public static Single getInstance(){
return s;
}
}
2、懶漢式:與餓漢式不同之處在於,不是直接生成一個對象,而是在需要對象是纔會生成,也就是隻有當調用getInstance這個方法是纔會產生這個唯一對象。(也可以說是單例的延遲加載方式)
/*懶漢式*/
public class Single2 {
private static Single2 s;
private Single2(){
}
public static Single2 getInstance(){
if(s==null){
s = new Single2();
}
return s;
}
}
這個類可以滿足基本要求,但是,像這樣毫無線程安全保護的類,如果我們把它放入多線程的環境下,肯定就會出現問題了(問題就是假如有多個線程同時到達if(s==null)時,這個條件都成立,那麼就會產生多個對象了,就違背了我們的本意),如何解決?我們首先會想到對getInstance方法加synchronized關鍵字。所以該爲下面這句即可。
public static synchronized Single2 getInstance();
但是,synchronized關鍵字鎖住的是這個對象,這樣的用法,在性能上會有所下降,因爲每次調用getInstance(),都要對對象上鎖,事實上,只有在第一次創建對象的時候需要加鎖,之後就不需要了,所以,這個地方需要改進:public static synchronized Single2 getInstance(){
if(s==null){
synchronized (s) {
<span style="white-space:pre"> </span>if (s == null) {
<span style="white-space:pre"> </span> s = new Single2();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
}
return s;
}
看上去寫的很好,但在自己運行時,就會發現還是有錯誤,雖然知道是線程的原因,卻沒有深究。大概知道那麼回事。看看大神的準確解答:在Java指令中創建對象和賦值操作是分開進行的,也就是說instance
= new Singleton();語句是分兩步執行的。但是JVM並不保證這兩個操作的先後順序,也就是說有可能JVM會爲新的Singleton實例分配空間,然後直接賦值給instance成員,然後再去初始化這個Singleton實例。這樣就可能出錯了。
b、單例變形-----多例 (“單例+緩存”技術)
★ 緩存在單例中的使用
緩存在編程中使用很頻繁,有着非常重要的作用,它能夠幫助程序實現以空間換取時間,通常被設計成整個應用程序所共享的一個空間,現要求實現一個用緩存存放單例對象的類。
說明:該緩存中可以存放多個該類對象,每個對象以一個key值標識,key值相同時所訪問的是同一個單例對象。
import java.util.HashMap;
import java.util.Map;
public class A {
//定義一個緩存(集合),用來存放數據的容器
private static Map<String,A> map = new HashMap<String,A>();
public static A getInstance(String key){
A a = map.get(key);
//判斷a是否存在,不存在則是null
if(a==null){
a = new A();//新建一個對象
map.put(key, a);//把新對象放入緩存
}
return a;
}
}
★ 單例變形——多例模式(“單例+緩存+控制實例個數”技術)
把上面緩存的單例實現,做成一個能夠控制對象個數的共享空間,供整個應用程序使用。在緩存中維護指定個數的對象,每個對象的key值由該類內部指定,有外部請求時直接返回其中一個對象出去。
說明:相當於維護一個指定數量的對象池,當請求個數超過控制的總數時,開始循環重複使用 。
import java.util.HashMap;
import java.util.Map;
public class Multiple {
private static Map<Integer,Multiple> map = new HashMap<Integer,Multiple>();
private static int num=1;
private static int count=3;//控制實例的個數:3
public static Multiple getInstance(){
Multiple m = map.get(num);
if(m==null){
m = new Multiple();
map.put(num, m);
}
num++;
//如果num超過所控制的個數,則重新設爲1,以進行循環重複使用緩存中的對象
if(num>count){
num=1;
}
return m;
}
}
二、工廠模式(Factory Pattern)
工廠模式:Java程序開發講究面向接口編程,通過建立一個工廠類,隱藏具體的實現類。
首先創建公共接口:
public interface Api {
public abstract String t1();
}
然後創建實現類:
public class Impl implements Api {
@Override
public String t1() {
return "11111111111111";
}
}
public class Impl2 implements Api {
@Override
public String t1() {
return "222222";
}
}
最後創建工廠類:
public class DepFactory {
public static Api createApi(){
return new Impl2();//通過配置文件+類反射,讓我們的程序依賴字符串
}
}
測試:
public class Client {
public static void main(String[] args) {
Api obj = DepFactory.createApi();//new Impl();
String str = obj.t1();
System.out.println(str);
}
}
結果:222222三、值對象模式(Value Object Pattern)
值對象模式:簡單的說就是封裝數據,方便各個模塊之間的數據的交流。
★ 基本的編寫步驟:
1、寫一個類,實現可序列化(如果以後數據是往數據庫裏存的,那麼可以不序列化,節省資源)
2、私有化所有屬性,保持一個默認構造方法(public無參)
3、爲每個屬性提供get()、set()方法(如果是boolean型變量,最好把get改成is)
4、推薦覆蓋實現equals()、hashCode()和toString()方法
public class UserModel implements Serializable {
private String id,name,address;
private boolean man;
public UserModel(String name){
this.name = name;
}
public UserModel(){
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public boolean isMan() {
return man;
}
public void setMan(boolean man) {
this.man = man;
}
//hashCode和equals一般只用“主鍵”來生成
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UserModel other = (UserModel) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
return "UserModel [id=" + id + ", name=" + name + ", address="
+ address + ", man=" + man + "]";
}
}
四、裝飾模式(Decorator Pattern)
裝飾模式:在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
例:寫一個MyBufferedReader類,使它能夠對字符流(如FileReader、InputStreamReader和PipedReader等)進行功能增強:
(1) 提供帶緩衝的myRead()方法,對原有的read()方法進行增速;
(2)提供一個能夠每次讀取一行字符的myReadLine()方法。
思路:一般要實現一個類的功能就想到繼承這個類。但是這裏要實現多個類的功能,而繼承只能繼承一個。那就不得不寫多個類了,這樣就會顯得臃腫。
另一種方法就是封裝,不用繼承,但是缺點和上面一樣。
所以想到把二者結合起來,從而達到要求。也就是裝飾模式。
import java.io.IOException;
import java.io.Reader;
public class MyBufferedReader extends Reader{ //※※※讓加強類融入到體系中
private Reader r;//封裝
private char[] buf = new char[1024];
private int count=0;//記錄當前緩衝區中字符的個數
private int pos=0;//數組元素的下標,當前所讀的位置
public MyBufferedReader(Reader r){
this.r = r;
}
public int myRead() throws IOException{
if(count==0){
count = r.read(buf);
pos=0;
}
if(count==-1){
return -1;
}
char ch = buf[pos];
pos++;
count--;
return ch;
}
public String myReadLine() throws IOException{
StringBuilder sb = new StringBuilder();
int ch=0;
while((ch=myRead())!=-1){
if(ch=='\r'){
continue;
}
if(ch=='\n'){
return sb.toString();
}
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void close() throws IOException{
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
}