日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

JAVA进阶—注解,对象克隆,设计模式

發布時間:2024/1/8 asp.net 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA进阶—注解,对象克隆,设计模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

注解

什么是注解

內置注解

元注解

重點掌握

自定義注解

對象克隆

為什么要克隆?

如何實現克隆

解決多層克隆問題

Java 設計模式(java design patterns)

設計模式概念

建模語言

面向對象設計原則

java 設計模式類型

23 種設計模式介紹

常用設計模式

代理模式


注解

什么是注解

Java 注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制。

Java 語言中的類、方法、變量、參數和包等都可以被標注。Java 標注可以通過反射獲取標注內容。在編譯器生成類文件時,標注可以被嵌入到字節碼中。Java 虛擬機可以保留標注內容,在運行時可以獲取到標注內容 。當然它也支持自定義 Java 標注。

內置注解

Java 語言中已經定義好的注解

@Override

檢查該方法是否是重寫方法。如果發現其父類,或者是引用的接口中并沒有該方法時,會報編譯錯誤。

@Deprecated

標記過時方法。如果使用該方法,會報編譯警告。

@SuppressWarnings

指示編譯器去忽略注解中聲明的警告。

@FunctionalInterface

用于指示被修飾的接口是函數式接口。

元注解

元注解是 java API 提供的,是用于修飾注解的注解,通常用在注解的定義上:

@Retention

標識這個注解怎么保存,是只在代碼中,還是編入 class 文件中,或者是在運行時可以通過反射訪問。

@Documented

標記這些注解是否包含在用戶文檔中。

@Target

標記這個注解應該是哪種 Java 成員。

@Inherited

標記這個注解是繼承于哪個注解類(默認注解并沒有繼承于任何子類)。

@Repeatable

標識某注解可以在同一個聲明上使用多次。

重點掌握

@Target:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)

ElementType.TYPE 可以應用于類的任何元素。

ElementType.CONSTRUCTOR 可以應用于構造函數。

ElementType.FIELD 可以應用于字段或屬性。

ElementType.LOCAL_VARIABLE 可以應用于局部變量。

ElementType.METHOD 可以應用于方法級注釋。

ElementType.PACKAGE 可以應用于包聲明。

ElementType.PARAMETER 可以應用于方法的參數。

@Retention:@Retention 定義了該注解被保留的時間長短:某些注解僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在 class 文件中;編譯在 class文件中的注解可能會被虛擬機忽略,而另一些在 class 被裝載時將被讀取(請注意并不影響 class 的執行,因為注解與 class 在使用上是被分離的)。用于描述注解的生命周期(即:被描述的注解在什么范圍內有效)取值有:

1.SOURCE:在源文件中有效(即源文件保留)

2.CLASS:在 class 文件中有效(即 class 保留)

3.RUNTIME:在運行時有效(即運行時保留)

自定義注解

注解

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; ? ? @Target(ElementType.FIELD)//作用在屬性上 @Retention(RetentionPolicy.RUNTIME)//在運行時檢測 public @interface NotNull { ?String message() default ""; //注解屬性int length() default 0;String lengthmessage() default ""; }

使用注解的類

public class User {private int num; ?@NotNull(message="姓名不能為空",length=3,lengthmessage="長度不能小于3")private String name; ?public String getName() {return name;} ?/*** 是使用在文檔注釋中的標注,與@NotNull是不同的* @param name*/public void setName(String name) {this.name = name;} ?public int getNum() {return num;} ?public void setNum(int num) {this.num = num;} ?public static void main(String[] args) {new Date().getYear();ArrayList list = new ArrayList();} }

測試類

