Java進階(注解,設計模式,對象克隆)

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();
創建對象這一條語句編譯為指令時,可以分為三個指令

  1. new 申請空間
  2. 調用構造方法初始化對象
  3. 把對象地址 賦給引用變量
    如果按照這個正常的順序執行,是沒有問題的,
    但是執行時,如果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 代理模式

在不修改原來代碼的前提下,為我們方法添加額外的功能。通過代理對象幫助我們進行調用。

有些時候,目標對象(比如汽車廠)不想或不能直接與客戶打交道,通過代理對象進行訪問,代理對象可以保護目標對象,對目標對象功能進行擴展,降低了模塊之間的耦合度。

涉及到三個主題:

  1. 抽象主題:抽取的功能,讓目標對象進行實現,以及代理對象進行實現
  2. 具體主題:真正要實現功能的類
  3. 代理對象

代理模式實現方式又有兩種:

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 動態代理:
  1. 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中。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62264.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62264.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62264.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

部署loki,grafana 以及springcloud用法舉例

文章目錄 場景docker 部署grafanadocker-compose部署loki維護配置文件 local-config.yaml維護docker-compose.yml配置啟動 grafana 添加loki數據源springcloud用法舉例查看loki的explore,查看日志 場景 小公司缺少運維崗位&#xff0c;需要研發自己部署日志系統&#xff0c;elk…

keil報錯---connection refused due to device mismatch

解決辦法如下&#xff1a; 記得改成1 把Enable取消

第三節、電機定速轉動【51單片機-TB6600驅動器-步進電機教程】

摘要&#xff1a;本節介紹用定時器定時的方式&#xff0c;精準控制脈沖時間&#xff0c;從而控制步進電機速度 一、計算過程 1.1 電機每一步的角速度等于走這一步所花費的時間&#xff0c;走一步角度等于步距角&#xff0c;走一步的時間等于一個脈沖的時間 w s t e p t … ……

vue中pdf.js的使用,包括pdf顯示,跳轉指定頁面,高亮關鍵詞

目錄 一、下載pdf.js 二、引入到本地的項目中 三、實現預覽pdf 四、跳轉到指定頁面 五、利用pdf里面的find查找關鍵詞 六、修改頁面大小為實際大小 一、下載pdf.js https://github.com/mozilla/pdf.js 里面有很多的版本&#xff0c; 高版本的可能瀏覽器不兼容或者還要考…

OD B卷【連續字母長度】

題目 給定一個字符串&#xff0c;只包含大寫字母&#xff0c;求在包含同一字母的子串中&#xff0c;長度第k長的子串的長度&#xff0c;相同字母只取最長的那個子串。 輸入描述&#xff1a; 第一行輸入一個子串&#xff08;長【1,100】&#xff09;&#xff0c;只包含大寫字母…

python中的 Pydantic 框架介紹

Pydantic 框架介紹 Pydantic 是一個用于數據驗證和設置管理的 Python 庫。它主要通過數據模型類的定義來處理 JSON 數據、解析請求和響應數據&#xff0c;并提供自動化的驗證和轉換。Pydantic 主要用于處理 Python 類型的安全性和驗證&#xff0c;尤其在 FastAPI 等現代 Pytho…

橋接模式和組合模式的區別

橋接模式&#xff08;Bridge Pattern&#xff09;和組合模式&#xff08;Composite Pattern&#xff09;都是結構型設計模式&#xff0c;旨在解決對象結構的復雜性問題&#xff0c;但它們的應用場景和目的有所不同。以下是它們的區別&#xff1a; 1. 定義與目的 橋接模式&…

Qt 小項目 學生管理信息系統

主要是對數據庫的增刪查改的操作 登錄/注冊界面&#xff1a; 主頁面&#xff1a; 添加信息&#xff1a; 刪除信息&#xff1a; 刪除第一行&#xff08;支持多行刪除&#xff09; 需求分析&#xff1a; 用QT實現一個學生管理信息系統&#xff0c;數據庫為MySQL 要求&#xf…

14.數據容器-set集合

特點 無序的&#xff0c;元素不重復&#xff0c;自帶去重功能。 可以容納不同類型的元素數據。 # 定義一個空set my_set {} your_set set() my_set {aa, bb, bb, aa} # {aa, bb} print(my_set) 因為set集合是無序的&#xff0c;所以集合不支持下標索引訪問。所以set集合…

“量子躍遷與數據織網:深入探索K最近鄰算法在高維空間中的優化路徑、神經網絡融合技術及未來機器學習生態系統的構建“

