Java進階(注解,設計模式,對象克隆)
一. 注解
1.1 什么是注解
java中注解(Annotation),又稱java標注,是一種特殊的注釋
可以添加在包,類,成員變量,方法,參數等內容上
注解會隨同代碼被編譯到字節碼文件中
在運行時,可以通過反射機制獲取到類中注解,然后根據不同的注解進行相應的解析
1.2 內置注解
java中已經定義好的注解
//檢查該方法是否是重寫方法。如果發現其父類,或者是引用的接口中沒有該方法時,會發生編譯報錯
@Override
//標記過時方法。如果使用該方法,會報編譯警告
@Deprecated
//指示編譯器去忽略注解中聲明的警告
@SuppressWarnings
//用于指示被修飾的接口是函數式接口
//函數式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口,具體實現可以用Lambda表達式
@Functionallnterface
@Functionallnterface:
比如Runnable中有個@Functionallnterface注解,當我們在使用Thread時,需要實現Runnable中的run。而Runnable就是函數式接口,需要創建內部類實現。
new Thread(new Runnable(){@Overridepublic void run(){}
})
1.3 元注解
是注解的注解,用來定義其他注解的注解
@Target(ElementType.METHOD) //標注此注解可以作用在哪些內容上面
@Target(ElementType.TYPE,ElementType.METHOD) //標注此注解可以作用在方法和類上@Retention(RetentionPolicy.SOURCE) //在編譯階段有用的,可以不編譯到字節碼中
@Retention(RetentionPolicy.RUNTIME) //在運行中有用,編譯到字節碼中
1.4 自定義注解
聲明注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {//聲明注解的參數String message() default "";int minlength() default 0;String lengthmessage() default "";
}
解析注解:
public class Test {public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception {User user = new User();//user.setName("jim");//通過反射解析User類中注解Field[] fields = user.getClass().getDeclaredFields();//拿到類中所有的成員變量 連同私有的也可以獲取//循環所有的屬性for (Field field : fields) {NotNull notNull = field.getAnnotation(NotNull.class);//獲取屬性上面 名字為NotNull注解if (notNull != null) {//通過屬性,生成對應的get方法Method m = user.getClass().getMethod("get" + getMethodName(field.getName()));//調用方法 obj就是get方法的返回值Object obj=m.invoke(user);if (obj==null) {System.err.println(field.getName() +notNull.message());throw new NullPointerException(notNull.message());}else{if(String.valueOf(obj).length()<(notNull.minlength())){System.err.println(field.getName() +notNull.lengthmessage());throw new NullPointerException(notNull.lengthmessage());}}}}}/*** 把一個字符串的第一個字母大寫*/private static String getMethodName(String fildeName) throws Exception {byte[] items = fildeName.getBytes();items[0] = (byte) ((char) items[0] - 'a' + 'A');return new String(items);}
}
使用注解:
public class User {private int num=0;@NotNull(message="姓名不能為空",minlength=3,lengthmessage="長度不能小于3")private String name=null;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}}
二. 對象克隆
2.1 為什么要克隆
為什么不可以直接new一個對象呢?因為直接new一個對象的屬性都是初始化的值,所以當需要一個對象直接來保存當前對象的狀態就要clone()了
我們常見的
Student stu1=new Student();
Student stu2=stu1;
這只是復制的引用,也就是對象在內存中的地址被復制,ab對象都指向了同一個對象。只能稱為引用復制,兩個引用指向的還是一個對象。
2.2 如何實現克隆
有兩種克隆方法深克隆和淺克隆
基本類型的值可以直接復制,引用類型的值只能復制引用地址。所以深淺克隆的區別在于是否支持引用類型的成員變量的復制。
@Override
protected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();return person;
}
Person p1 = new Person(100,"jim");
Person p2 = p1.clone(); //克隆一個新的對象System.out.println(p1==p2);//false,說明克隆成功,兩個對象地址不一樣,是不同的對象
2.2.1 淺克隆和深克隆
對于基本類型,在對象克隆時,可以將值直接復制到新對象中。
int a=10;
int b=a;
b=20; //b改變了a不受影響
只想把值屬性克隆然后只把對象屬性的地址復制則就是淺克隆
如果克隆還想要復制一個對象屬性,在克隆一個對象時同時克隆了它的關聯對象則就是深克隆
2.2.2 如何實現深克隆
方式1:在克隆對象時,將對象中關聯的對象也一同進行克隆,雖然能實現,但是要逐級進行克隆,層級較多時,比較麻煩
@Override
protected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();person.address = (Address)person.address.clone(); //深度復制 聯同person中關聯的對象也一同克隆.return person;
}
方式2:使用對象序列化(IO) 需要我們定義一個克隆方法,先將對象序列化再反序列化,自動將多級關聯的對象也一并重新創建,使用起來比較方便
? 對象序列化:將java中的對象輸出到一個文件中
? ObjectOutputStream
? 反序列化:將文件中信息輸入到程序,創建一個新的對象
? ObjectInputStream
public Person myclone() {Person person = null;try { // 將該對象序列化成流,因為寫在流里的是對象的一個拷貝,而原對象仍然存在于JVM里面。所以利用這個特性可以實現對象的深拷貝ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);// 將流序列化成對象ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);person = (Person) ois.readObject();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return person;
}
三. 設計模式
3.1 概述
設計模式起源于建筑領域,在1990年軟件領域也誕生設計模式。直到1995年在《設計模式:可復用面向對象軟件的基礎》中收納總結了23種設計模式。
什么是設計模式?
在長期編程的過程中,針對某一類問題經過反復的優化,最終總結出一個固定的解決方案,這些方案經過反復地使用,具有普遍性。
為什么要學設計模式?
使設計的代碼可重用性高,可擴展性高,使程序設計標準化,提高效率。能夠更好的去理解源碼架構
3.2 建模語言
3.2.1 類
統一建模語言(Unified Modeling Language,UML)是一套軟件設計和分析的語言工具,用圖形化的方式記錄類與類,類與接口,接口與接口之間的關系。
(1) 類名(Name)是一個字符串,例如,Student。
(2) 屬性(Attribute)是指類的特性,即類的成員變量。UML 按以下格式表示: [可見性]屬性名:類型[=默認值] 例如:-name:String 注意:“可見性”表示該屬性對類外的元素是否可見,包括公有(Public)、私 有(Private)、受保護(Protected)和朋友(Friendly)4 種,在類圖中分別 用符號+、-、#、~表示。
(3) 操作(Operations)是類的任意一個實例對象都可以使用的行為,是類的成 員方法。UML 按以下格式表示: [可見性]名稱(參數列表)[:返回類型] 例如:+display():void。
3.2.2 接口
接口(Interface)是一種特殊的類,它具有類的結構但不可被實例化,只可以 被子類實現。它包含抽象操作,但不包含屬性。它描述了類或組件對外可見的動 作。在 UML 中,接口使用一個帶有名稱的小圓圈來進行表示。
3.2.3 類之間的關系
在軟件系統中,類不是孤立存在的,類與類之間存在各種關系。根據類與類 之間的耦合度從弱到強排列,UML 中的類圖有以下幾種關系:依賴關系、關聯 關系、聚合關系、組合關系、泛化關系和實現關系。其中泛化和實現的耦合度相 等,它們是最強的
3.2.3.1 依賴關系
在一個類中的方法,把另一個類作為參數進行使用,具有臨時性。
方法執行結束后,依賴關系就不存在了。
一般把xxx類用到了yyy類,這種關系稱為依賴關系,也成為use-a關系
3.2.3.2 關聯關系
在一個類中,把另一個當做自己的成員,比如老師和學生,師傅和徒弟。關聯可以是單向關聯,雙向關聯,自關聯。
自關聯:
3.2.3.3 聚合關系
聚合關系也是一種關聯關系,是強關聯關系,是整體和部分之間的關系。
比如學校與老師,學校包含老師,如果學校沒了,老師依然存在。
3.2.3.4 組合關系
組合關系表示類之間整體與部分的關系,是一種更強烈的聚合關系。
比如頭和嘴,一旦頭不存在,嘴也就不存在。
3.2.3.5 繼承關系
繼承關系是對象之間耦合度最大的一種關系,是父類與子類的關系,is-a關系
3.2.3.6 實現關系
實現關系是接口和實現類之間的關系。類實現了接口,類中的操作實現了接口中所聲明的所有的抽象操作。
在UML類圖中,實現關系使用空心三角箭頭的虛線來表示,箭頭從實現類指向接口。比如汽車和船實現了交通工具。其類圖如下:
3.3 面向對象設計原則
3.3.1 單一職責原則
一個類只負責某一個具體功能,細化類的顆粒度
3.3.2 開閉原則
對修改關閉,對擴展開放
盡可能在擴展功能時,不要修改已有代碼,盡可能擴展一個新類實現新功能
3.3.3 里氏替換原則
繼承優勢:提高代碼復用性,子類繼承父類的功能
? 提高代碼的擴展性,子類還可以擴展自己功能,不影響其他類,重寫父類方法
? 劣勢:繼承使得類的體系結構變得復雜了
里氏替換:當子類繼承了父類后,在使用時,用子類替換父類后,要確保父類中的功能不受影響。比如父類的方法目的是要完成一個乘法,子類重寫后變成先乘再加了,這樣就改變了父類這個方法的功能了,子類重寫方法的前提是不改變父類方法的目的,子類可以通過別的方式完成這個乘法,但是不能改變它這個乘法的功能。
所以可以把父類方法寫成抽象方法,不具體實現,等子類繼承后實現
主要的思想:保證程序的穩定性
3.3.4 組合/聚合(關聯關系)復用原則
繼承使得類的體系變得復雜,如果我們只是想使用某個類中的方法,可以優先選擇關聯/依賴關系,降低類之間的耦合度。
比如B中使用A的某個方法,可以直接在B里聲明一個A屬性
class b{A a;public void use(){a.method();}
}
或者在B里寫一個使用A中方法的方法,把A當參數傳進來
class b{public void use(A a){a.method();}
}
3.3.5 依賴倒置
面向抽象編程,不要面向具體實現變成
具體實現應該依賴抽象層(多態,抽象層表示,具體的子實現類)
3.3.6 接口隔離
不要把所有的功能都定義到一個總的接口中,應該把不同的種類的功能定義在不同的接口中,讓實現類根據自己的需要去靈活的選擇
3.3.7 迪米特
只跟朋友聯系,不跟陌生人說話。
在程序之間相互調用時,如果兩個類沒有直接聯系,但是想相互調用,可以通過第三方進行轉發調用。降低模塊之間的耦合度。
四. 23種設計模式
4.1 單例模式
在一個項目中,如何確保一個類始終只有一個對象
4.1.1 餓漢式(急切式單例)
單例模式中的餓漢式(急切式單例)
在加載此類時,就已經將唯一的一個對象創建出來
好處:不會存在線程安全問題
不足:在類加載時,就會創建單例對象,有可能一段時間內還用不到它
public class MyWindow {//用static修飾,在第一次加載此類時已經把唯一的一個對象創建出來,后面getMyWindow都是這同一個MyWindow//在內部自己創建的一個單例對象static MyWindow myWindow=new MyWindow();private MyWindow(){}public static MyWindow getMyWindow(){return myWindow;}
}
eg:Runtime單例
4.1.2 懶漢式
在類加載時,并沒有創建單例對象,在第一次獲取單例對象時,才創建了單例對象
好處:類加載時先不創建,在第一次使用獲取時才會創建
不足:會出現線程安全問題,所以需要加鎖解決
public class MyWindow {private static MyWindow myWindow;//構造方法私有化,不讓外界訪問private MyWindow(){}
}
寫法1:會出現線程安全問題,多個線程同時進入,會返回多個對象
public static MyWindow getMyWindow(){if(myWindow==null){myWindow = new MyWindow();}return myWindow;
}
寫法2:為方法加鎖,效率低 一次只能有一個線程進入到該方法
public class MyWindow2 {private static MyWindow2 myWindow2;private static synchronized MyWindow2(){}public static MyWindow2 getMyWindow2(){if(myWindow2==null){myWindow2=new MyWindow2();}return myWindow2;}
}
寫法3:雙重檢索
public static MyWindow getMyWindow(){if(myWindow==null){synchronized (MyWindow.class){if(myWindow==null){myWindow = new MyWindow();}}}return myWindow;
}
寫法4:
雙重檢索 + volatile(可見性,避免重排序)
A a = new A();
創建對象這一條語句編譯為指令時,可以分為三個指令
- new 申請空間
- 調用構造方法初始化對象
- 把對象地址 賦給引用變量
如果按照這個正常的順序執行,是沒有問題的,
但是執行時,如果2,3條指令順序發生變化,導致把沒有初始化完成的對象地址返回了,拿去使用了,這么做會出問題,
因為對象沒有初始化完成.
所有需要使用volatile關鍵修飾單例成員變量,確保對其賦值時,指令不重新排序
private volatile static MyWindow myWindow;
public static MyWindow getMyWindow(){if(myWindow==null){synchronized (MyWindow.class){if(myWindow==null){myWindow = new MyWindow();}}}return myWindow;
}
4.2 工廠模式
4.2.1 簡單工廠模式
簡單工廠并不是一種設計模式,違背了開閉原則
主要是引出了工廠方法和抽象工廠模式
涉及的角色
工廠角色:根據我們的需求負責創建對應的對象
抽象產品:具體產品的抽象,具體產品實現/繼承抽象產品
? 可以使用上層的抽象父類,表示任意的子類對象
具體產品:具體的對象
優點:創建對象和使用對象分離了
缺點:只能創建實現同一個父類/接口的子類對象,擴展新的類型,需要修改工廠,違背了開閉原則
? 適合簡單的,子類較少的場景
CarFactory:
public class CarFactory {public static Car createCar(String name){if(name.equals("aodi")){return new Aodi();}if(name.equals("bmw")){return new Bmw();}return null;}
}
Car:
public interface Car {void run();
}
Bmw:
public class Bmw implements Car{@Overridepublic void run() {System.out.println("寶馬汽車行駛");}
}
Aodi:
public class Aodi implements Car{@Overridepublic void run() {System.out.println("奧迪汽車行駛");}
}
這樣子設計可以使得我們把創建對象的任務交給工廠,我們只需要輸入創建誰,由工廠給我們創建。可是這樣子,我們每有一個新品牌的車,就要寫一行代碼創建這個車。這樣子違背了開閉原則,所以簡單工廠模式不屬于23種設計模式。
4.2.2 工廠方法模式
由于簡單工廠中,一個工廠,可以造同一類型的所有具體產品,導致簡單工廠比較復雜,擴展一個新類型時,需要修改工廠代碼。
工廠方法模式,為工廠也進行抽象,并且為同類型每個具體產品都創建了一個具體的工廠。
每一個工廠負責創建一個具體的產品(類型)對象
這樣擴展一個新的類型,與之對應一個工廠,就不需要修改工廠了,遵守了開閉原則,單一職責原則
CarFactory:
public interface CarFactory {Car createCar();
}
Car:
public interface Car {void run();
}
AodiFactory:
public class AodiFactory implements CarFactory{@Overridepublic Car createCar() {return new Aodi();}
}
BmwFactory:
public class BmwFactory implements CarFactory{@Overridepublic Car createCar() {return new Bmw();}
}
Aodi:
public class Aodi implements Car {@Overridepublic void run() {System.out.println("奧迪汽車行駛");}
}
test:
CarFactory aodicarFactory = new AodiFactory();
Car aodi = aodicarFactory.createCar();
aodi.run();CarFactory bmwcarFactory = new BmwFactory();
Car bmw = bmwcarFactory.createCar();
bmw.run();
現在我們用什么工廠造什么汽車。
4.2.3 抽象工廠模式
工廠方法模式,是按照產品類型進行分類的,一類產品對應一類工廠,不同類型產品之間,相互隔離的。
例如華為和小米,既要造汽車又要造手機,都是屬于同一家的產品。但是工廠方法這種設計,同一個公司產品與產品之間沒有聯系。抽象工長對工廠重新進行分類,以公司為單位進行工廠的抽象(提取),一個工廠內,可以創建不同的產品。這樣我們就可以創建出像華為工廠,小米工廠這樣的具體工廠。
AbstractFactory:
public interface AbstractFactory {Car getCar();Phone getPhone();
}
Car:
public interface Car {void run();
}
Phone:
public interface Phone {void call();
}
AodiFactory:
public class AodiFactory implements AbstractFactory{@Overridepublic Car getCar() {return new AodiCar();}@Overridepublic Phone getPhone() {return new AodiPhone();}
}
AodiCar:
public class AodiCar implements Car{@Overridepublic void run() {System.out.println("奧迪汽車行駛");}
}
AodiPhone:
public class AodiPhone implements Phone{@Overridepublic void call() {System.out.println("奧迪手機打電話");}
}
BmwFactory
public class BmwFactory implements AbstractFactory{@Overridepublic Car getCar() {return new BmwCar();}@Overridepublic Phone getPhone() {return new BmwPhone();}
}
BmwCar:
public class BmwCar implements Car{@Overridepublic void run() {System.out.println("寶馬汽車行駛");}
}
BmwPhone:
public class BmwPhone implements Phone {@Overridepublic void call() {System.out.println("寶馬手機打電話");}
}
我們創建一個抽象工廠,讓新車或者新手機牌子擁有一個新的自己的工廠來繼承這個抽象工廠。每次需要某個對象,就調用對應工廠的方法。這樣子我們就可以從擴展代碼變成擴展一個新類。
4.3 原型模式
在某些場景下,為避免自己手動new對象,我們可以使用對象克隆方式,創建并返回一個新的對象,這種克隆新對象的效率比我們自己new的效率要高。
對象克隆實現方式:
1.實現Cloneable接口,重寫Clone
2.使用對象序列化 反序列化重新生成對象
注意深克隆淺克隆問題
4.4 代理模式
在不修改原來代碼的前提下,為我們方法添加額外的功能。通過代理對象幫助我們進行調用。
有些時候,目標對象(比如汽車廠)不想或不能直接與客戶打交道,通過代理對象進行訪問,代理對象可以保護目標對象,對目標對象功能進行擴展,降低了模塊之間的耦合度。
涉及到三個主題:
- 抽象主題:抽取的功能,讓目標對象進行實現,以及代理對象進行實現
- 具體主題:真正要實現功能的類
- 代理對象
代理模式實現方式又有兩種:
4.4.1 靜態代理:
創建一個代理類,代理實現與具體對象相同的接口/抽象類,重寫抽象方法。
還有一個成員變量,可以用于接受具體的主題
在代理對象中重寫的抽象方法中,調用真實主題方法,這樣就可以在調用之前和之后添加額外的功能。
CarFactoryImpl:
/*汽車廠*/
public class CarFactoryImpl implements Sell {@Overridepublic void sell() {System.out.println("汽車廠賣汽車");}}
Sell:
/*抽象操作定義 賣東西*/
public interface Sell {void sell();}
StaticProxy:
/*靜態代理,實際中很少使用靜態代理,因為其代理類實現的接口必須與目標類實現接口一致,擴展起來就比較麻煩*/
public class StaticProxy implements Sell {Sell sell;public StaticProxy(Sell sell) {this.sell = sell;}@Overridepublic void sell() {System.out.println("汽車介紹");sell.sell();System.out.println("辦理手續");}}
Test:
public class Test {public static void main(String[] args) {Sell carFactory = new CarFactoryImpl();//創建汽車廠代理對象StaticProxy staticProxy = new StaticProxy(carFactory);staticProxy.sell();}
}
不好的地方:一個代理對象,只能代理一個接口類型的對象,不靈活
4.4.2 動態代理:
-
jdk代理:
jdk代理實現是通過反射機制實現的,目標類必須要實現一個接口,通過接口動態獲得目標類中的信息
CarFactoryImpl:
/*汽車廠*/
public class CarFactoryImpl implements Sell {@Overridepublic void sell() {System.out.println("汽車廠賣汽車");}}
DynamicProxy:
/*動態代理類代理類不需要實現與目標類相同的接口,這樣就可以代理任意的目標類但是是有要求的,目標類必需實現接口,此種方式是動態代理的實現方式之一: jdk代理 是一種純反射機制實現(動態獲取目標類接口方法)*/
public class DynamicProxy implements InvocationHandler {Object object;//真實對象,接收任何的目標類對象public DynamicProxy(Object object) {this.object = object;}/*在代理類中調用目標類中的具體方法,動態的將代理動態對象,目標類中要調用的方法,及方法中的參數傳遞過來Method method 就是動態獲取的真正要執行的方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("之前開啟事務");method.invoke(object);System.out.println("之后提交事務");return proxy;}public Object getProxy(){return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);}}
Sell:
/*抽象操作定義 賣東西*/
public interface Sell {void sell();
}
Test:
public class Test {public static void main(String[] args) {CarFactoryImpl vip = new CarFactoryImpl();DynamicProxy dtproxy = new DynamicProxy(vip);//自己創建的代理類對象//這才是真正的創建動態代理對象 獲取目標類所實現的接口Sell carfactory = (Sell)dtproxy.getProxy();carfactory.sell();//使用代理對象調用接口中的方法,獲取當前調用的方法,最終調用invoke方法}
}
總結:動態代理對象需要繼承一個接口叫InvocationHandler,然后重寫invoke方法。先創建一個具體實現CarFactoryImpl讓vip進入到動態代理對象dtproxy中。vip在動態代理對象中可以利用反射機制調用各種不同的方法。但是這個方法名我們還需要用getProxy找到vip實現的接口,根據這個接口找到要完成的方法名,然后才可以使得carfactory.sell()進入到invoke中傳進來的method是sell。
2.cglib代理:
是spring中提供的一種代理技術,目標類可以不實現任何接口。采用字節碼生成子類的方式,對方法進行攔截,實現機制不同。
CarFactoryImpl:
//具體主題
public class CarFactoryImpl {public void sell() {System.out.println("汽車廠賣汽車");}}
CGLibProxy:
/** 動態代理類*/
public class CGLibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class<?> clazz){ enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } /** 攔截所有目標類方法的調用 * 參數: * obj 目標實例對象 * method 目標方法的反射對象 * args 方法的參數 * proxy 代理類的實例 */public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {//代理類調用父類的方法 System.out.println("開始事務"); Object obj1 = proxy.invokeSuper(obj, args); System.out.println("關閉事務"); return obj1; }
}
Test:
public class Test {public static void main(String[] args) {CGLibProxy proxy = new CGLibProxy();CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);carFactory.sell();}
}
4.5 模板方法模式
JdbcTemplate 執行sql時,步驟也是固定:1.鏈接數據庫。2.發送sql。 3.提交事務,關閉鏈接
模板方法模式,使一個在類中,定義好一個算法骨架,設定好實現步驟,把一些公共的通用的方法在父類中實現,然后一些不確定的實現在具體的子類中實現
結構:
? 抽象類:
? 模板方法:定義好執行順序的算法骨架,確定好執行流程順序
? 抽象方法:不確定的功能,定義為抽象的,交給子類實現
? 具體方法:都一樣的公共的通用的方法,在抽象父類中直接實現
? 具體子類:實現抽象類中的抽象方法的具體類,有不同的實現方式,就可以用多個子類
? new具體子類對象,用具體子類對象調用模板方法,把父類中具體方法與自己實現的抽象方法一起執行
適合流程相對固定,其中有變化的場景
AbstractBank:
public abstract class AbstractBank {//辦理業務方法 -- 模板方法public void handle(){this.offerNumber();this.lineup();this.business();this.score();}//抽號public void offerNumber(){System.out.println("抽號");}//排隊public void lineup(){System.out.println("排隊");}//辦理具體業務--抽象方法,由具體子類實現public abstract void business();//評分public void score(){System.out.println("評分");}
}
StoreBusiness:
/*存錢業務*/
public class StoreBusiness extends AbstractBank{//辦理的具體業務public void business() {System.out.println("我要存錢");}
}
TransferBusiness:
/*轉賬業務類*/
public class TransferBusiness extends AbstractBank{//轉賬public void business() {System.out.println("我要轉賬");}}
test:
public class Test {public static void main(String[] args) {StoreBusiness storeBusiness = new StoreBusiness();storeBusiness.handle();System.out.println("===================================");TransferBusiness transferBusiness = new TransferBusiness();transferBusiness.handle();}
}
由于銀行辦理業務這個模塊需要細分,具體辦理轉賬,存錢還是什么業務,所以在抽象類中不實現business()而是通過子類實現。存錢子類實現存錢功能,轉賬子類實現轉賬功能。handle是骨架,聲明了方法實現的順序
4.6 策略模式
將不同的實現算進行封裝,將功能的實現與使用相分離
在使用時,可以用不同的策略實現類進行替換,需要用到繼承多態
SalesMan:
//環境角色
public class SalesMan {//持有抽象策略角色的引用private Strategy strategy;public SalesMan(Strategy strategy) {this.strategy = strategy;}//向客戶展示促銷活動public void salesManShow(){strategy.show();}
}
Strategy:
public interface Strategy {void show();}
SA:
/*為春節準備的促銷活動A*/
public class StrategyA implements Strategy {public void show() {System.out.println("春節活動: 買一送一");}}
SB:
/*為中秋準備的促銷活動B*/
public class StrategyB implements Strategy {public void show() {System.out.println("中秋活動: 滿200元減50元");}}
SC:
/*為國慶準備的促銷活動C*/
public class StrategyC implements Strategy {public void show() {System.out.println("國慶活動:滿1000元加一元換購任意200元以下商品");}}
Test:
public class Test {public static void main(String[] args) {SalesMan salesManA = new SalesMan(new StrategyA());salesManA.salesManShow();SalesMan salesManB = new SalesMan(new StrategyB());salesManB.salesManShow();SalesMan salesManC = new SalesMan(new StrategyC());salesManC.salesManShow();}
}
通過SalesMan使用具體的實現方法,用SA的show方法就把SA傳進SalesMan中。