public class Test {//測試注解public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception {User user = new User();// user.setName("ji"); ?//通過反射機制獲取類的注解信息Field[] fields = user.getClass().getDeclaredFields();//獲取user類中所有的屬性for (Field field : fields) {NotNull notNull = field.getAnnotation(NotNull.class);//獲得此屬性上的 名為NotNull的注解標簽if (notNull != null) {//通過屬性名獲取找到屬性的get方法Method m = user.getClass().getMethod("get" + getMethodName(field.getName()));Object obj=m.invoke(user);//執行get方法 獲得屬性值if (obj==null) { //值為空 獲取注解信息System.err.println(field.getName() +notNull.message());throw new NullPointerException(notNull.message());}else{if(String.valueOf(obj).length()<(notNull.length())){System.err.println(field.getName() +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);} }

對象克隆

為什么要克隆?

直接 new 一個對象不行嗎?

new 出來的對象的屬性都還是初始化時候的值,所以當需要一個新的對象來保存當前對象的“狀態”就靠 clone 方法了。

誤區:

我們常見的

Student stu1 = new Student (); Student stu2 = stu1 ;

這種形式的代碼復制的是引用,即對象在內存中的地址,a 和 b 對象仍然指向了同一個對象。這種只能稱為引用復制,兩個引用指向的還是同一個對象。

?

如何實現克隆

先介紹一下兩種不同的克隆方法,淺克隆(ShallowClone)深克隆(DeepClone)

在 Java 語言中,數據類型分為值類型(基本數據類型)和引用類型,值類型包括 int、double、byte、boolean、char 等簡單數據類型,引用類型包括類、接口、數組等復雜類型。基本類型的值可以直接復制,引用類型只能復制引用地址。所以淺克隆和深克隆的主要區別在于是否支持引用類型的成員變量的復

淺克隆和深克隆

淺克隆

在淺克隆中,如果原型對象的成員變量是值類型,將復制一份給克隆對象;如果原型對象的成員變量是引用類型,則將引用對象的地址復制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內存地址。

簡單來說,在淺克隆中,當對象被復制時只復制它本身和其中包含的值類型的成員變量,而引用類型的成員變量并沒有復制。

?

實現方式:

1.在 Java 語言中,通過覆蓋 Object 類的 clone()方法可以實現淺克隆。

2.在 spring 框架中提供 BeanUtils.copyProperties(source,target);

深克隆

在深克隆中,無論原型對象的成員變量是值類型還是引用類型,都將復制一份給克隆對象,深克隆將原型對象的所有引用對象也復制一份給克隆對象。簡單來說,在深克隆中,除了對象本身被復制外,對象所包含的所有成員變量也將復制。

在 Java 語言中,如果需要實現深克隆,可以通過覆蓋 Object 類的 clone()方法實現,也可以通過序列化(Serialization)等方式來實現。

序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在于內存中。通過序列化實現的拷貝不僅可以復制對象本身,而且可以復制其引用的成員對象,因此通過序列化將對象寫到一個流中,再從流里將其讀出來,可以實現深克隆。需要注意的是能夠實現序列化的對象其類必須實現Serializable 接口,否則無法實現序列化操作。

?

解決多層克隆問題

如果引用類型里面還包含很多引用類型,或者內層引用類型的類里面又包含引用類型,使用 clone 方法就會很麻煩。這時我們可以用序列化的方式來實現對象的深克隆。

Java 設計模式(java design patterns)

設計模式概念

設計模式產生的背景

"設計模式"最初并不是出現在軟件設計中,而是被用于建筑領域的設計中。

1977 年美國著名建筑大師、加利福尼亞大學環境結構中心主任克里斯托夫·亞歷山大(Christopher Alexander)在他的著作《建筑模式語言:城鎮、建筑、構造》中描述了一些常見的建筑設計問題,并提出了 253 種關于對城鎮、鄰里、住宅、花園和房間等進行設計的基本模式。

1990 年軟件工程界開始研討設計模式的話題,后來召開了多次關于設計模式的研討會。直到 1995 年,艾瑞克·伽馬(ErichGamma)、理査德·海爾姆(Richard Helm)、拉爾夫·約翰森(Ralph Johnson)、約翰·威利斯迪斯(JohnVlissides)等 4 位作者合作出版了《設計模式:可復用面向對象軟件的基礎》一書,在此書中收錄了 23 個設計模式,這是設計模式領域里程碑的事件,導致了軟件設計模式的突破。這 4 位作者在軟件開發領域里也以他們的“四人組”(Gang of Four,GoF)著稱。

軟件設計模式的概念

軟件設計模式(Software Design Pattern),又稱設計模式,是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。它描述了在軟件設計過程中的一些不斷重復發生的問題,以及該問題的解決方案。也就是說,它是解決特定問題的一系列套路,是前輩們的代碼設計經驗的總結,具有一定的普遍性,可以反復使用。

為什么要學習設計模式

設計模式的本質是面向對象設計原則的實際運用,是對類的封裝性、繼承性和多態性以及類的關聯關系和組合關系的充分理解。

正確使用設計模式具有以下優點

可以提高程序員的思維能力、編程能力和設計能力。

使程序設計更加標準化、使軟件開發效率大大提高。

使設計的代碼可重用性高、可讀性強、可靠性高、靈活性好、可維護性強。

能夠更好的去理解源碼架構。

如果需要設計大型項目架構,我們必須考慮,當增加新的功能,代碼變動成本最低;新增加功能不對以前功能進行影響,如果沒有很好的設計,那么將會非常糟糕.

建模語言

統一建模語言(Unified Modeling Language,UML)是一種用于軟件系統分析和設計的語言工具,用于幫助軟件開發人員進行思考和記錄思路的結果。

UML 圖:通過不同的圖形和符號,來描述軟件模型及各個元素之間的關系。

類圖(Class diagram)是顯示了模型的靜態結構,特別是模型中存在的類、類的內部結構以及它們與其他類的關系等。類圖不顯示暫時性的信息。類圖是面向對象建模的主要組成部分。

在軟件工程中,類圖是一種靜態的結構圖,描述了系統的類的集合,類的屬性和類之間的關系,可以簡化了人們對系統的理解;類圖是系統分析和設計階段的重要產物,是系統編碼和測試的重要模型。

?

類是指具有相同屬性、方法和關系的對象的抽象,它封裝了數據和行為,是面向對象程序設計(OOP)的基礎,具有封裝性、繼承性和多態性等三大特性。在 UML 中,類使用包含類名、屬性和操作且帶有分隔線的矩形來表示。

(1) 類名(Name)是一個字符串,例如,Student。

(2) 屬性(Attribute)是指類的特性,即類的成員變量。UML 按以下格式表示:

[可見性]屬性名:類型[=默認值]

例如:-name:String

注意:“可見性”表示該屬性對類外的元素是否可見,包括公有(Public)、私有(Private)、受保護(Protected)和朋友(Friendly)4 種,在類圖中分別用符號+、-、#、~表示。

(3) 操作(Operations)是類的任意一個實例對象都可以使用的行為,是類的成員方法。UML 按以下格式表示:

[可見性]名稱(參數列表)[:返回類型]

例如:+display():void。

學生類的 UML 表示。

?

接口

接口(Interface)是一種特殊的類,它具有類的結構但不可被實例化,只可以被子類實現。它包含抽象操作,但不包含屬性。它描述了類或組件對外可見的動作。在 UML 中,接口使用一個帶有名稱的小圓圈來進行表示。圖形類接口的 UML 表示。

?

類之間的關系

在軟件系統中,類不是孤立存在的,類與類之間存在各種關系。根據類與類之間的耦合度從弱到強排列,UML 中的類圖有以下幾種關系:依賴關系、關聯關系、聚合關系、組合關系、泛化關系和實現關系。其中泛化和實現的耦合度相等,它們是最強的。

依賴關系

依賴關系是一種使用關系,它是對象之間耦合度最弱的一種關聯方式,是臨時性的關聯。在代碼中,某個類的方法通過局部變量、方法的參數或者對靜態方法的調用來訪問另一個類(被依賴類)中的某些方法來完成一些職責。

在 UML 類圖中,依賴關系使用帶箭頭的虛線來表示,箭頭從使用類指向被依賴的類。下圖所示是人與手機的關系圖,人通過手機的語音傳送方法打電話。

?

關聯關系

關聯關系是對象之間的一種引用關系,用于表示一類對象與另一類對象之間的聯系,如老師和學生、師傅和徒弟等。關聯關系是類與類之間最常用的一種關系,分為一般關聯關系、聚合關系和組合關系。我們先介紹一般關聯。

關聯又可以分為單向關聯,雙向關聯,自關聯。

單向關聯

?

在 UML 類圖中單向關聯用一個帶箭頭的實線表示。上圖表示每個顧客都有一個地址,這通過讓 Customer 類持有一個類型為 Address 的成員變量類實現。

雙向關聯

?

從上圖中我們很容易看出,所謂的雙向關聯就是雙方各自持有對方類型的成員變量。

在 UML 類圖中,雙向關聯用一個不帶箭頭的直線表示。上圖中在 Customer 類中維護一個 List,表示一個顧客可以購買多個商品;在 Product 類中維護一個 Customer 類型的成員變量表示這個產品被哪個顧客所購買。

自關聯

?

自關聯在 UML 類圖中用一個帶有箭頭且指向自身的線表示。上圖的意思就是Node 類包含類型為 Node 的成員變量,也就是“自己包含自己”。

聚合關系

聚合關系是關聯關系的一種,是強關聯關系,是整體和部分之間的關系。

聚合關系也是通過成員對象來實現的,其中成員對象是整體對象的一部分,但是成員對象可以脫離整體對象而獨立存在。例如,學校與老師的關系,學校包含老師,但如果學校停辦了,老師依然存在。

在 UML 類圖中,聚合關系可以用帶空心菱形的實線來表示,菱形指向整體。下圖所示是大學和教師的關系圖:

?

組合關系

組合表示類之間的整體與部分的關系,但它是一種更強烈的聚合關系

在組合關系中,整體對象可以控制部分對象的生命周期,一旦整體對象不存在,部分對象也將不存在,部分對象不能脫離整體對象而存在。例如,頭和嘴的關系,沒有了頭,嘴也就不存在了。

在 UML 類圖中,組合關系用帶實心菱形的實線來表示,菱形指向整體。下圖所示是頭和嘴的關系圖:

?

繼承關系

繼承關系是對象之間耦合度最大的一種關系,表示一般與特殊的關系,是父類與子類之間的關系,是一種繼承關系,是 is-a 的關系。

在 UML 類圖中,繼承關系用帶空心三角箭頭的實線來表示,箭頭從子類指向父類。在代碼實現時,使用面向對象的繼承機制來實現繼承關系。例如,Student 類和 Teacher 類都是 Person 類的子類,其類圖下圖所示。

?

實現關系

實現關系是接口與實現類之間的關系。在這種關系中,類實現了接口,類中的操作實現了接口中所聲明的所有的抽象操作。

在 UML 類圖中,實現關系使用帶空心三角箭頭的虛線來表示,箭頭從實現類指向接口。例如,汽車和船實現了交通工具,其類圖如下圖所示。

?

面向對象設計原則

在面向對象的設計過程中,首先需要考慮的是如何同時提高一個軟件系統的可維護性和可復用性。這時,遵從面向對象的設計原則,可以在進行設計方案時減少錯誤設計的產生,從不同的角度提升一個軟件結構的設計水平。

單一職責

單一職責原則是最簡單的面向對象設計原則,它用于控制類的粒度大小。

對于單一職責原則,可以理解為一個類只負責一個功能領域中的相應職責,即一個類不要負責太多“雜亂”的工作。

在軟件系統中,如果一個類承擔的職責過多,就等于把這些職責耦合在一起,一個職責的變化可能會削弱或抑制這個類完成其他職責的能力。這種耦合會導致脆弱的設計,當變化發生時設計或遭受到意想不到的破壞。

以項目開發為例,如果項目組成員每個人的職責都很明確,可以專心開發自己負責的模塊,則項目成果的質量往往很高。相反,如果職責不清晰,分工就會混亂。

優點:

低耦合

高內聚

開閉原則

開閉原則即對擴展開放,對修改封閉。在軟件系統開發過程中,軟件的需求往往會隨著時間的推移而發生變化。因此,進行軟件設計時需要考慮怎樣的設計才能面對需求的改變卻可以相對保持穩定,從而使得系統可以在第一個版本以后不斷推出新的版本,這時便可以以開閉原則作為指導。

為了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。在進行軟件設計時,一般先評估出最有可能發生變化的類,然后構造抽象來隔離那些變化。當變化發生時,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,是現在不修改已有代碼的基礎上擴展系統的功能,達到開閉原則的要求。

?

優點:

適應性和靈活性

穩定性和延續性

可復用性與可維護性

里氏替換原則

再說繼承

繼承優勢

提高代碼的復用性(每個子類有擁有父類的屬性和方法)

提高代碼的可擴展性

繼承劣勢

繼承是侵入性的(只要繼承,就必須擁有父類的屬性和方法,體系結構復雜)

繼承機制很大的增加了耦合性(父類被子類繼承,父類功能修改會影響子類)

里氏替換原則的提出

1987 年由麻省理工學院計算機科學實驗室的里斯科夫(Liskov)女士在“面向對象技術的高峰會議”上發表的一篇文章《數據抽象和層次》里提出來的,她提出:繼承必須確保超類所擁有的性質在子類中仍然成立

里氏替換原則主要闡述了有關繼承的一些原則,也就是什么時候應該使用繼承,什么時候不應該使用繼承。里氏替換原是繼承復用的基礎,它反映了基類與子類之間的關系,是對開閉原則的補充,是對實現抽象化的具體步驟的規范。

里氏替換原則的定義

所有使用父類的地方必須能透明地使用其子類的對象。

里氏替換原則表明,在軟件中將一個基類對象替換成它的子類對象時程序將不會產生任何錯誤和異常。

通俗地說,對于里氏替換原則,我們可作如下表述:任何基類可以出現的地方,子類一定可以出現。所以,子類可以擴展父類的功能,但不能改變父類原有的功能。換句話說,子類繼承父類時除添加新的方法完成新增功能外盡量不要重寫父類的方法。

里氏替換原則的主要作用

里氏替換原則是實現開閉原則的重要方式之一

它克服了繼承中重寫父類方法造成的可復用性變差的缺點

它是功能正確性的保證

加強程序的健壯性,提高程序的維護性降低需求變更時引入的風險

依賴倒置

上層模塊不應該依賴底層模塊,它們都應該依賴于抽象

抽象不應該依賴于細節,細節應該依賴于抽象

是程序要依賴于抽象接口,不要依賴于具體實現。簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。也就是針對抽象層編程,面向接口編程。

?

接口隔離

使用多個接口,而不使用單一的總接口,不強迫新功能實現不需要的方法。

?

迪米特原則

迪米特原則是1987年秋天在美國東北大學一個叫做迪米特的項目設計提出的,它要求一個對象應該對其他對象有最少的了解,所以迪米特法則又叫做最少知識原則。

只和你的直接朋友交談,不跟“陌生人”說話(Talk only to your immediate friends and not to strangers)。

直接朋友:

