面向?qū)ο蟪绦蛟O(shè)計(jì)的基本概念
- 面向過(guò)程與面向?qū)ο蟮膮^(qū)分
- 傳統(tǒng)的程序設(shè)計(jì)使用的是莺丑,結(jié)構(gòu)化程序設(shè)計(jì)方法谴返,通過(guò)設(shè)計(jì)一系列的過(guò)程(算法)來(lái)求解問(wèn)題。也即命贴,在面向過(guò)程的程序設(shè)計(jì)中道宅,首先確定如何操作數(shù)據(jù)(設(shè)計(jì)算法),然后確定如何組織數(shù)據(jù)以便于數(shù)據(jù)操作(設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu))胸蛛。
- 面向?qū)ο蟮某绦蛟O(shè)計(jì)中污茵,首先確定如何組織數(shù)據(jù)(設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)),而后再?zèng)Q定如何操作數(shù)據(jù)(設(shè)計(jì)各種方法)
- 面向?qū)ο蠛兔嫦蜻^(guò)程的不同適用范圍
- 對(duì)于一些小規(guī)模的問(wèn)題葬项,將其分解為過(guò)程的開(kāi)發(fā)方式比較理想泞当;
- 面向?qū)ο蟮姆绞礁m合解決規(guī)模較大的問(wèn)題。
- 類(lèi)
- 類(lèi)可以看作構(gòu)造對(duì)象的模板民珍。按照類(lèi)的定義襟士,由類(lèi)構(gòu)造對(duì)象的過(guò)程稱(chēng)為創(chuàng)建類(lèi)的實(shí)例。
- 需要注意的是嚷量,Java編寫(xiě)的所有代碼都位于某個(gè)類(lèi)的內(nèi)部
- 封裝
- 將數(shù)據(jù)和行為組合在一個(gè)包中陋桂,并向?qū)ο蟮氖褂谜唠[藏?cái)?shù)據(jù)的實(shí)現(xiàn)方式。
- 對(duì)象(類(lèi)的實(shí)例)中的數(shù)據(jù)叫做實(shí)例域(instance field)蝶溶,操縱數(shù)據(jù)的過(guò)程叫方法(method)嗜历。每個(gè)特定的類(lèi)實(shí)例(對(duì)象)都有一組特定的實(shí)例域值,這些值的集合就是這個(gè)對(duì)象的當(dāng)前狀態(tài)。
- 實(shí)現(xiàn)封裝的關(guān)鍵在于秸脱,絕對(duì)不能讓類(lèi)中的方法直接地訪問(wèn)其他類(lèi)地實(shí)例域落包,程序僅通過(guò)對(duì)象的方法與對(duì)象數(shù)據(jù)進(jìn)行交互。
- 對(duì)象
- 對(duì)象的行為:可以對(duì)對(duì)象施加哪些操作摊唇。
- 對(duì)象的狀態(tài):當(dāng)施加了特定的方法時(shí)咐蝇,對(duì)象如何響應(yīng)。
- 對(duì)象標(biāo)識(shí):如何辨別具有相同行為與狀態(tài)的不同對(duì)象巷查。
- 類(lèi)之間的關(guān)系
- 依賴(lài)(uses-a):A類(lèi)的方法操縱B類(lèi)的對(duì)象有序,則A類(lèi)依賴(lài)于B類(lèi)。
- 聚合(has-a):類(lèi)A的對(duì)象中包含了類(lèi)B的對(duì)象岛请,這種關(guān)系也叫關(guān)聯(lián)關(guān)系旭寿。
- 繼承(is-a):類(lèi)A是在類(lèi)B的基礎(chǔ)上,擴(kuò)展得來(lái)的崇败,類(lèi)A不僅包含從類(lèi)B繼承而來(lái)的方法盅称,還會(huì)擴(kuò)展一些新的內(nèi)容。
預(yù)定義類(lèi)的使用
- 對(duì)象與對(duì)象變量的區(qū)分
- 一個(gè)對(duì)象變量并沒(méi)有實(shí)際包含一個(gè)對(duì)象后室,而是引用了一個(gè)對(duì)象而已缩膝!此處的對(duì)象變量更類(lèi)似于C++中指向?qū)ο蟮闹羔槨K詊ava中的null引用對(duì)應(yīng)著C++中的null指針岸霹。
Date deadline; /*只是定義了一個(gè)對(duì)象變量deadline疾层,可以引用Date類(lèi)對(duì)象 但它本質(zhì)上還不是一個(gè)對(duì)象!不能由deadline調(diào)用任何Date類(lèi)的方法贡避。*/ deadline = new Date(); /*使用new構(gòu)造一個(gè)對(duì)象痛黎,并用這個(gè)對(duì)象初始化deadline對(duì)象變量。*/
- 與C++不同刮吧,所有的Java對(duì)象都存儲(chǔ)在堆中湖饱,當(dāng)一個(gè)對(duì)象包含另一個(gè)對(duì)象變量的時(shí)候,這個(gè)變量包含的是指向另一個(gè)堆對(duì)象的指針杀捻。
- GregorianCalendar類(lèi)的使用
- Date類(lèi)的實(shí)例有一個(gè)狀態(tài)琉历,也即每個(gè)Date類(lèi)的實(shí)例對(duì)應(yīng)著一個(gè)特定的時(shí)間點(diǎn)。這個(gè)時(shí)間是用距離一個(gè)固定時(shí)間點(diǎn)的毫秒數(shù)來(lái)表示的(可正可負(fù))水醋,這個(gè)固定時(shí)間點(diǎn)也就是紀(jì)元1970年1月1日00:00:00。但是Date類(lèi)對(duì)時(shí)間的所有操作都是基于公歷的彪置,并不適用于各種其他的歷法拄踪。
- GregorianCalendar類(lèi)的一些方法
/*通過(guò)提供年、月拳魁、日等時(shí)間信息構(gòu)造一個(gè)表示特定日期的日歷對(duì)象惶桐, 需要注意的是,月份從0開(kāi)始計(jì)數(shù),0表示一月份姚糊,因此使常量初始化月份更好贿衍。 */ new GregorianCalendar(2020, 1, 21); new GregorianCalendar(2020,Calendar.January, 21); new GregorianCalendar(2020, Calendar.January,21, 23, 59, 59); /*通過(guò)calendar類(lèi)中的一些常量,從GregorianCalendar類(lèi)中獲取信息, 或者通過(guò)calendar類(lèi)中的一些常量修改GregorianCalendar類(lèi)對(duì)象的一些信息*/ GregorianCalendar now = new GregorianCalendar(); int month = now.get(Calendar.MONTH); int day = now.get(Calendar.DAY_OF_WEEK); now.set(Calendar.YEAR, 2020); now.set(Calendar.MONTH, Calendar.APRIL); now.set(Calendar.DAY_OF_MONTH, 21); int firstDayOfWeek = now.getFirstDayOfWeek(); /*Date類(lèi)對(duì)象與GregorianCalendar類(lèi)對(duì)象的轉(zhuǎn)換*/ GregorianCalendar calendar = new GregorianCalendar(); Date someday = calendar.getTime();
自定義類(lèi)的使用
- 一個(gè)完整的Java程序中救恨,由若干個(gè)類(lèi)組合在一起贸辈,但是只有一個(gè)類(lèi)有main方法。每一個(gè)java源文件的名稱(chēng)必須和文件中public類(lèi)的名字相匹配肠槽,并且一個(gè).java文件中擎淤,只能有一個(gè)public類(lèi)。
- 構(gòu)造器與類(lèi)同名秸仙,在構(gòu)造類(lèi)對(duì)象的時(shí)候嘴拢,構(gòu)造器會(huì)運(yùn)行,以將實(shí)例域初始化為所希望的狀態(tài)寂纪。
- 構(gòu)造器總是伴隨著new操作符的執(zhí)行被調(diào)用席吴,不能對(duì)一個(gè)已經(jīng)存在的對(duì)象調(diào)用構(gòu)造器來(lái)達(dá)到重新設(shè)置實(shí)例域的目的。
- 在構(gòu)造器方法中捞蛋,不能定義與實(shí)例域重名的局部變量孝冒。
- 在訪問(wèn)器方法中,不要返回對(duì)可變對(duì)象的引用襟交!
class Employee{ private Date hireDay; ... public Date getHireDay(){ return this.hireDay; } } public static void main(Strings []){ Employee harry = new Employee(...); Date d = harry.getHireDay(); d.setTime(d.getTime() - (long)(10*365*24*60*60*1000)); } /*d和harry.getHireDay()引用的是同一個(gè)Date類(lèi)對(duì)象迈倍, 并且這個(gè)對(duì)象是可變的(有更改器方法)。 因此對(duì)d調(diào)用更改器方法捣域,就會(huì)修改Date類(lèi)對(duì)象的實(shí)例域啼染。 相當(dāng)于在Employee類(lèi)的外部,存在一個(gè)途徑焕梅, 可以在不調(diào)用Employee類(lèi)更改器的情況下迹鹅,修改其實(shí)例域! 嚴(yán)重破壞封裝性贞言!*/
- 在訪問(wèn)器方法中斜棚,如果需要返回對(duì)可變對(duì)象的引用,應(yīng)該首先對(duì)可變對(duì)象進(jìn)行clone该窗,而后將clone的副本的引用作為返回值弟蚀,寫(xiě)入訪問(wèn)器方法。
class Employee{ ... public Date getHireDay(){ return this.hireDay.clone(); } } /*任何對(duì)getHireDay()返回值的操作酗失,實(shí)際上都是在操作clone出來(lái)的副本义钉, 實(shí)例域中原本的可變對(duì)象就被很好地封裝起來(lái),不受影響规肴。*/
- 方法的訪問(wèn)權(quán)限是基于類(lèi)的捶闸,而不是基于具體對(duì)象的夜畴。方法可以訪問(wèn)所屬類(lèi)的私有特性,而不僅限于訪問(wèn)隱式參數(shù)的私有特性删壮。
class Employee{ ... public boolean equals(Employee other){ return this.name.equals(other.name); /*此處直接從other對(duì)象讀取其實(shí)例域的值贪绘, 而不是通過(guò)other對(duì)象的訪問(wèn)器方法! 如果類(lèi)A的某個(gè)方法中央碟,隱式參數(shù)和顯示參數(shù)都是類(lèi)A的對(duì)象税灌, 那么在該方法中就可以直接訪問(wèn)對(duì)象的私有實(shí)例域*/ } }
靜態(tài)域與靜態(tài)方法
- 靜態(tài)域可以理解為類(lèi)域,即便某個(gè)類(lèi)沒(méi)有一個(gè)實(shí)例對(duì)象存在硬耍,這個(gè)類(lèi)的靜態(tài)域也存在垄琐。每個(gè)類(lèi)中的靜態(tài)域是唯一的,每個(gè)對(duì)象對(duì)于除靜態(tài)域以外的實(shí)例域保存一份自己的副本经柴。
class Employee{ private static int nextId = 1; private int id; } Employee[] stuff = new Employee[1000]; /*在stuff對(duì)象數(shù)組中狸窘,有1000個(gè)實(shí)例域id,但只有一個(gè)靜態(tài)域nextId*/
- 靜態(tài)方法坯认,靜態(tài)方法本身是不能向?qū)ο髮?shí)施操作的翻擒,在這種方法中不存在this參數(shù)。靜態(tài)方法不能操作對(duì)象牛哺,所以不能在靜態(tài)方法中訪問(wèn)實(shí)例域陋气,它只能訪問(wèn)所屬類(lèi)的靜態(tài)域。
//使用靜態(tài)方法的兩種情況 //一個(gè)方法不需要訪問(wèn)對(duì)象狀態(tài)引润,所需參數(shù)全部由顯示參數(shù)提供 import Math; Math.pow(x,a); //一個(gè)方法只需要訪問(wèn)類(lèi)的靜態(tài)域 class Employee{ private static int nextId = 1; private int id; public static int getNextId(){ return nextId;//返回靜態(tài)域中的一個(gè)值 } }
- 工廠方法
- main方法巩趁,本質(zhì)上它是一個(gè)靜態(tài)方法。main方法不操作任何一個(gè)對(duì)象淳附。在啟動(dòng)程序的時(shí)候還沒(méi)有任何一個(gè)對(duì)象存在议慰,靜態(tài)的main方法會(huì)執(zhí)行并創(chuàng)建程序所需要的對(duì)象。在同一個(gè)源文件中可能存在多個(gè)類(lèi)奴曙,每個(gè)類(lèi)中都可以添加main方法别凹,但當(dāng)出現(xiàn)多個(gè)main方法的時(shí)候,只有與源文件同名的public類(lèi)的main方法會(huì)被執(zhí)行
方法參數(shù)(值傳遞洽糟?or 引用傳遞炉菲?)
- 按值調(diào)用表示方法接收到得是調(diào)用者提供的值。
- 按引用調(diào)用表示方法接收的是調(diào)用者提供的變量地址坤溃。
- 對(duì)于基本數(shù)據(jù)類(lèi)型的方法參數(shù)拍霜,Java總是采用按值調(diào)用。
- 對(duì)于對(duì)象類(lèi)型的參數(shù)薪介,Java本質(zhì)上仍然使用按值調(diào)用沉御!
public static void swap(Employee x, Employee y){ Employee temp = x; x = y; y = tmp; } Employee a = new Employee("Alice"...); Employee b = new Employee("Bob"...); swap(a, b); //如果Java是按引用調(diào)用,a指向Bob,b指向Alice //但事實(shí)上,a仍然指向Alice昭灵,b仍然指向Bob吠裆! //這是因?yàn)閟wap方法的參數(shù)x y被初始化為兩個(gè)對(duì)象引用的拷貝,swap方法中交換的實(shí)際上是這兩個(gè)拷貝的值烂完!
- 總而言之试疙,無(wú)論方法中的參數(shù)是什么類(lèi)型,Java方法中使用的都只是該類(lèi)型的一個(gè)拷貝值?衮肌(方法參數(shù)是一個(gè)對(duì)象類(lèi)型的時(shí)候祝旷,拷貝的是指向這個(gè)對(duì)象的一個(gè)引用的值)
對(duì)象構(gòu)造
- 重載:多個(gè)方法有相同的名字,不同的參數(shù)嘶窄。編譯器會(huì)將各個(gè)方法給出的參數(shù)類(lèi)型與特定方法調(diào)用所使用的值類(lèi)型進(jìn)行匹配怀跛,如果找不到匹配或者找到了多個(gè)匹配,則報(bào)出編譯時(shí)錯(cuò)誤柄冲。
- 方法的簽名:方法名 + 參數(shù)類(lèi)型吻谋。簽名是對(duì)一個(gè)方法的完整描述。
- 對(duì)于方法中的局部變量而言现横,Java不為其提供默認(rèn)初始化漓拾。
- 對(duì)于類(lèi)中的域來(lái)說(shuō),如果在構(gòu)造器里面沒(méi)有顯示地為其進(jìn)行初始化戒祠,那么就會(huì)被自動(dòng)地賦為默認(rèn)值骇两。
- 如果類(lèi)中不包含任何的構(gòu)造器,那么系統(tǒng)會(huì)提供一個(gè)無(wú)參構(gòu)造器姜盈,系統(tǒng)提供的這個(gè)構(gòu)造器中會(huì)將所有的實(shí)例域賦為默認(rèn)值低千。如果在類(lèi)中編寫(xiě)了無(wú)參構(gòu)造器,最好在該無(wú)參構(gòu)造器中對(duì)實(shí)例域手動(dòng)進(jìn)行初始化馏颂。
- 在一個(gè)構(gòu)造器中調(diào)用另一個(gè)構(gòu)造器
public Employee(double s){ //調(diào)用構(gòu)造器Employee(String, double) this("Employee #" + nextId, s); nextId++; }
- 初始化塊:在類(lèi)的聲明中示血,可以包含多個(gè)代碼塊,只要構(gòu)造類(lèi)的對(duì)象饱亮,這些代碼塊就會(huì)被執(zhí)行矾芙。在構(gòu)造對(duì)象的時(shí)候,首先執(zhí)行類(lèi)聲明中的代碼塊近上,而后再運(yùn)行構(gòu)造器的主體部分剔宪!
class Employee{ private static int nextId; private int id; private String name; private double salary; //初始化塊 { id = nextId; nextId++; } public Employee(String aName, double aSalary){ name = aName; salary = aSalary; } public Employee(){ name = ""; salary = 0; } ... } //無(wú)論使用哪個(gè)構(gòu)造器構(gòu)造對(duì)象, //id域都會(huì)在初始化塊中先被初始化壹无!而后再運(yùn)行特定構(gòu)造器葱绒!
包的使用
- 標(biāo)準(zhǔn)的Java類(lèi)庫(kù)分布在多個(gè)不同的包中,同名的包只要放置在不同的包中就不會(huì)發(fā)生沖突斗锭。嵌套的包之間沒(méi)有任何的關(guān)系地淀,比如java.util與java.util.jar之間。
- 類(lèi)的導(dǎo)入岖是,一個(gè)類(lèi)可以使用所屬包中的所有類(lèi)帮毁,以及其他包中的公有類(lèi)实苞。
- 在C++中必須使用include語(yǔ)句將外部特性的聲明加載進(jìn)來(lái),因?yàn)镃++編譯器無(wú)法查看任何文件的內(nèi)部烈疚,除了正在編譯的文件以及在頭文件中明確包含的文件黔牵。Java編譯器可以查看文件的內(nèi)部,只要告訴它到哪里去查看就可以了爷肝。
- 導(dǎo)入靜態(tài)方法和靜態(tài)域
import static java.lang.System.*; //添加這樣一條語(yǔ)句猾浦,就可以使用system類(lèi)的靜態(tài)域和靜態(tài)方法。 out.println("Goodbye~"); exit(0);
- 將一個(gè)類(lèi)放入一個(gè)包中灯抛,只需要將包的名字放在源文件的開(kāi)頭即可金赦,類(lèi)似于
package com.horstman.corejava
。 - 變量一般都需要聲明為private对嚼,不然的話就會(huì)默認(rèn)包可見(jiàn)夹抗。標(biāo)記為public的方法可以被任意類(lèi)使用,標(biāo)記為private的方法只能被定義它們的類(lèi)使用猪半,若沒(méi)有指定public或private則可以被同一個(gè)包中的所有方法訪問(wèn)兔朦。
- 標(biāo)記為public的類(lèi)可以被其他包中的類(lèi)訪問(wèn),沒(méi)有標(biāo)記為public的類(lèi)只能被所屬包中的其他類(lèi)訪問(wèn)磨确。
- 一個(gè)jar包中可以包含多個(gè)壓縮形式的類(lèi)文件和子目錄沽甥,它使用zip格式組織文件,可以使用zip解壓方法解壓查看jar包
注釋的基本要點(diǎn)
- 方法注釋
@param乏奥,注釋當(dāng)前方法的參數(shù)摆舟。 @return,對(duì)當(dāng)前方法的返回值進(jìn)行注釋邓了。 @throws,描述當(dāng)前方法可能拋出的異常
- 域注釋(通常只用于公有域恨诱、靜態(tài)常量)
/** * ... */
- 通用注釋
@author,描述當(dāng)前類(lèi)編寫(xiě)者
基本類(lèi)設(shè)計(jì)技巧
- 一定要保證數(shù)據(jù)私有骗炉,絕對(duì)不要破壞封裝性!
- 一定要對(duì)數(shù)據(jù)進(jìn)行初始化照宝,Java不對(duì)局部變量進(jìn)行初始化,但是會(huì)對(duì)類(lèi)對(duì)象的實(shí)例域進(jìn)行初始化句葵。
- 不要在類(lèi)中使用過(guò)多基本類(lèi)型厕鹃。
- 不是所有的域都需要設(shè)置訪問(wèn)器和更改器,因?yàn)橛行?shí)例域是不希望別人獲取或者設(shè)置的乍丈。
- 將職責(zé)過(guò)多的類(lèi)進(jìn)行分解剂碴。
- 類(lèi)名和方法名要能夠體現(xiàn)他們的職責(zé)。
- 類(lèi)命名:一個(gè)名詞(order)轻专、有形容詞修飾的名詞忆矛、有ing修飾的動(dòng)詞+名詞。
- 方法命名:駝峰法