&#x1f3bc;個人主頁&#xff1a;【Y小夜】 &#x1f60e;作者簡介&#xff1a;一位雙非學校的大二學生&#xff0c;編程愛好者&#xff0c; 專注于基礎和實戰分享&#xff0c;歡迎私信咨詢&#xff01; &#x1f386;入門專欄&#xff1a;&#x1f387;【MySQL&#xff0…

硬件選型規則

光源選型: 先用型號中帶H的&#xff0c;沒有的選標準的. 光源和光源控制器的搭配需要確保接口一致。 根據型號表中的最佳工作距離和相機的尺寸。 光源控制器選型&#xff1a; 首先選擇海康風格系列光源控制器考慮與光源的接口匹配。功率應該滿足接近光源功率。檢查是否退市…

【QNX+Android虛擬化方案】135 - QNX側如何Dump 88Q5152 MIBS報文計數

【QNX+Android虛擬化方案】135 - QNX側如何Dump 88Q5152 MIBS報文計數 一、讀取 88Q5152 MIBS 計數二、讀取 88Q5152 WDT 相關寄存器基于原生純凈代碼,自學總結 純技術分享,不會也不敢涉項目、不泄密、不傳播代碼文檔!!! 本文禁止轉載分享 !!! 匯總鏈接:《【QNX+Andro…

C#核心(15)繼承中的構造函數

前言 我們之前學過構造函數是什么東西&#xff0c;今天的內容也和構造函數緊密相關&#xff0c;一個繼承了父親的子類里面構造函數的規則是什么樣的&#xff0c;今天內容很簡單&#xff0c;請聽我慢慢講來。 基本概念 特點&#xff1a;當申明一個子類時&#xff0c;先執行父…

TVbox源貢獻指南

歡迎各路大佬踴躍提PR&#xff0c;分享爬蟲代碼。 源碼倉庫地址 https://github.com/lushunming/AndroidCatVodSpider 快速開始 本工程是一個完整的AndroidStudio工程&#xff0c;請你用AS打開編輯。 工程調試完畢后要需要導出生成jar文件配合軟件使用&#xff0c;執行根目…

FastAPI快速入門

文章目錄 了解FastAPI程序結構第一步&#xff0c;導入FastAPI第二步&#xff0c;創建一個app實例第三步&#xff0c;編寫一個 路徑操作裝飾器第五步、運行開發服務器uvicorn main:app --reload即可訪問api鏈接。符案例 聲明路徑參數聲明路徑參數的類型get請求查詢參數請求體如何…

云計算.運維.面試題

1、計算機能直接識別的語言( C )。 A、匯編語言 B、自然語言 C、機器語言 D、高級語言 2、應用軟件是指( D )。 A、所有能夠使用的軟件 B、能被各應用單位共同使用的某種軟件 C、所有計算機上都應使用的基本軟件D、專門為某一應用目的而編制的軟件 3、計算機的顯示器是一…

如何優雅地實現單例模式?內部靜態類還是雙重檢查鎖定?

在最近的一個項目中&#xff0c;我需要為一個核心配置類實現單例模式。在設計過程中&#xff0c;我發現要同時滿足延遲加載和線程安全這兩個要求&#xff0c;常見的實現方式有兩種&#xff1a;內部靜態類和雙重檢查鎖定&#xff08;Double-Checked Locking, DCL&#xff09;。 …

【計算機網絡】 —— 數據鏈路層(壹)

文章目錄 前言 一、概述 1. 基本概念 2. 數據鏈路層的三個主要問題 二、封裝成幀 1. 概念 2. 幀頭、幀尾的作用 3. 透明傳輸 4. 提高效率 三、差錯檢測 1. 概念 2. 奇偶校驗 3. 循環冗余校驗CRC 1. 步驟 2. 生成多項式 3. 例題 4. 總結 四、可靠傳輸 1. 基本…

golang實現簡單的redis服務

golang 手搓redis服務器倉庫地址:實現思路: golang 手搓redis服務器 倉庫地址: 倉庫: https://github.com/dengjiayue/my-redis.git 實現思路: ● 協議: tcp通信 ● 數據包: 長度(4byte)方法(1byte)數據json ● 數據處理: 單線程map讀寫 ○ 依次處理待處理隊列的請求(chan)…

智慧銀行反欺詐大數據管控平臺方案(八)

智慧銀行反欺詐大數據管控平臺的核心理念&#xff0c;在于通過整合先進的大數據技術、算法模型和人工智能技術&#xff0c;構建一個全面、智能、動態的反欺詐管理框架&#xff0c;以實現對金融交易的全方位監控、欺詐行為的精準識別和高效處理。這一理念強調數據驅動決策&#…