  • 類中的成員屬性

  • 在類中的方法作為參數使用

  • 在類中的方法作為返回值類型

  • 注意事項:

    迪米特法則的核心是降低類之間的耦合

    從被依賴者的角度來說,盡量將邏輯封裝在類的內部,對外除了提供的 public 方法,不泄露任何信息

    從依賴者的角度來說,只依賴應該依賴的對象

    切忌不要為了用而用

    組合/聚合復用原則

    優先使用組合,使系統更靈話,其次才考慮繼承,達到復用的目的。

    一般而言,如果兩個類之間是"Has-A"關系應使用組合或聚合,如果是"Is-A"關系可使用繼承。

    總結

    開閉原則:要求對擴展開放,對修改關閉

    里氏替換原則:不要破壞繼承體系

    依賴倒置原則:要求面向接口編程

    單一職責原則:實現類職責要單一

    接口隔離原則:在設計接口的時候要精簡單一

    迪米特法則:只與直接的朋友的通信

    合成復用原則:盡量使用聚合和組合的方式,而不是使用繼承

    設計原則的核心思想

    找出應用中可能需要變化之處,獨立出來,不要和不需要變化的代碼混在一 起

    針對接口編程,而壞是針對實現編程

    為了交互對象的松耦合設計而努力

    遵循設計原則:就是為了讓程序高內聚,低耦合

