?
類(lèi)和對(duì)象
?
?
?
?
??? Java語(yǔ)言是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,類(lèi)和對(duì)象是面向?qū)ο蟪绦蛟O(shè)計(jì)的基本概念。類(lèi)是相似對(duì)象中共同屬性和方法的集合體。對(duì)象是類(lèi)的實(shí)例。包是Java組織和管理類(lèi)的一種方法。類(lèi)的封裝性是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重要特點(diǎn)。
?
?
1、面向?qū)ο蟪绦蛟O(shè)計(jì)
?
1.面向?qū)ο蟪绦蛟O(shè)計(jì)的基本概念
???
Java語(yǔ)言是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言。面向?qū)ο蟪绦蛟O(shè)計(jì)(Object Oriented Programming,簡(jiǎn)稱OOP)是一種集問(wèn)題分析方法、軟件設(shè)計(jì)方法和人類(lèi)的思維方法于一體的、貫穿軟件系統(tǒng)分析、設(shè)計(jì)和實(shí)現(xiàn)整個(gè)過(guò)程的程序設(shè)計(jì)方法。面向?qū)ο蟪绦蛟O(shè)計(jì)方法的出發(fā)點(diǎn)和追求的基本目標(biāo),是使我們分析、設(shè)計(jì)和實(shí)現(xiàn)一個(gè)軟件系統(tǒng)的方法盡可能接近我們認(rèn)識(shí)一個(gè)事物的方法。面向?qū)ο蟪绦蛟O(shè)計(jì)的基本思想是:對(duì)軟件系統(tǒng)要模擬的客觀實(shí)體以接近人類(lèi)思維的方式進(jìn)行自然分割,然后對(duì)客觀實(shí)體進(jìn)行結(jié)構(gòu)模擬和功能模擬,從而使設(shè)計(jì)出的軟件盡可能直接地描述客觀實(shí)體,從而構(gòu)造出模塊化的、可重用的、維護(hù)方便的軟件。
??? 在現(xiàn)實(shí)世界中,客觀實(shí)體有兩大類(lèi):
??? (1)我們身邊存在的一切有形事物和抽象概念都是客觀實(shí)體。有形事物的例子如一個(gè)教師、一件衣服、一本書(shū)、一個(gè)飯店、一座樓、一個(gè)學(xué)校等;抽象概念的例子如學(xué)校校規(guī)、企業(yè)規(guī)定等;
????
(2)我們身邊發(fā)生的一切事件都是客觀實(shí)體,例如一場(chǎng)足球比賽、一次流感侵襲、一次到醫(yī)院的就診過(guò)程、一次到圖書(shū)館的借書(shū)過(guò)程,等等。
??? 不同的客觀實(shí)體具有各自不同的特征和功能。例如,飯店具有飯店的特征和功能,學(xué)校具有學(xué)校的特征和功能;又例如,就診過(guò)程具有就診過(guò)程的特征和功能,借書(shū)過(guò)程具有借書(shū)過(guò)程的特征和功能。
??? 現(xiàn)實(shí)世界中的一切客觀實(shí)體都具有如下特征:
??? ?有一個(gè)名字用來(lái)惟一地標(biāo)識(shí)該客觀實(shí)體;
??? ?有一組屬性用來(lái)描述其特征;
??? ?有一組方法用來(lái)實(shí)現(xiàn)其功能。
???
例如,作為書(shū)店客觀實(shí)體,每個(gè)書(shū)店有不同的點(diǎn)名、負(fù)責(zé)人等屬性(特征),書(shū)店的入庫(kù)、出庫(kù)、圖書(shū)上架等是其基本的方法(功能)。
??? 面向?qū)ο蟪绦蛟O(shè)計(jì)方法下的軟件系統(tǒng)設(shè)計(jì)方法,首先是把問(wèn)題中涉及的客觀實(shí)體分割出來(lái),并設(shè)計(jì)成稱為類(lèi)的可重復(fù)使用的模塊,然后定義出一個(gè)個(gè)實(shí)例化的類(lèi)(稱為對(duì)象),再按照問(wèn)題的要求,設(shè)計(jì)出用各個(gè)對(duì)象的操作完成的軟件系統(tǒng)。
?
2.類(lèi)
???
在現(xiàn)實(shí)世界中,類(lèi)這個(gè)術(shù)語(yǔ)是對(duì)一組相似客觀實(shí)體的抽象描述。例如,有A書(shū)店、B書(shū)店、C書(shū)店等。而書(shū)店類(lèi)則是對(duì)書(shū)店這類(lèi)客觀實(shí)體所應(yīng)具有的共同屬性和方法的抽象描述。即書(shū)店類(lèi)不是具體的描述,而是抽象的描述。
??? 在面向?qū)ο蠓椒ㄖ校唧w的客觀實(shí)體稱為對(duì)象,類(lèi)是對(duì)具有相同屬性和相同方法的一組相似對(duì)象的抽象,或者說(shuō),類(lèi)所包含的屬性和方法描述了一組對(duì)象的共同的屬性和方法。
??? 面向?qū)ο蠓椒ㄖ熊浖O(shè)計(jì)的主體是類(lèi)。類(lèi)是相同屬性和方法的封裝體,因此類(lèi)具有封裝性;子類(lèi)可以在繼承父類(lèi)所有屬性和方法的基礎(chǔ)上,再增加自己特有的屬性和方法,因此類(lèi)具有繼承性;在一個(gè)類(lèi)層次中,定義為根類(lèi)的對(duì)象可被賦值為其任何子類(lèi)的對(duì)象,并根據(jù)子類(lèi)對(duì)象的不同而調(diào)用不同的方法,因此類(lèi)具有多態(tài)性。類(lèi)的這種封裝性、多態(tài)性和繼承性,是面向?qū)ο蟪绦蛟O(shè)計(jì)的三個(gè)最重要的特點(diǎn)。
?
3.對(duì)象
???
類(lèi)是具有相同屬性和方法的一組相似對(duì)象的抽象描述,但在現(xiàn)實(shí)世界中,抽象描述的類(lèi)是并不具體存在的,例如,現(xiàn)實(shí)世界中只存在具體的A書(shū)店、B書(shū)店、C書(shū)店,并不存在抽象的書(shū)店。我們把按照類(lèi)這個(gè)模板所定義的一個(gè)個(gè)具體的對(duì)象稱作類(lèi)的實(shí)例,或稱作對(duì)象。
??? 首先,一個(gè)具體對(duì)象必須具有具體的屬性值,如A書(shū)店對(duì)象就必須具有如下屬性:書(shū)點(diǎn)名為A書(shū)店,負(fù)責(zé)人為張三等。其次,任何對(duì)象都具有相應(yīng)類(lèi)所規(guī)定的所有方法。
?
?
2、類(lèi)
??? 面向?qū)ο蠓椒ㄖ校浖O(shè)計(jì)的最主要部分是設(shè)計(jì)類(lèi)。類(lèi)的設(shè)計(jì)可以劃分為類(lèi)聲明和類(lèi)主體設(shè)計(jì)兩部分。
?
1、類(lèi)聲明
?
1.類(lèi)聲明的格式
???
類(lèi)聲明的格式如下:
??? [<修飾符>] class<類(lèi)名>[extends<父類(lèi)名>]?[implements<接口名表>]
??? {
????? 類(lèi)主體
??? }
???
其中,class是定義類(lèi)的關(guān)鍵字,<類(lèi)名>是所定義的類(lèi)的名字,<父類(lèi)名>是已經(jīng)定義過(guò)的類(lèi)名,<接口名表>是已經(jīng)定義過(guò)的若干個(gè)接口名,當(dāng)接口名多于一個(gè)時(shí),用逗號(hào)分隔開(kāi)。方括號(hào)表示該項(xiàng)是可選項(xiàng)。本章只討論基本的類(lèi)設(shè)計(jì)方法,含有父類(lèi)和接口的類(lèi)將在下一章討論。
?
2.類(lèi)的修飾符
??? 類(lèi)聲明的<修飾符>分為訪問(wèn)控制符和類(lèi)型說(shuō)明符兩部分,分別用來(lái)說(shuō)明該類(lèi)的訪問(wèn)權(quán)限以及該類(lèi)是否為抽象類(lèi)或最終類(lèi)。
??? (
1)訪問(wèn)控制符public和默認(rèn)
????當(dāng)訪問(wèn)控制符為public時(shí),表示該類(lèi)被定義為公共類(lèi)。公共類(lèi)表示該類(lèi)能被任何類(lèi)訪問(wèn)。由于類(lèi)都放于某個(gè)包中,包中的類(lèi)互相能訪問(wèn),而不在一個(gè)包中的類(lèi)互相不能直接訪問(wèn)。如果要在一個(gè)包中訪問(wèn)另一個(gè)包中的類(lèi),就必須用import語(yǔ)句導(dǎo)入所需要的類(lèi)到該包中,但Java語(yǔ)言規(guī)定,被導(dǎo)入的類(lèi)必須是用public修飾的類(lèi)。
??? 當(dāng)沒(méi)有訪問(wèn)控制符public時(shí),即是默認(rèn)類(lèi)(或稱缺省類(lèi))。默認(rèn)類(lèi)表示該類(lèi)只能被同一個(gè)包中的類(lèi)訪問(wèn),而不能被其他包中的類(lèi)訪問(wèn)。Java語(yǔ)言規(guī)定,一個(gè)Java文件中可以有很多類(lèi),但最多只能有一個(gè)公共類(lèi),其他都必須定義為默認(rèn)類(lèi)。
??? 例如: public? class? Teacher就聲明了一個(gè)公共類(lèi)Teacher,該類(lèi)可以通過(guò)import語(yǔ)句導(dǎo)入到其他包的類(lèi)中,并能被其他所有的類(lèi)訪問(wèn)。又例如,class?Student就聲明了一個(gè)默認(rèn)類(lèi)Student,該類(lèi)只能被同一個(gè)包中的其他類(lèi)訪問(wèn)。
??? (
2)類(lèi)型說(shuō)明符abstract和final
??? 當(dāng)類(lèi)型說(shuō)明符為abstract時(shí),表示該類(lèi)為抽象類(lèi),抽象類(lèi)不能用來(lái)定義對(duì)象,抽象類(lèi)通常設(shè)計(jì)成一些具有類(lèi)似成員變量和方法的子類(lèi)的父類(lèi)。
??? 當(dāng)類(lèi)型說(shuō)明符為final時(shí),表示該類(lèi)為最終類(lèi),最終類(lèi)不能用來(lái)再派生子類(lèi)。
??? 訪問(wèn)控制符和類(lèi)型說(shuō)明符一起使用時(shí),訪問(wèn)控制符在前,類(lèi)型說(shuō)明符在后。例如,public?abstract?class?Teacher就聲明了一個(gè)公共抽象類(lèi)Teacher。
?
?
2、類(lèi)主體設(shè)計(jì)
???
在上節(jié)討論面向?qū)ο蟪绦蛟O(shè)計(jì)時(shí)曾說(shuō)過(guò),類(lèi)由成員變量和成員方法組成。因此,類(lèi)主體設(shè)計(jì)包括類(lèi)的成員變量設(shè)計(jì)和類(lèi)的成員方法設(shè)計(jì)兩部分。由于Java語(yǔ)言中的所有方法必定屬于某個(gè)類(lèi),即方法一定是成員方法,所以成員方法可簡(jiǎn)稱為方法。我們首先給出一個(gè)簡(jiǎn)單的日期類(lèi)設(shè)計(jì),然后分別討論類(lèi)成員變量和類(lèi)方法的設(shè)計(jì)。
【例3.1】設(shè)計(jì)一個(gè)日期類(lèi)。要求方法應(yīng)包括設(shè)置日期、顯示日期和判斷是否是閏年。
類(lèi)設(shè)計(jì)如下:
public class Date???????????????????//類(lèi)聲明
{
??//以下為類(lèi)成員變量聲明
? private int year;????????????????? ?//成員變量,表示年
??private int month;??????????????? ??//成員變量,表示月
??private int day;????????????????? ??//成員變量,表示日
????
??//以下為類(lèi)方法聲明和實(shí)現(xiàn)
??public void SetDate(int y,int m,int d)??? ?//設(shè)置日期值
??? {??????????????????????????????????
??????year = y;
??????month = m;
??????day = d;
??? }?
????
??public void Print()????????????????????? ??//輸出日期值
??? {
??????System.out.println("date is "+year+'-'+month+'-'+day);
??? }?
????????
??public boolean IsLeapYear()????????????? ??//判斷是否為閏年
??? {??????????????????????????????????
??????return (year%400==0) | (year%100!=0) & (year%4==0);
??? }
}
1.聲明成員變量
???
聲明一個(gè)成員變量就是聲明該成員變量的名字及其所屬的數(shù)據(jù)類(lèi)型,同時(shí)指定其他一些附加特性。聲明成員變量的格式為:
??? [<修飾符>] [static] [final] [transient]?<變量類(lèi)型>??<變量名>;
??? 其中,<修飾符>有private、public和protected三種。當(dāng)不加任何修飾符時(shí),定義為默認(rèn)修飾符。
???
private修飾符表示該成員變量只能被該類(lèi)本身訪問(wèn),任何其他類(lèi)都不能訪問(wèn)該成員變量。
???
protected修飾符表示該成員變量除可以被該類(lèi)本身和同一個(gè)包的類(lèi)訪問(wèn)外,還可以被它的子類(lèi)(包括同一個(gè)包中的子類(lèi)和不同包中的子類(lèi))訪問(wèn)。
???
public修飾符表示該成員變量可以被所有類(lèi)訪問(wèn)。
??? 不加任何訪問(wèn)權(quán)限限定的成員變量屬于默認(rèn)訪問(wèn)權(quán)限。默認(rèn)訪問(wèn)權(quán)限表示該成員變量只能被該類(lèi)本身和同一個(gè)包的類(lèi)訪問(wèn)。
??? 上述修飾符實(shí)現(xiàn)了類(lèi)中成員變量在一定范圍內(nèi)的信息隱藏。這既符合程序設(shè)計(jì)中隱藏內(nèi)部信息處理細(xì)節(jié)的原則,也有利于數(shù)據(jù)的安全性。
???
static指明該成員變量是一個(gè)類(lèi)成員變量
???
final指明該成員變量是常量
???
transient指明該成員變量是臨時(shí)變量。
transient很少使用。
??? 類(lèi)成員變量是一個(gè)類(lèi)的所有對(duì)象共同擁有的成員變量。關(guān)于類(lèi)成員變量的用途和使用方法我們將通過(guò)后面的例子說(shuō)明。
??? 因此,例3.1中的成員變量year、month和day都是int類(lèi)型的private成員變量,即這三個(gè)成員變量只能被該類(lèi)本身訪問(wèn),任何其他類(lèi)都不能訪問(wèn)該成員變量。
?
2.聲明方法
???
聲明成員方法的格式為:
??? [<修飾符>] [static]?<返回值類(lèi)型>?<方法名>? ([<參數(shù)列表>])?
??? {
????? <方法體>
??? }
???
其中,<修飾符>和成員變量的修飾符一樣,有private、public和protected三種,另外,還有默認(rèn)。
??? 各個(gè)修飾符的含義也和成員變量修飾符的含義相同。static指明該方法是一個(gè)類(lèi)方法。關(guān)于類(lèi)方法的用途和使用方法我們將通過(guò)后面的例子說(shuō)明。
??? 方法聲明中必須給出方法名和方法的返回值類(lèi)型,如果沒(méi)有返回值,用關(guān)鍵字void標(biāo)記。方法名后的一對(duì)圓括號(hào)是必須的,即使參數(shù)列表為空,也要加一對(duì)空括號(hào)。
??? 例如:
??? public void SetDate(int y,int m,int d)
??? 上述語(yǔ)句聲明了方法名為SetDate的public方法,其返回值為空,參數(shù)有三個(gè),分別為y、m和d,這三個(gè)參數(shù)的數(shù)據(jù)類(lèi)型均為int。
?
3.方法體
???
方法體是方法的具體實(shí)現(xiàn)。方法體的設(shè)計(jì)即是第2章討論的變量定義、賦值語(yǔ)句、if語(yǔ)句、for語(yǔ)句等根據(jù)方法體設(shè)計(jì)要求的綜合應(yīng)用。例如,
??? public void SetDate(int y,int m,int d)??? ?//設(shè)置日期值
??? {??????????????????????????????????
????? year = y;????????//給成員變量year賦值y
?????month = m;???????//給成員變量month賦值m
?????day = d; ????????//給成員變量day賦值d
??? }
?
4.成員變量和變量
??? 初學(xué)者經(jīng)常會(huì)混淆成員變量和變量,一個(gè)最簡(jiǎn)單的區(qū)別方法是:定義在類(lèi)中的都是成員變量,定義在方法內(nèi)的都是變量。另外,還有定義在方法參數(shù)中的虛參變量,如例3.1 SetDate()中的y、m和 d。關(guān)于方法中定義的變量的例子見(jiàn)后面的設(shè)計(jì)舉例。
??? 成員變量和變量的類(lèi)型既可以是基本數(shù)據(jù)類(lèi)型(如int、long等),也可以是已定義的類(lèi)。
?
3、構(gòu)造方法
??? 在類(lèi)的方法中,有一種特殊的方法專門(mén)用來(lái)進(jìn)行對(duì)象的初始化,這個(gè)專門(mén)用來(lái)進(jìn)行對(duì)象初始化的方法稱為構(gòu)造方法。構(gòu)造方法也稱作構(gòu)造函數(shù)。一般來(lái)說(shuō),一個(gè)類(lèi)中至少要有一個(gè)構(gòu)造方法。
??? 構(gòu)造方法在語(yǔ)法上等同于其他方法。因此構(gòu)造方法的設(shè)計(jì)方法和前面說(shuō)的其他方法的設(shè)計(jì)方法類(lèi)同。但構(gòu)造方法的名字必須與其類(lèi)名完全相同,并且沒(méi)有返回值,甚至連表示空類(lèi)型(void)的返回值都沒(méi)有。構(gòu)造方法一般應(yīng)定義為public。
??? 構(gòu)造方法用來(lái)在對(duì)象創(chuàng)建時(shí)為對(duì)象的成員變量進(jìn)行初始化賦值。其實(shí)現(xiàn)過(guò)程是:在創(chuàng)建對(duì)象時(shí),將調(diào)用相應(yīng)類(lèi)中的構(gòu)造方法為對(duì)象的成員變量進(jìn)行初始化賦值。
??? 例如,我們可以為上述日期類(lèi)設(shè)計(jì)一個(gè)如下的構(gòu)造方法:
??? public Date(int y, int m, int d)?????? ???//構(gòu)造方法
??? {
? ??? year = y;
? ??? month = m;
? ??? day = d;
??? }
?
?
3、對(duì)象
??? 類(lèi)是一類(lèi)相似對(duì)象的抽象描述,一個(gè)軟件系統(tǒng)是對(duì)具體問(wèn)題的客觀事物進(jìn)行模擬或描述的,因此需要具體的描述。對(duì)象是類(lèi)的實(shí)例化,對(duì)象就是軟件系統(tǒng)中對(duì)具體問(wèn)題的客觀事物進(jìn)行的具體模擬或具體描述。
?
1、main方法
?
??? 類(lèi)是作為許多相似對(duì)象的抽象描述設(shè)計(jì)的,如果要使用這個(gè)類(lèi)時(shí),就必須創(chuàng)建這個(gè)類(lèi)的對(duì)象。那么,對(duì)象創(chuàng)建應(yīng)該是在同一個(gè)類(lèi)中呢?還是應(yīng)該在另一個(gè)類(lèi)中呢?答案是兩者都可以,但最好是在另一個(gè)類(lèi)中。這樣沒(méi)有對(duì)象定義的純粹的類(lèi)設(shè)計(jì)部分就可以單獨(dú)保存在一個(gè)文件中,就不會(huì)影響該類(lèi)的重復(fù)使用。
??? Java語(yǔ)言規(guī)定,一個(gè)類(lèi)對(duì)應(yīng)一個(gè).class文件,一個(gè)程序文件中可以包含一個(gè)或一個(gè)以上的類(lèi),但其中只允許一個(gè)類(lèi)被定義成public類(lèi)型。類(lèi)中可以沒(méi)有main方法。但是要運(yùn)行的類(lèi)中必須有main方法。程序就是從main方法開(kāi)始執(zhí)行的。
??? 下面的例子是把對(duì)象創(chuàng)建在同一個(gè)類(lèi)中的main方法中。
【例3.2】打印某個(gè)日期,并判斷該年是否是閏年。
public class Date?????????????????????//類(lèi)聲明
{
? private int year;???????????????????//成員變量,表示年
??private int month;??????????????? ??//成員變量,表示月
??private int day;????????????????? ??//成員變量,表示日
??public Date(int y, int m, int d)?????? ???//構(gòu)造方法
??{
??? year = y;
????month = m;
????day = d;
???}
????
??//以下為其他類(lèi)方法
??public void SetDate(int y, int m, int d)??? ?//設(shè)置日期值
??{?
????year = y;
????month = m;
????day = d;
??}?
????
??public void Print()?????????????????????? ???//輸出日期值
??{
????System.out.println("date is "+year+'-'+month+'-'+day);
??}?
????????
??public boolean IsLeapYear()?????????????? ??//判斷是否閏年
??{??????????????????????????????????
????return (year%400==0) | (year%100!=0) & (year%4==0);
??}
?
??public static void main(String args[])???//main()方法
??{
????Date a = new Date(2004, 8, 5) ;????? ??//創(chuàng)建對(duì)象
????a.Print();
????if(a.IsLeapYear())
??????System.out.println(a.year + " 是閏年");
????else
??????System.out.println(a.year + " 不是閏年");
??}
}
main方法必須放在類(lèi)中,且格式必須為:public static void main(String args[])
2、對(duì)象的創(chuàng)建和初始化
?
??? 在例3.2中,語(yǔ)句Date a = new Date(2004, 8, 5);?
??? 實(shí)現(xiàn)了定義對(duì)象a和為對(duì)象分配內(nèi)存空間,并初始化對(duì)象a的成員變量數(shù)值為:
??? year = 2004; month = 8; day = 5;
??? 上述方法是把定義對(duì)象和創(chuàng)建對(duì)象這兩個(gè)步驟結(jié)合在了一起,并同時(shí)進(jìn)行了對(duì)象的初始化賦值。這是最簡(jiǎn)單、也是最經(jīng)常使用的對(duì)象定義、創(chuàng)建和初始化方法。
??? 對(duì)象的定義和創(chuàng)建過(guò)程也可以分開(kāi)進(jìn)行,即首先定義對(duì)象,然后為對(duì)象分配內(nèi)存空間,并可同時(shí)進(jìn)行初始化賦值。
?
1.定義對(duì)象
???
定義對(duì)象的語(yǔ)句格式為:
??? <類(lèi)名>?<對(duì)象名>;
??? 例如下面語(yǔ)句就定義了一個(gè)Date類(lèi)的對(duì)象a:
??? Date a;?
??? 對(duì)象和數(shù)組一樣,也是引用類(lèi)型。即對(duì)象定義后,系統(tǒng)將給對(duì)象標(biāo)識(shí)符分配一個(gè)內(nèi)存單元,用于存放實(shí)際對(duì)象在內(nèi)存中的存放位置。由于在對(duì)象定義時(shí),對(duì)象在內(nèi)存中的實(shí)際存放位置還沒(méi)有給出,所以,此時(shí)該對(duì)象名的值為空(null)。上述語(yǔ)句后對(duì)象a的當(dāng)前狀態(tài)如下圖的(a)所示:
???
?
2.為對(duì)象分配內(nèi)存空間和進(jìn)行初始化賦值
???
和為數(shù)組分配內(nèi)存空間一樣,為對(duì)象分配內(nèi)存空間也使用new運(yùn)算符。為對(duì)象分配內(nèi)存空間的語(yǔ)句格式為:
??? <對(duì)象名>=? new?<類(lèi)名> ([<參數(shù)列表>]);
??? 其中,new運(yùn)算符申請(qǐng)了對(duì)象所需的內(nèi)存空間,new運(yùn)算符返回所申請(qǐng)的內(nèi)存空間的首地址。系統(tǒng)將根據(jù)<類(lèi)名>和<參數(shù)列表>調(diào)用相應(yīng)的構(gòu)造方法為對(duì)象進(jìn)行初始化賦值(即把參數(shù)值存入相應(yīng)的內(nèi)存單元中)。賦值語(yǔ)句把new運(yùn)算符分配的連續(xù)地址的首地址賦給了對(duì)象名。正因?yàn)闃?gòu)造方法名和類(lèi)名完全相同,所以這里的類(lèi)名既用來(lái)作為new運(yùn)算符的參數(shù)向系統(tǒng)申請(qǐng)對(duì)象所需的內(nèi)存空間,又作為構(gòu)造方法名為對(duì)象進(jìn)行初始化賦值。例如:
??? a = new Date(2004, 8, 5) ;?
??? 就先向系統(tǒng)申請(qǐng)了Date類(lèi)對(duì)象所需的內(nèi)存空間(其首地址由對(duì)象名a指示),又用參數(shù)2004、8和5調(diào)用了構(gòu)造方法Date(2004, 8, 5),為對(duì)象a進(jìn)行了初始化賦值。上述語(yǔ)句后對(duì)象a的當(dāng)前狀態(tài)如上圖中的(b)所示。
??? 程序設(shè)計(jì)時(shí)最經(jīng)常使用的方法,是在定義對(duì)象的同時(shí)為對(duì)象分配內(nèi)存空間和進(jìn)行初始化賦值。例如,
??? Date a = new Date(2004, 8, 5) ;
?
3.對(duì)象的使用
???
一旦定義并創(chuàng)建了對(duì)象(創(chuàng)建對(duì)象是指為對(duì)象分配了內(nèi)存單元),就可以在程序中使用對(duì)象。
??? 對(duì)象的使用主要有三種情況,分別是:使用對(duì)象的成員變量或方法,對(duì)象間賦值和把對(duì)象作為方法的參數(shù)。
??? (1)
使用對(duì)象的成員變量或方法
??? 一旦定義并創(chuàng)建了對(duì)象,就可以使用對(duì)象的成員變量或方法。
??? 例如,例3.2的main()方法中a.year就使用了對(duì)象a的成員變量year。
??? 又例如,例3.2的main()方法中a.Print()就使用了對(duì)象a的成員方法Print()。
??? 另外,還可以修改對(duì)象的成員變量的數(shù)值,例如,如果在例3.2的main()方法中增加如下語(yǔ)句,就把對(duì)象a的成員變量year的數(shù)值修改為2005,
??? a.year = 2005;
??? (
2)對(duì)象間賦值
??? 對(duì)象可以像變量一樣賦值。例如,如果在例3.2的main()方法中增加如下語(yǔ)句,則對(duì)象b的值和對(duì)象a的值相同。
??? Date b;
??? b = a;
??? 但和變量賦值不一樣的是,對(duì)象賦值并沒(méi)有真正把一個(gè)對(duì)象的數(shù)值賦給另一個(gè)對(duì)象,而是讓另一個(gè)對(duì)象名(如對(duì)象名b)存儲(chǔ)的對(duì)象的首地址和這個(gè)對(duì)象名(如對(duì)象名a)存儲(chǔ)的對(duì)象的首地址相同。即對(duì)象的賦值是對(duì)象的首地址的賦值。例如,上述語(yǔ)句就把對(duì)象a的首地址值賦給了對(duì)象b,因此,對(duì)象b和對(duì)象a表示的是同一個(gè)對(duì)象。
??? 仔細(xì)分析上圖所示的對(duì)象的存儲(chǔ)結(jié)構(gòu),就可以理解對(duì)象間賦值的實(shí)現(xiàn)方法。
??? (
3)把對(duì)象作為方法的參數(shù)
??? 對(duì)象也可以像變量一樣,作為方法的參數(shù)使用。但系統(tǒng)實(shí)現(xiàn)兩者的方法不同,變量作為方法的實(shí)參時(shí),系統(tǒng)把實(shí)參的數(shù)值復(fù)制給虛參;而對(duì)象作為方法的實(shí)參時(shí),系統(tǒng)是把實(shí)參對(duì)象名(該對(duì)象必須已創(chuàng)建)指示的對(duì)象的首地址賦給了虛參對(duì)象名。其實(shí)現(xiàn)方法實(shí)際上和對(duì)象間賦值的實(shí)現(xiàn)方法相同。
?
4.垃圾對(duì)象的回收
???
從上面的討論可知,對(duì)象和變量在很多方面有些類(lèi)同,例如,對(duì)象和變量都需要分配內(nèi)存空間。但是,變量的內(nèi)存空間是系統(tǒng)在變量定義時(shí)自動(dòng)分配的,當(dāng)變量超出作用范圍時(shí),系統(tǒng)將自動(dòng)回收該變量的內(nèi)存空間。
??? 而對(duì)象的內(nèi)存空間是在用戶需要時(shí),用new運(yùn)算符創(chuàng)建的。對(duì)象也有作用范圍,我們把超出作用范圍的對(duì)象(或稱不再被使用的對(duì)象)稱作垃圾對(duì)象。那么,誰(shuí)來(lái)負(fù)責(zé)這些垃圾對(duì)象的回收呢?答案是,在Java中,收集和釋放內(nèi)存是一個(gè)叫做自動(dòng)垃圾回收線程的責(zé)任。線程的概念將在第10章討論,這里可以把自動(dòng)垃圾回收線程理解為一個(gè)系統(tǒng)自己開(kāi)啟、并與用戶程序并行運(yùn)行的一個(gè)服務(wù)程序。自動(dòng)垃圾回收線程在系統(tǒng)空閑時(shí)自動(dòng)運(yùn)行,這個(gè)線程監(jiān)視用戶程序中所有對(duì)象的有效作用范圍,當(dāng)某個(gè)對(duì)象超出其作用范圍時(shí),該線程就對(duì)這樣的對(duì)象做上垃圾對(duì)象標(biāo)識(shí),并在適當(dāng)?shù)臅r(shí)候一次性回收這些垃圾對(duì)象。
??? 所以,用戶程序只需考慮為對(duì)象分配內(nèi)存空間,不需考慮垃圾對(duì)象內(nèi)存空間的回收。
?
5.實(shí)例成員變量與類(lèi)成員變量
???
類(lèi)有兩種不同類(lèi)型的成員變量:實(shí)例成員變量與類(lèi)成員變量。類(lèi)成員變量也稱作靜態(tài)成員變量。
?
??? (1)
實(shí)例成員變量
??? 類(lèi)定義中沒(méi)用關(guān)鍵字static修飾的成員變量就是實(shí)例成員變量,不同對(duì)象的實(shí)例成員變量其值不相同。
??? 例3.2中類(lèi)Date的成員變量定義語(yǔ)句:
??? private int year;
??? private int month;
??? private int day;
??? 就定義了三個(gè)private類(lèi)型的實(shí)例成員變量year、month和day。
??? 例如,若有如下對(duì)象定義:
??? Date a = new Date(2003, 1, 1) ;
??? Date b = new Date(2004, 5, 10) ;
??? 則對(duì)象a和對(duì)象b的實(shí)例成員變量數(shù)值就不同。如a.year的數(shù)值為2003,而b.year的數(shù)值為2004。
?
??? (2)
類(lèi)成員變量
??? 用關(guān)鍵字static修飾的成員變量稱為類(lèi)成員變量,一個(gè)類(lèi)的所有對(duì)象共享該類(lèi)的類(lèi)成員變量。類(lèi)成員變量可以用來(lái)保存和類(lèi)相關(guān)的信息,或用來(lái)在一個(gè)類(lèi)的對(duì)象間交流信息。
【例3.3】用類(lèi)成員變量表示當(dāng)前該類(lèi)共有多少個(gè)對(duì)象被定義。
public class ObjectNumber?????//類(lèi)聲明
{
??private static int number = 0;???//類(lèi)成員變量
????
??public ObjectNumber()?????//構(gòu)造方法
??{??????????????????????????????????
????number++;
??}?
????
??public void Print()??????//對(duì)象個(gè)數(shù)輸出
??{
????System.out.println(number);
??}?
????????
??public static void main(String args[])
??{
????ObjectNumber a = new ObjectNumber() ; ??//創(chuàng)建對(duì)象
????System.out.print("當(dāng)前對(duì)象個(gè)數(shù)為:");
????a.Print();?????????//對(duì)象個(gè)數(shù)輸出
????ObjectNumber b = new ObjectNumber() ;?? //創(chuàng)建對(duì)象??
????System.out.print("當(dāng)前對(duì)象個(gè)數(shù)為:");
????b.Print();?????????//對(duì)象個(gè)數(shù)輸出
??}
}
輸出結(jié)果為:
當(dāng)前對(duì)象個(gè)數(shù)為:1
當(dāng)前對(duì)象個(gè)數(shù)為:2
?
6.實(shí)例方法與類(lèi)方法
???
類(lèi)有兩種不同類(lèi)型的方法:實(shí)例方法與類(lèi)方法。類(lèi)方法也稱作靜態(tài)方法。
??? (
1)實(shí)例方法
??? 沒(méi)用關(guān)鍵字static修飾的方法就是實(shí)例方法。實(shí)例方法只能通過(guò)對(duì)象來(lái)調(diào)用。實(shí)例方法既可以訪問(wèn)類(lèi)成員變量,也可以訪問(wèn)類(lèi)變量。例3.2中類(lèi)Date的SetDate()方法、 Print()方法和IsLeapYear()方法都是實(shí)例方法。這些實(shí)例方法體中都訪問(wèn)了實(shí)例成員變量。
??? (2)
類(lèi)方法
???
用關(guān)鍵字static修飾的方法稱為類(lèi)方法。類(lèi)方法通過(guò)類(lèi)名來(lái)調(diào)用(也可以通過(guò)對(duì)象來(lái)調(diào)用)。類(lèi)方法只能訪問(wèn)類(lèi)變量,不能訪問(wèn)實(shí)例變量。類(lèi)方法主要用來(lái)處理和整個(gè)類(lèi)相關(guān)的數(shù)據(jù)。雖然類(lèi)方法既可以用類(lèi)名來(lái)調(diào)用,也可以用對(duì)象來(lái)調(diào)用,但用類(lèi)名調(diào)用類(lèi)方法程序的可讀性更好。
【例3.4】用類(lèi)方法處理當(dāng)前該類(lèi)共有多少個(gè)對(duì)象被定義。
程序設(shè)計(jì)如下:
public class ObjectNumber2?????????//類(lèi)聲明
{
??private static int number = 0;???//類(lèi)成員變量
????
??public ObjectNumber2()?????????? //構(gòu)造方法
??{
????number++;
??}?
????
??public static void Print()?????? //類(lèi)方法
??{
????System.out.println("當(dāng)前對(duì)象個(gè)數(shù)為:" + number);
??}?
????????
??public static void main(String args[])
??{
????ObjectNumber2 a = new ObjectNumber2() ; ?//創(chuàng)建對(duì)象
????ObjectNumber2.Print();??????? ?//對(duì)象個(gè)數(shù)輸出
//??a.Print();//可以,但不提倡
????ObjectNumber2 b = new ObjectNumber2() ; ?//創(chuàng)建對(duì)象??
????ObjectNumber2.Print();???????? //對(duì)象個(gè)數(shù)輸出
//??b.Print();//可以,但不提倡
??? }
}
輸出結(jié)果為:
當(dāng)前對(duì)象個(gè)數(shù)為:1
當(dāng)前對(duì)象個(gè)數(shù)為:2
?
7.方法的重寫(xiě)
????
類(lèi)的各種方法(包括構(gòu)造方法和其他方法)都允許重寫(xiě)(也稱做重載)。所謂方法重寫(xiě)(overloading),是指一個(gè)方法名定義了多個(gè)方法實(shí)現(xiàn)。方法重寫(xiě)時(shí)要求,不同的方法,其參數(shù)類(lèi)型或參數(shù)個(gè)數(shù)要有所不同。若類(lèi)的某個(gè)方法被重寫(xiě),該類(lèi)的對(duì)象在訪問(wèn)該方法時(shí),以對(duì)象調(diào)用該方法的參數(shù)個(gè)數(shù)和參數(shù)類(lèi)型與類(lèi)的同名方法進(jìn)行匹配,對(duì)象調(diào)用該方法的參數(shù)個(gè)數(shù)和參數(shù)類(lèi)型與類(lèi)定義中哪個(gè)方法的參數(shù)個(gè)數(shù)和參數(shù)類(lèi)型完全一樣,則調(diào)用類(lèi)中的哪個(gè)方法。
【例3.5】方法重寫(xiě)示例。
public class Date2????????????????????//類(lèi)聲明
{
? private int year;???????????????????//成員變量,表示年
??private int month;??????????????? ??//成員變量,表示月
??private int day;????????????????????//成員變量,表示日
?????
??public Date2(int y, int m, int d)???//構(gòu)造方法
??{
????year?= y;
????month = m;
????day?? = d;
??}?
????
??public Date2()????????????????????? //構(gòu)造方法
??{
????year?= 2004;
????month = 8;
????day? ?= 10;
??}?
????
??public void Print()?????????????????//輸出日期值
??{
????System.out.println("date is "+year+'-'+month+'-'+day);
??}?
????
??public void Print(int y)????????????//輸出日期值
??{
????System.out.println("date is "+y+'-'+month+'-'+day);
??}?
????????????
??public static void main(String args[])
??{
????Date2 a = new Date2(2003,1,1) ;???//創(chuàng)建對(duì)象
????Date2 b = new Date2() ;???????????//創(chuàng)建對(duì)象??
????a.Print();
????b.Print(2000);
??}
}
程序運(yùn)行輸出:
date is 2003-1-1
date is 2000-8-10
??? 上述例子中,構(gòu)造方法和Print()方法都重寫(xiě)了兩個(gè)。第一個(gè)構(gòu)造方法有三個(gè)參數(shù),第二個(gè)構(gòu)造方法沒(méi)有一個(gè)參數(shù);第一個(gè)Print()方法沒(méi)有參數(shù),第二個(gè)Print()方法有一個(gè)參數(shù)。在main()方法中,定義對(duì)象a時(shí)有三個(gè)參數(shù),所以和第一個(gè)構(gòu)造方法匹配,定義對(duì)象b時(shí)沒(méi)有參數(shù),所以和第二個(gè)構(gòu)造方法匹配;對(duì)象a調(diào)用方法Print()時(shí)沒(méi)有參數(shù),所以和第一個(gè)Print()方法匹配,對(duì)象b調(diào)用方法Print()時(shí)有一個(gè)參數(shù),所以和第二個(gè)Print()方法匹配。
??? 必須注意的是,方法重寫(xiě)時(shí)必須做到:要么參數(shù)個(gè)數(shù)不同,要么參數(shù)類(lèi)型不同。否則,系統(tǒng)無(wú)法識(shí)別與重寫(xiě)的哪個(gè)方法匹配。但是,如果兩個(gè)重寫(xiě)方法僅返回值的類(lèi)型不同則不允許。
?
?
4、包
??? 面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)特點(diǎn)就是公共類(lèi)資源可以重用。這樣,在設(shè)計(jì)一個(gè)軟件系統(tǒng)過(guò)程中設(shè)計(jì)的許多公共類(lèi)(除包含有main方法的公共類(lèi)外),就可以在以后的軟件系統(tǒng)設(shè)計(jì)中重復(fù)使用。但是,當(dāng)應(yīng)用軟件比較大時(shí),就有許多Java文件,這些Java文件統(tǒng)統(tǒng)放在一個(gè)文件夾中,給以后的軟件資源重用帶來(lái)了許多不便。Java解決此問(wèn)題的方法是包。
??? 包(package)是Java提供的文件(即公共類(lèi))的組織方式。一個(gè)包對(duì)應(yīng)一個(gè)文件夾,一個(gè)包中可以包括許多類(lèi)文件。包中還可以再有子包,稱為包等級(jí)。
????Java語(yǔ)言可以把類(lèi)文件存放在可以有包等級(jí)的不同的包中。這樣,在軟件系統(tǒng)設(shè)計(jì)時(shí),就可以把相關(guān)的一組文件(即相關(guān)的一組公共類(lèi))存放在一個(gè)文件夾中,當(dāng)文件夾太大時(shí),還可以設(shè)計(jì)子文件夾按更細(xì)的分類(lèi)方法存放相關(guān)文件,從而可以大大方便日后的軟件資源重用。Java語(yǔ)言規(guī)定,同一個(gè)包中的文件名必須惟一,不同包中的文件名可以相同。Java語(yǔ)言的包等級(jí)和Windows的文件組織方式完全相同,只是表示方法不同。
?
1、包的建立方法
?
1.定義文件所屬的包
???
簡(jiǎn)單的包的定義語(yǔ)句格式為:
??? package?<包名>;
??? 其中,package是關(guān)鍵字,<包名>是包的標(biāo)識(shí)符。package語(yǔ)句指出了該語(yǔ)句所在文件所有的類(lèi)屬于哪個(gè)包。
??? Java語(yǔ)言規(guī)定,如果一個(gè) Java文件中有package語(yǔ)句,那么package語(yǔ)句必須寫(xiě)在Java源程序的第一行。例如,下面的Java源程序MyClass.java中的類(lèi)MyClass將屬于包MyPackage。
??? package?MyPackage;
??? public class MyClass;
??? {
??? ?……
??? }
?
2.創(chuàng)建包文件夾
???
程序員自定義的包(如前面例子的MyPackage包)必須通知系統(tǒng)其文件夾所在的路徑,這就需要用戶首先創(chuàng)建包的文件夾并設(shè)置包的路徑。創(chuàng)建包文件夾的方法是:
??? (
1)創(chuàng)建與包同名的文件夾
??? 例如,我們可以在D盤(pán)根目錄下創(chuàng)建一個(gè)與包同名的文件夾d:\MyPackage。注意,這里的包名MyPackage必須與package語(yǔ)句后的包名大小寫(xiě)完全一致。
??? (
2)設(shè)置包的路徑
??? 用戶必須通過(guò)設(shè)置環(huán)境變量classpath,把用戶自定義包所在的路徑添加到環(huán)境變量classpath中。例如,作者設(shè)置的包MyPackage其路徑為d:\ ,所以要把d:\添加到環(huán)境變量classpath中。
??? 環(huán)境參數(shù)設(shè)置語(yǔ)句應(yīng)改寫(xiě)為:
??? set classpath=.;c:\jdk1.3.1\lib;d:\;
??? 環(huán)境參數(shù)設(shè)置說(shuō)明:
??? ①分號(hào)(;)用來(lái)分隔開(kāi)各項(xiàng)。因此,上述的設(shè)置共有三項(xiàng)。
??? ②c:\jdk1.3.1\lib是作者計(jì)算機(jī)上安裝的JDK1.3.1版本的系統(tǒng)包的路徑。
??? ③新添加的d:\是用戶自定義包文件夾的上一級(jí)路徑。
??? ④新添加的路徑d:\也可放在圓點(diǎn)(.)(表示當(dāng)前工作路徑)前,則操作時(shí)只需把當(dāng)前路徑下編譯成功的.class文件復(fù)制到自定義包文件夾中;如果路徑d:\放在圓點(diǎn)(.)后,則操作時(shí)需把當(dāng)前路徑下編譯成功的.class文件移動(dòng)到自定義包文件夾。
??? 當(dāng)多個(gè)Java源程序文件中都有package語(yǔ)句,且package語(yǔ)句后的包名相同時(shí),則說(shuō)明這些類(lèi)同屬于一個(gè)包。
??? 一個(gè)包還可以有子包,子包下還可以有子子包。在這種情況下,可以具體指明一個(gè)類(lèi)所屬包的完整路徑。所以,完整的package語(yǔ)句格式為:
??? package??<包名>[.<子包名>[.<子子包名>…]];
??? 其中,在package語(yǔ)句中,圓點(diǎn)(.)起分隔作用;而在Windows的目錄中,圓點(diǎn)(.)和反斜杠(\)等義,即加一個(gè)圓點(diǎn)(.)就表示下一級(jí)目錄。
??? 當(dāng)然,要把一個(gè)類(lèi)放在某個(gè)子包或子子包中,前提條件是已經(jīng)創(chuàng)建了與子包或子子包同名的目錄結(jié)構(gòu)也相同的文件夾。
??? (
3)把編譯生成的.class文件移入包中
??? 用戶的源程序文件(即.java文件)通常存放在另外的文件夾中,.java文件編譯后產(chǎn)生的.class文件也存放在和.java文件相同的文件夾中。用戶在編譯.java文件成功后,需要把編譯成功的.class文件移入用戶自定義的包中。要保證包中有相應(yīng)的.class文件,而當(dāng)前工作目錄下沒(méi)有。
??? 例如,當(dāng)上述的 MyClass.java文件編譯成功后,需要設(shè)計(jì)人員自己把MyClass.class文件移入到d:\ MyPackage文件夾中,否則系統(tǒng)會(huì)因找不到類(lèi)而給出出錯(cuò)信息。
?
2、包的使用方法
?
??? 包中存放的是編譯后的類(lèi)文件(.class文件)。用戶可以在以后的程序中,通過(guò)import語(yǔ)句導(dǎo)入這些類(lèi),從而使用包中的這些類(lèi)。
??? import語(yǔ)句的使用分兩種情況:
????
(1)導(dǎo)入某個(gè)包中的某個(gè)類(lèi);
????
(2)導(dǎo)入某個(gè)包中的全部類(lèi)。
??? 這兩種情況分別用如下兩種形式的import語(yǔ)句:
??? import? MyPackage.MyClass;?//導(dǎo)入包MyPackage中的MyClass類(lèi)
??? import? MyPackage.*;?????? //導(dǎo)入包MyPackage中的全部類(lèi),但不包含其子包
???
要說(shuō)明的是,Java中的包是按類(lèi)似Windows文件的形式組織的,Windows文件用反斜杠(\)表示一條路徑下的子路徑,而Java用圓點(diǎn)(.)表示一個(gè)包的子包。
【例3.6】設(shè)計(jì)一個(gè)日期類(lèi)及其測(cè)試程序。
要求:把日期類(lèi)放在包MyPackage中,以便于以后重復(fù)使用。
程序設(shè)計(jì)如下:
//Date1.java文件
package MyPackage;???????????????????? //定義類(lèi)所屬的包
public class Date1?????????????????????//類(lèi)聲明
{
??public int year,month,day;???????????//成員變量,表示年、月、日
??public Date1(int y, int m, int d)????//構(gòu)造方法
??{
????year = y;
????month = m;
????day = d;
??}?
??public void print()??????????????? ??//輸出日期值
??{
????System.out.println("日期是:" + year + '-' + month +? '-'+day);
??}
}
?
// UseDate1.java文件
import MyPackage.Date1;??????????????? //導(dǎo)入MyPackage中的Date1類(lèi)
public class UseDate1
{
??public static void main(String args[])
??{
????Date1 a = new Date1(2004,5,10) ;? ?//創(chuàng)建對(duì)象
????a.print();
??}
}
程序運(yùn)行結(jié)果:
日期是:2004-5-10
程序設(shè)計(jì)說(shuō)明:因UseDate1.java文件和Date1.java文件不在一個(gè)包中,所以,UseDate1.java文件要用import語(yǔ)句導(dǎo)入文件中使用的類(lèi)。
總結(jié)編寫(xiě)、運(yùn)行上述帶有自定義包Java程序的操作步驟如下:
(1)創(chuàng)建文件夾。如在本地計(jì)算機(jī)的d盤(pán)創(chuàng)建文件夾MyPackage(d:\MyPackage)
(2)在環(huán)境變量中添加自定義包的路徑。如在autoexec.bat文件的classpath參數(shù)中添加d:\(注意:若在Windows98下,則設(shè)置完成后要運(yùn)行一下該批處理文件)
(3)編譯包中類(lèi)的.java文件。如在DOS下執(zhí)行命令:javac Date1.java
(4)把編譯成功的.class文件移入包中。如把當(dāng)前工作路徑下的Date1.class文件移動(dòng)到文件夾d:\MyPackage中
(5)編譯導(dǎo)入包的.java文件。如在DOS下執(zhí)行命令:javac UseDate.java
(6)運(yùn)行導(dǎo)入包的.class文件。如在DOS下執(zhí)行命令:java UseDate
?
?
3、包的訪問(wèn)權(quán)限
?
??? 關(guān)于包中類(lèi)的訪問(wèn)權(quán)限已在3.2.1節(jié)討論,關(guān)于包中類(lèi)的成員變量和方法的訪問(wèn)權(quán)限已在3.2.2節(jié)討論。本節(jié)分相同包中的類(lèi)和不同包中的類(lèi)兩種情況舉例說(shuō)明。
?
1.相同包中的類(lèi)和類(lèi)的成員的訪問(wèn)權(quán)限
【例3.7】相同包中的訪問(wèn)權(quán)限舉例。
程序設(shè)計(jì)如下:
//文件B1.java
package MyPackage;???????? //文件中定義的兩個(gè)類(lèi)都在同一個(gè)包
class C1???????????????????//C1聲明為缺省類(lèi)
{
??int number;????????????? //默認(rèn)成員變量number
??protected int age;? ?????//protected成員變量age
????
??C1(int n, int a)???????? //構(gòu)造方法
??{
????number = n;
????age = a;
??}
??public void output()?????// C1類(lèi)的public方法
??{
????System.out.println("number = " + number + "\n" + "age = " + age);
??}
}
?
public class B1????????????//B1聲明為public類(lèi)??????????
{
? public void output()?????//B1類(lèi)的方法output()
??{
????C1 s1 = new C1(0,0);??//B1類(lèi)可以訪問(wèn)同一個(gè)包中的默認(rèn)類(lèi)C1
????s1.number = 1;???????? //同一包的對(duì)象可以訪問(wèn)默認(rèn)類(lèi)的默認(rèn)成員變量
????s1.age = 25;????????? ?//同一包的對(duì)象可以訪問(wèn)默認(rèn)類(lèi)的protected成員變量
????s1.output();???????? ??//同一包的對(duì)象可以訪問(wèn)默認(rèn)類(lèi)的public方法
??}
}
?
//文件D1.java
//類(lèi)D1在當(dāng)前工作路徑下
import MyPackage.B1;?????? //導(dǎo)入MyPackage包中的B1類(lèi)
public class D1
{
?public static void main(String args[])
?{
????B1 t1 = new B1();
????t1.output();
??}
}
程序的運(yùn)行結(jié)果為:
number = 1
age = 25
程序說(shuō)明:D1類(lèi)中只能定義B1類(lèi)的對(duì)象,不能定義C1類(lèi)的對(duì)象(因C1類(lèi)定義為默認(rèn)類(lèi));但B1類(lèi)中可以定義C1類(lèi)的對(duì)象(因兩個(gè)類(lèi)在同一個(gè)包中)。
?
2.不同包中的類(lèi)和類(lèi)的成員的訪問(wèn)權(quán)限
?
【例3.8】不同包中的訪問(wèn)權(quán)限舉例。
要求:把例3.7中的C1類(lèi)和B1類(lèi)分別放在兩個(gè)不同的包中。
程序設(shè)計(jì)如下:
//文件C2.java
package MyPackage.MyPackage1;
public class C2
{
??public int number;?
??public int age;???
???
??public C2(int n, int a)?
??{
????number = n;
????age = a;
??}
??public void output()?
??{
????System.out.println("number = " + number + "\n" + "age = " + age);
??}
}
?
//文件B2.java
package MyPackage;
import MyPackage.MyPackage1.C2;
public class B2?????????????
{
?public void output()?
??{
????C2 s1 = new C2(0,0);?
????s1.number = 1;??
????s1.age = 25;????
????s1.output();?????
??}
}
?
//文件D2.java
import MyPackage.B2;?
public class D2???????????
{
?public static void main(String args[])
?{
????B2 t1 = new B2();
????t1.output();?
??}
}
程序的運(yùn)行結(jié)果和例3.7的相同。
程序設(shè)計(jì)說(shuō)明:
??? (1)把例3.7程序稍做改變,把C2類(lèi)放在MyPackage.MyPackage1包中(當(dāng)然,要建立相應(yīng)的文件夾),把B2類(lèi)放在MyPackage包中。當(dāng)然,B2.java文件中要用import語(yǔ)句導(dǎo)入C2類(lèi)。此時(shí),由于B2類(lèi)和C2類(lèi)不在同一個(gè)包中,而C2類(lèi)定義為默認(rèn)類(lèi),所以編譯時(shí)語(yǔ)句:
??? C2 s1 = new C2(0,0);將出錯(cuò)
??? 把C2類(lèi)定義為public類(lèi)后,則不會(huì)出現(xiàn)上述錯(cuò)誤。但是,由于B2類(lèi)和C2類(lèi)不在同一個(gè)包中,所以編譯時(shí)語(yǔ)句:
??? s1.number = 1;
??? s1.age = 25;將出錯(cuò)
??? 這是由于C2類(lèi)的age成員變量定義為protected,number定義為默認(rèn),而修飾為protected和默認(rèn)的成員變量不允許其他包中的C2類(lèi)的對(duì)象調(diào)用;當(dāng)把C2類(lèi)的age和numbe成員變量的修飾符改為public,編譯成功。
????
(2)如果把C2類(lèi)放在MyPackage包中,把B2類(lèi)放在MyPackage.MyPackage1包中,則編譯時(shí)會(huì)出錯(cuò)。這是由于JDK規(guī)定:在一個(gè)樹(shù)型結(jié)構(gòu)的包中,上層包可以導(dǎo)入下層包,而下層包不可以導(dǎo)入上層包。在下層包的類(lèi)中要使用上層包的類(lèi)時(shí),要在類(lèi)前面加上包名。
?
4、系統(tǒng)定義的包
?
??? Java語(yǔ)言提供了許多方便用戶程序設(shè)計(jì)的基礎(chǔ)類(lèi),這些系統(tǒng)定義的類(lèi)以包的形式保存在系統(tǒng)包文件夾中。如果要使用這些包中的類(lèi),必須在源程序中用import語(yǔ)句導(dǎo)入。其導(dǎo)入方法和前面介紹的導(dǎo)入自定義包方法類(lèi)同。例如,要進(jìn)行圖形用戶界面設(shè)計(jì),需要導(dǎo)入系統(tǒng)包java.awt中的所有類(lèi),所以要在源程序中使用如下導(dǎo)入語(yǔ)句:
??? import? java.awt.*;?????//導(dǎo)入java.awt包中的所有類(lèi)
??? 又例如,在進(jìn)行圖形用戶界面設(shè)計(jì)時(shí),還需要進(jìn)行事件處理,因此需要導(dǎo)入圖形用戶界面包java.awt中的所有類(lèi)和事件處理包java.awt.event中的所有類(lèi),所以要在源程序中使用如下導(dǎo)入語(yǔ)句:
??? import java.awt.*;?????//導(dǎo)入java.awt包中的所有類(lèi)
??? import java.awt.event.*;???//導(dǎo)入java.awt.event包中的所有類(lèi)
??? 讀者也許會(huì)想,從Java語(yǔ)言包的組織形式看,顯然,java.awt.event包是java.awt包的子包,那么,僅有第一條導(dǎo)入語(yǔ)句似乎就可以了,第二條導(dǎo)入語(yǔ)句似乎沒(méi)有必要。
??? 讀者需要注意:第一條導(dǎo)入語(yǔ)句只導(dǎo)入了java.awt包中的所有類(lèi),并沒(méi)有導(dǎo)入java.awt包的子包,因此,也就沒(méi)有導(dǎo)入子包中的類(lèi)。
?
?
5、內(nèi)部類(lèi)
?
??? 一個(gè)類(lèi)被嵌套定義于另一個(gè)類(lèi)中,稱為內(nèi)部類(lèi)(Inner Classes)或內(nèi)隱類(lèi)。包含內(nèi)部類(lèi)的類(lèi)稱為外部類(lèi)。
??? 內(nèi)部類(lèi)中還有一種更特殊的形式——匿名類(lèi),匿名類(lèi)和內(nèi)部類(lèi)的功能類(lèi)似。這里我們只討論內(nèi)部類(lèi),不討論匿名類(lèi)。
??? 內(nèi)部類(lèi)與前面討論的非內(nèi)部類(lèi)的設(shè)計(jì)方法基本相同,但除外部類(lèi)外的其他類(lèi)無(wú)法訪問(wèn)內(nèi)部類(lèi)。當(dāng)一個(gè)類(lèi)只在某個(gè)類(lèi)中使用,并且不允許除外部類(lèi)外的其他類(lèi)訪問(wèn)時(shí),可考慮把該類(lèi)設(shè)計(jì)成內(nèi)部類(lèi)。
?
【例3.9】設(shè)計(jì)一個(gè)人員類(lèi),要求自動(dòng)生成人員的編號(hào)。
設(shè)計(jì)思想:由于要自動(dòng)生成人員的編號(hào),因此可設(shè)計(jì)一個(gè)static成員變量,當(dāng)每生成一個(gè)對(duì)象時(shí),該成員變量自動(dòng)加1;由于這樣的處理只是作用于外部類(lèi),所以把該處理過(guò)程用內(nèi)部類(lèi)的方法來(lái)實(shí)現(xiàn)。
程序設(shè)計(jì)如下:
public class PeopleCount????????//外部類(lèi)PeopleCount
{
? private String Name;
??private int ID;???????????????//外部類(lèi)的私有成員變量
??private static int count = 0;?//外部類(lèi)的static私有成員變量????
??public class People???????????//內(nèi)部類(lèi)People
??{
????public People()???????????? //內(nèi)部類(lèi)的構(gòu)造方法
????{
??????count++;????????????????? //訪問(wèn)外部類(lèi)的成員變量
??????ID = count;?????????????? //訪問(wèn)外部類(lèi)的成員變量
?????}
?????public void output()?????? //內(nèi)部類(lèi)的方法
?????{
???????System.out.println(Name + "的ID為:" + ID);
?????}
???}
?? public PeopleCount(String sn)?//外部類(lèi)的構(gòu)造方法
???{
?????Name = sn;?
???}
?
???public void output()??????????//外部類(lèi)的方法
???{
???? People p = new People();????//建立內(nèi)部類(lèi)對(duì)象p
?????p.output();?????????????????//通過(guò)p調(diào)用內(nèi)部類(lèi)的方法
???}
???public static void main (String args[])
???{
???? PeopleCount p1 = new PeopleCount("張三");
?????p1.output();
?????PeopleCount p2 = new PeopleCount("李四");
?????p2.output();????????
???}
}
程序的運(yùn)行結(jié)果為:
張三的ID為:1
李四的ID為:2
??? 程序設(shè)計(jì)說(shuō)明:
??? 在外部類(lèi)PeopleCount內(nèi)嵌套定義了一個(gè)內(nèi)部類(lèi)People,當(dāng)定義了外部類(lèi)對(duì)象p1和p2后,調(diào)用p1或p2的方法output時(shí),該方法將首先定義一個(gè)內(nèi)部類(lèi)對(duì)象,內(nèi)部類(lèi)對(duì)象的構(gòu)造方法將先把外部類(lèi)的static成員變量count加1,然后把count的值賦給人員編號(hào)成員變量PeopleID,然后輸出PeopleID的值。
??? 外部類(lèi)與內(nèi)部類(lèi)的訪問(wèn)原則是:在外部類(lèi)中,一般通過(guò)一個(gè)內(nèi)部類(lèi)的對(duì)象來(lái)訪問(wèn)內(nèi)部類(lèi)的成員變量或方法;在內(nèi)部類(lèi)中,可以直接訪問(wèn)外部類(lèi)的所有成員變量和方法(包括靜態(tài)成員變量和方法、實(shí)例成員變量和方法及私有成員變量和方法)。
??? 內(nèi)部類(lèi)具有以下特性:
??內(nèi)部類(lèi)作為外部類(lèi)的成員。Java將內(nèi)部類(lèi)作為外部類(lèi)的一個(gè)成員,因此內(nèi)部類(lèi)可以訪問(wèn)外部類(lèi)的私有成員變量或方法。
??內(nèi)部類(lèi)的類(lèi)名只能用在外部類(lèi)和內(nèi)部類(lèi)自身中。當(dāng)外部類(lèi)引用內(nèi)部類(lèi)時(shí),必須給出完整的名稱,且內(nèi)部類(lèi)的類(lèi)名不能與外部類(lèi)的類(lèi)名相同。
??? 在實(shí)際的Java程序設(shè)計(jì)中,內(nèi)部類(lèi)主要用來(lái)實(shí)現(xiàn)接口。
?
?
6、類(lèi)的封裝性
?
??? 面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言的一個(gè)重要特性是其封裝性,Java語(yǔ)言是按類(lèi)劃分程序模塊的,Java語(yǔ)言很好地實(shí)現(xiàn)了類(lèi)的封裝性。
??? 保證大型軟件設(shè)計(jì)正確性和高效性的一個(gè)重要原則是模塊化軟件設(shè)計(jì)。這需要設(shè)計(jì)許多可重復(fù)使用的模塊,然后在需要使用這些模塊的地方調(diào)用這些模塊。但是,如何劃分好模塊的界限,以保證模塊的正確性是非常重要的問(wèn)題。因?yàn)槿绻橙穗S意修改了已經(jīng)被其他人使用的模塊,必將使程序出錯(cuò),并且這樣的錯(cuò)誤很難發(fā)現(xiàn)和修改。
??? 在面向?qū)ο蟪绦蛟O(shè)計(jì)中,保證模塊正確性的基本方法是類(lèi)的封裝性。類(lèi)的封裝性是指類(lèi)把成員變量和方法封裝為一個(gè)整體,這就劃分了模塊的界限。
??? 保證模塊正確性的措施則是由信息的隱藏性來(lái)實(shí)現(xiàn)的。類(lèi)包括成員變量和方法兩部分。那些允許其他包程序訪問(wèn)和修改的成員變量可以定義為public類(lèi)型。那些只允許同在一個(gè)包中的其他類(lèi)以及該類(lèi)的子類(lèi)訪問(wèn)和修改的成員變量可以定義為protected類(lèi)型。那些不允許其他類(lèi)(內(nèi)部類(lèi)除外)訪問(wèn)和修改的成員變量可以定義為private類(lèi)型。我們說(shuō),private類(lèi)型和protected類(lèi)型的成員變量有效地隱藏了類(lèi)的不能被隨意修改的成員變量信息,從而保證了共享的類(lèi)模塊的正確性。類(lèi)的封裝性和信息的隱藏性是雙胞胎,兩者是結(jié)合在一起的。
??? 同樣,那些允許其他包程序訪問(wèn)的方法可以定義為public類(lèi)型。那些只允許同在一個(gè)包中的其他類(lèi)以及該類(lèi)的子類(lèi)訪問(wèn)的方法可以定義為protected類(lèi)型。那些不允許其他類(lèi)(內(nèi)部類(lèi)除外)訪問(wèn)的方法可以定義為private類(lèi)型。類(lèi)方法的不同類(lèi)型定義,給調(diào)用者提供了權(quán)限明了的調(diào)用接口。
??? 和別的面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言(如C++語(yǔ)言)相比,Java語(yǔ)言增加了包的概念。這樣,同一個(gè)包中類(lèi)之間的信息傳遞就比較方便。
?
7、設(shè)計(jì)舉例
?
??? 本節(jié)給出一個(gè)較為復(fù)雜的程序設(shè)計(jì)舉例。
【例3.10】設(shè)計(jì)一個(gè)包括矩陣加和矩陣減運(yùn)算的矩陣類(lèi),并設(shè)計(jì)一個(gè)測(cè)試程序完成簡(jiǎn)單的測(cè)試。為簡(jiǎn)化設(shè)計(jì)代碼,矩陣的元素值在構(gòu)造方法中用隨機(jī)函數(shù)隨機(jī)給出。
程序設(shè)計(jì)如下:
//MyMatrix.java文件
public class MyMatrix ?????//矩陣類(lèi)MyMatrix
{
?private int[][] table;????//矩陣元素表
? ?private int height; ????//矩陣的行
? ?private int width; ?????//矩陣的列
?
? ?private void init(int m,int n)??//元素隨機(jī)賦值方法
? ?{
??? ??table=new int[m][n];???//分配矩陣元素?cái)?shù)組內(nèi)存空間
??? ??for(int i=0; i<m; i++)
????? ??for(int j=0; j<n; j++)
????? ??{
??????? ???table[i][j]=(int)(Math.random() * 100); //元素隨機(jī)賦值
????? ??}
??}
?
? ?public MyMatrix(int n)????//構(gòu)造方法,構(gòu)造方陣
? ?{
????? ?height = n;??????
????? ?width = n;
????? ?this.init(height,width);??//調(diào)用元素隨機(jī)賦值方法
? ?}
?
? ?public MyMatrix(int m,int n) ??//構(gòu)造方法,構(gòu)造m行n列矩陣
? ?{
??? ??height=m;
??? ??width=n;
??? ??this.init(height,width); ??//調(diào)用元素隨機(jī)賦值方法
? ?}
?
? ?public int getHeight()????//返回矩陣的行數(shù)方法
? ?{
??? ??return height;
? ?}
?
? ?public int getWidth()????//返回矩陣的列數(shù)方法
? ?{
??? ??return width;
? ?}
?
? ?public int[][] getTable()???//返回矩陣方法
? ?{
??? ??return table;
? ?}
?
? ?public MyMatrix add(MyMatrix b)??//矩陣加方法
??? {??? ?
if(this.getHeight()!=b.getHeight()&&
this.getWidth()!=b.getWidth())
??? ??{
????? ??System.out.println("the two matrix don't macth");
????? ??return null;
??? ??}
??? ??MyMatrix result=new MyMatrix(b.getHeight(),b.getWidth());
??? ??for(int i=0;i<b.getHeight();i++)
????? ??for(int j=0;j<b.getWidth();j++)
????? ??{
??????? ???result.table[i][j]=this.table[i][j]+b.table[i][j];
????? ??}
??? ??return result;
? ?}
?
? ?public MyMatrix subtract(MyMatrix b) ???//矩陣減方法
? ?{
??? ??if(this.getHeight()!=b.getHeight()&&
this.getWidth()!=b.getWidth())
??? ??{
????? ??System.out.println("the two matrix don't macth");
????? ??return null;
??? ??}
?
??? ??MyMatrix result=new MyMatrix(b.getHeight(),b.getWidth());
??? ??for(int i=0;i<b.getHeight();i++)
????? ??for(int j=0;j<b.getWidth();j++)
????? ??{
??????? ???result.table[i][j]=this.table[i][j]-b.table[i][j];
????? ??}
??? ??return result;
? ?}
}
?
// TestMyMatrix.java文件
public class TestMyMatrix ???????//測(cè)試類(lèi)
{
? ?public static void main(String[] args)
? ?{
?? ??MyMatrix mm1=new MyMatrix(4,4);
?? ??MyMatrix mm2=new MyMatrix(4,4);
?? ??MyMatrix mm3=new MyMatrix(4,5);
?? ??MyMatrix mm4=new MyMatrix(4,5);
?
?? ??MyMatrix add_result=mm1.add(mm2);
?? ??int[][] add_table=add_result.getTable();
?? ??MyMatrix subtract_result=mm3.subtract(mm4);
?? ??int[][] subtract_table=subtract_result.getTable();
?
?? ??System.out.println("two matrix add result:");
?? ??for(int i=0;i<add_result.getHeight();i++)
?? ??{
???? ??for(int j=0;j<add_result.getWidth();j++)
???? ??{
?????? ???System.out.print(add_table[i][j]+"? ");
???? ??}
???? ??System.out.println();
?? ??}
?
?? ??System.out.println("two matrix subtract result:");
? ??for(int i=0;i<subtract_result.getHeight();i++)
? ??{
??? ???for(int j=0;j<subtract_result.getWidth();j++)
??? ???{
????? ???System.out.print(subtract_table[i][j]+"? ");
??? ???}
??? ???System.out.println();
? ??}
? ?}
}
程序運(yùn)行結(jié)果如下:
two matrix add result:
67? 94? 130? 78
21? 171? 78? 104
47? 100? 84? 111
125? 152? 98? 61
two matrix subtract result:
-15? 88? 37? -25? -21
56? -5? 32? 40? 41
-56? 31? -75? -21? -4
-17? -46? -18? 2? -28
-The End-