    java 設計模式類型

    據模式是用來完成什么工作來劃分,這種方式可分為創建型模式、結構型 模式和行為型模式 3 種。

    創建型模式:用于描述“怎樣創建對象”,它的主要特點是“將對象的創建與使用分離”。提供了單例、原型、工廠方法、抽象工廠、建造者 5 種創建型模式。

    結構型模式:用于描述如何將類或對象按某種布局組成更大的結構,提供了代理、 適配器、橋接、裝飾、外觀、享元、組合 7 種結構型模式。

    行為型模式:用于描述類或對象之間怎樣相互協作共同完成單個對象都無法單獨 完成的任務,以及怎樣分配職責。提供了模板方法、策略、命令、職責鏈、狀態、 觀察者、中介者、迭代器、訪問者、備忘錄、解釋器 11 種行為型模式。

    23 種設計模式介紹

    設計模式 | 菜鳥教程

  • 單例(Singleton)模式:某個類只能生成一個實例,該類提供了一個全局訪問點供外部獲取該實例,其拓展是有限多例模式。

  • 原型(Prototype)模式:將一個對象作為原型,通過對其進行復制而克隆出多個和原型類似的新實例。

  • 工廠方法(Factory Method)模式:定義一個用于創建產品的接口,由子類決定生產什么產品。

  • 抽象工廠(AbstractFactory)模式:提供一個創建產品族的接口,其每個子類可以生產一系列相關的產品。

  • 建造者(Builder)模式:將一個復雜對象分解成多個相對簡單的部分,然后根據不同需要分別創建它們,最后構建成該復雜對象。

  • 代理(Proxy)模式:為某對象提供一種代理以控制對該對象的訪問。即客戶端通過代理間接地訪問該對象,從而限制、增強或修改該對象的一些特性。

  • 適配器(Adapter)模式:將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。

  • 橋接(Bridge)模式:將抽象與實現分離,使它們可以獨立變化。它是用組合關系代替繼承關系來實現,從而降低了抽象和實現這兩個可變維度的耦合度。

  • 裝飾(Decorator)模式:動態的給對象增加一些職責,即增加其額外的功能。

  • 外觀(Facade)模式:為多個復雜的子系統提供一個一致的接口,使這些子系統更加容易被訪問。

  • 享元(Flyweight)模式:運用共享技術來有效地支持大量細粒度對象的復用。

  • 組合(Composite)模式:將對象組合成樹狀層次結構,使用戶對單個對象和組合對象具有一致的訪問性。

  • 模板方法(TemplateMethod)模式:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。

  • 策略(Strategy)模式:定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的改變不會影響使用算法的客戶。

  • 命令(Command)模式:將一個請求封裝為一個對象,使發出請求的責任和執行請求的責任分割開。

  • 職責鏈(Chain of Responsibility)模式:把請求從鏈中的一個對象傳到下一個對象,直到請求被響應為止。通過這種方式去除對象之間的耦合。

  • 狀態(State)模式:允許一個對象在其內部狀態發生改變時改變其行為能力。

  • 觀察者(Observer)模式:多個對象間存在一對多關系,當一個對象發生改變時,把這種改變通知給其他多個對象,從而影響其他對象的行為。

  • 中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的交互關系,降低系統中對象間的耦合度,使原有對象之間不必相互了解。

  • 迭代器(Iterator)模式:提供一種方法來順序訪問聚合對象中的一系列數據,而不暴露聚合對象的內部表示。

  • 訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者對象訪問。

  • 備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取并保存一個對象的內部狀態,以便以后恢復它。

  • 解釋器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。

  • 常用設計模式

    單例模式

    在有些系統中,為了節省內存資源、保證數據內容的一致性,對某些類要求只能創建一個實例,這就是所謂的單例模式. 例如,Windows 中只能打開一個任務管理器,這樣可以避免因打開多個任務管理器窗口而造成內存資源的浪費,或出現各個窗口顯示內容的不一致等錯誤。

    特點

  • 單例類只有一個實例對象

  • 該單例對象必須由單例類自行創建

  • 單例類對外提供一個訪問該單例的全局訪問點

  • 結構

    單例類:包含一個實例且能自行創建這個實例的類。 訪問類:使用單例的類。其結構如圖所示。

    ?

    通常兩種實現

    第 1 種:懶漢式單例 該模式的特點是類加載時沒有生成單例對象,只有當第一次調用 getlnstance方法時才去創建這個單例。這種寫法會存在線程安全問題

    public class Singleton {public static volatile Singleton singleton; ?private Singleton(){ ?} ?public static Singleton creatSingleton(){if (singleton == null){synchronized (Singleton.class){if (singleton == null){singleton = new Singleton();}}}return singleton;} }

    第 2 種:餓漢式單例 該模式的特點是類一旦加載就創建一個單例,保證在調用 getInstance 方法之前單例已經存在了,這種寫法在加載中創建,不會出現線程安全問題

    /** 餓漢式單例* 一般又稱為急切式單例* 在類加載時,就會創建此單例對象,這種寫法不會出現線程安全問題*/ public class Singleton {//創建 Singleton 的一個對象private static Singleton instance = new Singleton();//讓構造函數為 privateprivate Singleton(){}//獲取唯一可用的對象public static Singleton getInstance(){return instance;} }

    懶漢式單例雙重檢索+volatile

    idea 安裝 IDEA 字節碼查看插件 "jclasslib Bytecode Viewer" 選中編譯后的 class 文件

    ?

    ?

    源碼

    public class D {int i = 0;public void test(){}public static void main(String[] args) {D d = new D();} }

    編譯后的匯編指令碼

    正常執行順序

    0 new #3 //new 申請內存空間

    3 dup

    4 invokespecial #4 : ()V> //調用構造方法

    7 astore_1 //將對象地址賦給引用變量

    8 return

    線程 1 開始執行,先執行 new,在內存中申請內存空間

    ?

    此時指令可能發生重排序,先把半成品對象引用地址賦給引用變量 t

    ?

    線程 1暫停執行,線程 2進入到 cpu執行,引用變量t 不為空,指向的是半成品對象

    ?

    Runtime 類

    Jdk 中的源碼 Runtime 類就是一個單例類,利用 Runtime 類可以啟動新的進程或進行相關運行時環境的操作。比如,取得內存空間以及釋放垃圾空間。Runtime 類屬于典型的單例設計。

    工廠模式(Factory Pattern)

    簡單工廠模式

    簡單工廠模式并不是 23 種設計模式之一,因為它并不符合開閉原則。主要目的是為了引出工廠方法模式,適合產品子類比較少的、創建操作比較簡單的情況。

    ?

    由一個工廠類根據傳入的參數(一般是字符串參數),動態決定應該創建哪一個產品子類的實例,并以父類形式返回。

    該模式中包含的角色及其職責:

    工廠角色:簡單工廠模式的核心,它負責實現創建所有實例的內部邏輯。工廠類 提供靜態方法,可以被外界直接調用,創建所需的產品對象。

    /** 工廠,負責生產對象*/ public class SimpleFactory { ?//工廠中負責制造對象的方法public Product createProduct(String className){if(className == null){return null;}else{try {return (Product) Class.forName(className).newInstance();//反射機制} catch (InstantiationException e) {e.printStackTrace();return null;} catch (IllegalAccessException e) {e.printStackTrace();return null;} catch (ClassNotFoundException e) {e.printStackTrace();return null;}} ? ? ?}}

    抽象產品角色:簡單工廠模式所創建的所有對象的父類,描述所有實例共有的接 口。可以是抽象類或接口。

    //抽象產品 public interface Product { ?void show(); ? }

    具體產品角色:是簡單工廠模式的創建目標。

    //具體產品1 public class ProductA implements Product { ?@Overridepublic void show() {System.out.println("具體產品1顯示...");} ? } //具體產品2 public class ProductB implements Product { ?@Overridepublic void show() {System.out.println("具體產品2顯示...");} ? }

    優點

    客戶端不負責對象的創建,而是由專門的工廠類完成;客戶端只負責對象的 調用,實現了創建和調用的分離,降低了客戶端代碼的難度;

    缺點

    如果增加和減少產品子類,需要修改簡單工廠類,違背了開閉原則如果產品 子類過多,會導致工廠類非常的龐大,違反了高內聚原則,不利于后期維護.

    適用場景

    所有的產品子類都有同一個父類(或接口),屬于同一個產品系列產品子類 比較少的、創建操作比較簡單.

    工廠方法模式

    與簡單工廠模式不同,工廠方法模式的對工廠也進行了抽象。有一個抽象的Factory 類(可以是抽象類和接口),這個類將不在負責具體的產品生產,而是只制定一些規范,將實際創建工作推遲到子類去完成。

    在簡單工廠模式中,有個全能的園丁,控制所有作物的種植、生長和收獲。現在農場規模變大了,管理更加專業化了。過去全能的園丁沒有了,每一種作物都有專門的園丁管理,形成了規模化和專業化生產。

    ?

    基本原理

    在這個模式中,工廠類和產品類往往可以——對應。即一個抽象工廠對應 一個抽象產品,一個具體工廠對應一個具體產品,這個具體的工廠就負責生產對 應的產品。 該模式中包含的角色及其職責.

    抽象工廠角色:工廠方法模式的核心,與應用程序無關。任何在模式中創建的對 象的工廠類必須實現這個接口。

    具體工廠角色:這是實現抽象工廠接口的具體工廠類,包含與應用程序密切相關 的邏輯,并且受到應用程序調用以創建產品對象。

    抽象產品角色:工廠方法模式所創建的對象的父類型,也就是產品對象的共同父 類或共同擁有的接口。

    具體產品角色:這個角色實現了抽象產品角色所定義的接口。某具體產品有專門 的具體工廠創建,它們之間往往——對應。

    優點:

    客戶端不負責對象的創建,而是由專門的工廠類完成;客戶端只負責對象的調用, 實現了創建和調用的分離,降低了客戶端代碼的難度;若增加和減少產品子類,不需修改工廠類,只增加產品子類和工廠子類,符合開閉原則即使產品子類過多, 不會導致工廠類的龐大,利于后期維護

    ?

    缺點:

    需要額外的編寫代碼,增加了工作量

    適用場景:

    所有的產品子類都有同一個父類(或接口),屬于同一個產品系列產品子類 比較多的、創建操作比較復雜

    抽象工廠模式

    簡單工廠模式:一個工廠類負責同一類所有產品對象的創建,如果產品較多,職責大大增加。增加或減少產品時,需要修改工廠類,違背了開閉原則。

    工廠方法模式:一個具體的工廠類負責創建一個單獨的產品,如果產品增加或減少,一不需要修改工廠類,符合開閉原則。但是即使這些產品是有聯系的,也必須由不同的工廠分別創建

    基本原理

    抽象工廠模式中,一個具體的工廠負責創建一系列相互關聯的產品。會簡化客戶端的調一用。并且更換產品系列非常方便,更換一個工廠類即可。

    ?

    該模式中包含的角色及其職責: 抽象工廠、具體工廠、抽象產品、具體產品。

    優點:獲取具體系列產品只需要通過具體系列工廠獲取,無序關心創建的細節

    原型模式

    有時候,我們需要多個實例,但是創建這個實例的過程比較復雜,比如構造函數非常的復雜,執行這個構造函數時會消耗較長的時間,但另外一方面,這個構造函數中的一些信息又沒有什么變化(也就是說創建第一個實例時初始化信息是這樣的,創建第二個實例時初始化信息還是還是這樣的),那么直接使用 new 再創建這樣一個實例就顯得太昂貴了,此時可以使用克隆,也就是復制,就是通過復制現在已經有了的實例來創建新的實例,提高創建速度.

    生活案例:同一個簡歷模板打印了 5 分,要求同一個面試者進行填寫,每份簡歷內容相同。面試者寫了第一份后(多么復雜的一個創建對象的過程),還需要再重復的寫四份嗎?那太費時間了。直接找個復印機復印四份不就可以了。原型模式就是這個道理.

    ?

    代理模式

    當你需要買東西的時候,通常都不是直接去買的,而是通過中間商代理來買,例如購買火車票通過12306來買

    優點

    代理模式在客戶端與目標對象之間起到一個中介作用和保護目標對象的作用

    代理對象可以擴展目標對象的功能

    代理模式能將客戶端與目標對象分離,在一定程度上降低了系統的耦合度

    結構

    抽象主題類:類通過接口或抽象類聲明真實主題和代理對象實現的業務方法。

    真實主題類:實現了抽象主題中的具體業務,是代理對象所代表的二等真實對象,是最終要引用的對象

    代理類:提供了與真實主題相同的接口,其內部有對真實主題的引用,它可以訪問、控制或擴展真實主題的功能。

    ?

    代理實現可以分為靜態代理和動態代理

    靜態代理

    靜態代理模式的特點,代理類接受一個 Subject 接口的對象,任何實現該接 口的對象,都可以通過代理類進行代理,增加了通用性。

    優點:可以做到在符合開閉原則的情況下對目標對象進行功能擴展。

    缺點:一個代理類只能代理一個接口,工作量太大;代理類是運行前編碼已 經完成的;必須先有接口,再有代理;接口一旦發生變量,代理類也要修改

    動態代理

    在動態代理中我們不再需要再手動的創建代理類,我們只需要編寫一個動態 處理器就可以了。真正的代理對象在運行時為我們動態的來創建。

    動態代理分為 jdk 動態代理和 cglib 動態代理

    jdk 代理

    動態代理是實現方式,是通過反射來實現的,借助 Java 自帶的java.lang.reflect.Proxy,通過固定的規則生成。

    編寫一個委托類的接口,即靜態代理的

    /*Dao接口,定義保存功能*/ public interface BaseDao {void save();}

    實現一個真正的委托類,即靜態代理的

    /*實際功能實現類*/ public class UserDaoImpl implements BaseDao { ?@Overridepublic void save() {System.out.println("UserDaoImpl:save()");} ? }

    創建一個動態代理類,實現 InvocationHandler 接口,并重寫該 invoke 方法

    /** 動態代理類* ? jdk代理 底層實現使用反射機制*/ public class DynamicDaoProxy implements InvocationHandler { ?// 被代理類的實例 目標private Object object;// BaseDao ---> Object(任意的) ?// 將被代理者的實例傳進動態代理類的構造函數中public DynamicDaoProxy(Object object) {this.object = object;} ?/** 覆蓋InvocationHandler接口中的invoke()方法* ? Object proxy 表示代理對象* ? Method method 代理對象中的方法* Object[] args 表示代理方法中的參數* 更重要的是,動態代理模式可以使得我們在不改變原來已有的代碼結構* 的情況下,對原來的“真實方法”進行擴展、增強其功能,并且可以達到* 控制被代理對象的行為,下面的before、after就是我們可以進行特殊* 代碼切入的擴展點了。*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before");Object result = method.invoke(object, args);//用反射機制獲取到目標對象中的方法System.out.println("after");return result;} }

    在測試類中,生成動態代理的對象。

    public class Test { ?public static void main(String[] args) {//我們要代理的真實對象 ? Service層是需要添加事務UserDaoImpl userDaoImpl = new UserDaoImpl();//我們要代理哪個真實對象,就將該對象傳進去,最后是通過該真實對象來調用其方法的InvocationHandler dynamicProxy = new DynamicDaoProxy(userDaoImpl);/** 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數* 第一個參數 handler.getClass().getClassLoader() ,我們這里使用handler這個類的ClassLoader對象來加載我們的代理對象* 第二個參數realSubject.getClass().getInterfaces(),我們這里為代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了* 第三個參數dynamicProxy, 我們這里將這個代理對象關聯到了上方的 InvocationHandler 這個對象上*///動態生成的代理對象BaseDao baseDao = (BaseDao) Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(), userDaoImpl.getClass().getInterfaces(), dynamicProxy);// mybatis 接口代理 訪問 接口中的方法baseDao.save();} }

    jdk 動態代理總結:

    雖然相對于靜態代理,動態代理大大減少了我們的開發任務,同時減少了 對業務接口的依賴,降低了耦合度。

    但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持 interface 代理的桎梏,因為它的設計注定了這個遺憾。

    Cglib 代理

    JDK 實現動態代理需要實現類通過接口定義業務方法,對于沒有接口的類,如何實現動態代理呢,這就需要 CGLib 了。

    CGLIB(Code Generator Library)是一個強大的、高性能的代碼生成庫。CGLib 采用了非常底層的字節碼技術,其原理是通過字節碼技術為一個類創建子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。

    CGLIB 相比于 JDK 動態代理更加強大,JDK 動態代理雖然簡單易用,但是其有一個致命缺陷是,只能對接口進行代理。如果要代理的類為一個普通類、沒有接口,那么 Java 動態代理就無法使用了。

    Cglib 子類代理實現方法:

    1.需要引入 cglib 的 jar 文件,但是 Spring 的核心包中已經包括了 Cglib 功能,所以直接引入 spring-core-xxx.jar 即可.

    2.引入功能包后,就可以在內存中動態構建子類

    3.代理的類不能為 final,否則報錯

    4.目標對象的方法如果為 final/static,那么就不會被攔截,即不會執行目標對象額外的業務方法.

    CGLIB 創建的動態代理對象比JDK 創建的動態代理對象的性能更高,但是 CGLIB創建代理對象時所花費的時間卻比 JDK 多得多。所以對于單例的對象,因為無需頻繁創建對象,用 CGLIB 合適,反之使用 JDK 方式要更為合適一些。同時由于 CGLib 由于是采用動態創建子類的方法,對于 final 修飾的方法無法進行代理。

    總結

    以上是生活随笔為你收集整理的JAVA进阶—注解,对象克隆,设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。