Java學(xué)習(xí)(二).類和對(duì)象
?
?
??? Java語(yǔ)言是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,類和對(duì)象是面向?qū)ο蟪绦蛟O(shè)計(jì)的基本概念。類是相似對(duì)象中共同屬性和方法的集合體。對(duì)象是類的實(shí)例。包是Java組織和管理類的一種方法。類的封裝性是面向?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ì)方法和人類的思維方法于一體的、貫穿軟件系統(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í)體以接近人類思維的方式進(jìn)行自然分割,然后對(duì)客觀實(shí)體進(jìn)行結(jié)構(gòu)模擬和功能模擬,從而使設(shè)計(jì)出的軟件盡可能直接地描述客觀實(shí)體,從而構(gòu)造出模塊化的、可重用的、維護(hù)方便的軟件。
??? 在現(xiàn)實(shí)世界中,客觀實(shí)體有兩大類:
??? (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ì)成稱為類的可重復(fù)使用的模塊,然后定義出一個(gè)個(gè)實(shí)例化的類(稱為對(duì)象),再按照問(wèn)題的要求,設(shè)計(jì)出用各個(gè)對(duì)象的操作完成的軟件系統(tǒng)。
?
2.類
???
在現(xiàn)實(shí)世界中,類這個(gè)術(shù)語(yǔ)是對(duì)一組相似客觀實(shí)體的抽象描述。例如,有A書(shū)店、B書(shū)店、C書(shū)店等。而書(shū)店類則是對(duì)書(shū)店這類客觀實(shí)體所應(yīng)具有的共同屬性和方法的抽象描述。即書(shū)店類不是具體的描述,而是抽象的描述。
??? 在面向?qū)ο蠓椒ㄖ校唧w的客觀實(shí)體稱為對(duì)象,類是對(duì)具有相同屬性和相同方法的一組相似對(duì)象的抽象,或者說(shuō),類所包含的屬性和方法描述了一組對(duì)象的共同的屬性和方法。
??? 面向?qū)ο蠓椒ㄖ熊浖O(shè)計(jì)的主體是類。類是相同屬性和方法的封裝體,因此類具有封裝性;子類可以在繼承父類所有屬性和方法的基礎(chǔ)上,再增加自己特有的屬性和方法,因此類具有繼承性;在一個(gè)類層次中,定義為根類的對(duì)象可被賦值為其任何子類的對(duì)象,并根據(jù)子類對(duì)象的不同而調(diào)用不同的方法,因此類具有多態(tài)性。類的這種封裝性、多態(tài)性和繼承性,是面向?qū)ο蟪绦蛟O(shè)計(jì)的三個(gè)最重要的特點(diǎn)。
?
3.對(duì)象
???
類是具有相同屬性和方法的一組相似對(duì)象的抽象描述,但在現(xiàn)實(shí)世界中,抽象描述的類是并不具體存在的,例如,現(xiàn)實(shí)世界中只存在具體的A書(shū)店、B書(shū)店、C書(shū)店,并不存在抽象的書(shū)店。我們把按照類這個(gè)模板所定義的一個(gè)個(gè)具體的對(duì)象稱作類的實(shí)例,或稱作對(duì)象。
??? 首先,一個(gè)具體對(duì)象必須具有具體的屬性值,如A書(shū)店對(duì)象就必須具有如下屬性:書(shū)點(diǎn)名為A書(shū)店,負(fù)責(zé)人為張三等。其次,任何對(duì)象都具有相應(yīng)類所規(guī)定的所有方法。
?
?
2、類
??? 面向?qū)ο蠓椒ㄖ校浖O(shè)計(jì)的最主要部分是設(shè)計(jì)類。類的設(shè)計(jì)可以劃分為類聲明和類主體設(shè)計(jì)兩部分。
?
1、類聲明
?
1.類聲明的格式
???
類聲明的格式如下:
??? [<修飾符>] class<類名>[extends<父類名>] [implements<接口名表>]
??? {
????? 類主體
??? }
???
其中,class是定義類的關(guān)鍵字,<類名>是所定義的類的名字,<父類名>是已經(jīng)定義過(guò)的類名,<接口名表>是已經(jīng)定義過(guò)的若干個(gè)接口名,當(dāng)接口名多于一個(gè)時(shí),用逗號(hào)分隔開(kāi)。方括號(hào)表示該項(xiàng)是可選項(xiàng)。本章只討論基本的類設(shè)計(jì)方法,含有父類和接口的類將在下一章討論。
?
2.類的修飾符
??? 類聲明的<修飾符>分為訪問(wèn)控制符和類型說(shuō)明符兩部分,分別用來(lái)說(shuō)明該類的訪問(wèn)權(quán)限以及該類是否為抽象類或最終類。
??? (
1)訪問(wèn)控制符public和默認(rèn)
??? 當(dāng)訪問(wèn)控制符為public時(shí),表示該類被定義為公共類。公共類表示該類能被任何類訪問(wèn)。由于類都放于某個(gè)包中,包中的類互相能訪問(wèn),而不在一個(gè)包中的類互相不能直接訪問(wèn)。如果要在一個(gè)包中訪問(wèn)另一個(gè)包中的類,就必須用import語(yǔ)句導(dǎo)入所需要的類到該包中,但Java語(yǔ)言規(guī)定,被導(dǎo)入的類必須是用public修飾的類。
??? 當(dāng)沒(méi)有訪問(wèn)控制符public時(shí),即是默認(rèn)類(或稱缺省類)。默認(rèn)類表示該類只能被同一個(gè)包中的類訪問(wèn),而不能被其他包中的類訪問(wèn)。Java語(yǔ)言規(guī)定,一個(gè)Java文件中可以有很多類,但最多只能有一個(gè)公共類,其他都必須定義為默認(rèn)類。
??? 例如: public? class? Teacher就聲明了一個(gè)公共類Teacher,該類可以通過(guò)import語(yǔ)句導(dǎo)入到其他包的類中,并能被其他所有的類訪問(wèn)。又例如,class Student就聲明了一個(gè)默認(rèn)類Student,該類只能被同一個(gè)包中的其他類訪問(wèn)。
??? (
2)類型說(shuō)明符abstract和final
??? 當(dāng)類型說(shuō)明符為abstract時(shí),表示該類為抽象類,抽象類不能用來(lái)定義對(duì)象,抽象類通常設(shè)計(jì)成一些具有類似成員變量和方法的子類的父類。
??? 當(dāng)類型說(shuō)明符為final時(shí),表示該類為最終類,最終類不能用來(lái)再派生子類。
??? 訪問(wèn)控制符和類型說(shuō)明符一起使用時(shí),訪問(wèn)控制符在前,類型說(shuō)明符在后。例如,public abstract class Teacher就聲明了一個(gè)公共抽象類Teacher。
?
?
2、類主體設(shè)計(jì)
???
在上節(jié)討論面向?qū)ο蟪绦蛟O(shè)計(jì)時(shí)曾說(shuō)過(guò),類由成員變量和成員方法組成。因此,類主體設(shè)計(jì)包括類的成員變量設(shè)計(jì)和類的成員方法設(shè)計(jì)兩部分。由于Java語(yǔ)言中的所有方法必定屬于某個(gè)類,即方法一定是成員方法,所以成員方法可簡(jiǎn)稱為方法。我們首先給出一個(gè)簡(jiǎn)單的日期類設(shè)計(jì),然后分別討論類成員變量和類方法的設(shè)計(jì)。
【例3.1】設(shè)計(jì)一個(gè)日期類。要求方法應(yīng)包括設(shè)置日期、顯示日期和判斷是否是閏年。
類設(shè)計(jì)如下:
public class Date?????????????????? //類聲明
{
? //以下為類成員變量聲明
? private int year;?????????????????? //成員變量,表示年
? private int month;????????????????? //成員變量,表示月
? private int day;??????????????????? //成員變量,表示日
???
? //以下為類方法聲明和實(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ù)類型,同時(shí)指定其他一些附加特性。聲明成員變量的格式為:
??? [<修飾符>] [static] [final] [transient] <變量類型>? <變量名>;
??? 其中,<修飾符>有private、public和protected三種。當(dāng)不加任何修飾符時(shí),定義為默認(rèn)修飾符。
???
private修飾符表示該成員變量只能被該類本身訪問(wèn),任何其他類都不能訪問(wèn)該成員變量。
???
protected修飾符表示該成員變量除可以被該類本身和同一個(gè)包的類訪問(wèn)外,還可以被它的子類(包括同一個(gè)包中的子類和不同包中的子類)訪問(wèn)。
???
public修飾符表示該成員變量可以被所有類訪問(wèn)。
??? 不加任何訪問(wèn)權(quán)限限定的成員變量屬于默認(rèn)訪問(wèn)權(quán)限。默認(rèn)訪問(wèn)權(quán)限表示該成員變量只能被該類本身和同一個(gè)包的類訪問(wèn)。
??? 上述修飾符實(shí)現(xiàn)了類中成員變量在一定范圍內(nèi)的信息隱藏。這既符合程序設(shè)計(jì)中隱藏內(nèi)部信息處理細(xì)節(jié)的原則,也有利于數(shù)據(jù)的安全性。
???
static指明該成員變量是一個(gè)類成員變量
???
final指明該成員變量是常量
???
transient指明該成員變量是臨時(shí)變量。
transient很少使用。
??? 類成員變量是一個(gè)類的所有對(duì)象共同擁有的成員變量。關(guān)于類成員變量的用途和使用方法我們將通過(guò)后面的例子說(shuō)明。
??? 因此,例3.1中的成員變量year、month和day都是int類型的private成員變量,即這三個(gè)成員變量只能被該類本身訪問(wèn),任何其他類都不能訪問(wèn)該成員變量。
?
2.聲明方法
???
聲明成員方法的格式為:
??? [<修飾符>] [static] <返回值類型>?<方法名>? ([<參數(shù)列表>])
??? {
? ??? <方法體>
??? }
???
其中,<修飾符>和成員變量的修飾符一樣,有private、public和protected三種,另外,還有默認(rèn)。
??? 各個(gè)修飾符的含義也和成員變量修飾符的含義相同。static指明該方法是一個(gè)類方法。關(guān)于類方法的用途和使用方法我們將通過(guò)后面的例子說(shuō)明。
??? 方法聲明中必須給出方法名和方法的返回值類型,如果沒(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ù)類型均為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ū)別方法是:定義在類中的都是成員變量,定義在方法內(nèi)的都是變量。另外,還有定義在方法參數(shù)中的虛參變量,如例3.1 SetDate()中的y、m和 d。關(guān)于方法中定義的變量的例子見(jiàn)后面的設(shè)計(jì)舉例。
??? 成員變量和變量的類型既可以是基本數(shù)據(jù)類型(如int、long等),也可以是已定義的類。
?
3、構(gòu)造方法
??? 在類的方法中,有一種特殊的方法專門(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è)類中至少要有一個(gè)構(gòu)造方法。
??? 構(gòu)造方法在語(yǔ)法上等同于其他方法。因此構(gòu)造方法的設(shè)計(jì)方法和前面說(shuō)的其他方法的設(shè)計(jì)方法類同。但構(gòu)造方法的名字必須與其類名完全相同,并且沒(mé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)類中的構(gòu)造方法為對(duì)象的成員變量進(jìn)行初始化賦值。
??? 例如,我們可以為上述日期類設(shè)計(jì)一個(gè)如下的構(gòu)造方法:
??? public Date(int y, int m, int d)????????? //構(gòu)造方法
??? {
? ??? year = y;
? ??? month = m;
? ??? day = d;
??? }
?
?
3、對(duì)象
??? 類是一類相似對(duì)象的抽象描述,一個(gè)軟件系統(tǒng)是對(duì)具體問(wèn)題的客觀事物進(jìn)行模擬或描述的,因此需要具體的描述。對(duì)象是類的實(shí)例化,對(duì)象就是軟件系統(tǒng)中對(duì)具體問(wèn)題的客觀事物進(jìn)行的具體模擬或具體描述。
?
1、main方法
?
??? 類是作為許多相似對(duì)象的抽象描述設(shè)計(jì)的,如果要使用這個(gè)類時(shí),就必須創(chuàng)建這個(gè)類的對(duì)象。那么,對(duì)象創(chuàng)建應(yīng)該是在同一個(gè)類中呢?還是應(yīng)該在另一個(gè)類中呢?答案是兩者都可以,但最好是在另一個(gè)類中。這樣沒(méi)有對(duì)象定義的純粹的類設(shè)計(jì)部分就可以單獨(dú)保存在一個(gè)文件中,就不會(huì)影響該類的重復(fù)使用。
??? Java語(yǔ)言規(guī)定,一個(gè)類對(duì)應(yīng)一個(gè).class文件,一個(gè)程序文件中可以包含一個(gè)或一個(gè)以上的類,但其中只允許一個(gè)類被定義成public類型。類中可以沒(méi)有main方法。但是要運(yùn)行的類中必須有main方法。程序就是從main方法開(kāi)始執(zhí)行的。
??? 下面的例子是把對(duì)象創(chuàng)建在同一個(gè)類中的main方法中。
【例3.2】打印某個(gè)日期,并判斷該年是否是閏年。
public class Date???????????????????? //類聲明
{
? 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;
?? }
???
? //以下為其他類方法
? 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方法必須放在類中,且格式必須為: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ǔ)句格式為:
??? <類名>?<對(duì)象名>;
??? 例如下面語(yǔ)句就定義了一個(gè)Date類的對(duì)象a:
??? Date a;
??? 對(duì)象和數(shù)組一樣,也是引用類型。即對(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 <類名> ([<參數(shù)列表>]);
??? 其中,new運(yùn)算符申請(qǐng)了對(duì)象所需的內(nèi)存空間,new運(yùn)算符返回所申請(qǐng)的內(nèi)存空間的首地址。系統(tǒng)將根據(jù)<類名>和<參數(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)作為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類對(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ì)象和變量在很多方面有些類同,例如,對(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í)例成員變量與類成員變量
???
類有兩種不同類型的成員變量:實(shí)例成員變量與類成員變量。類成員變量也稱作靜態(tài)成員變量。
?
??? (1)
實(shí)例成員變量
??? 類定義中沒(méi)用關(guān)鍵字static修飾的成員變量就是實(shí)例成員變量,不同對(duì)象的實(shí)例成員變量其值不相同。
??? 例3.2中類Date的成員變量定義語(yǔ)句:
??? private int year;
??? private int month;
??? private int day;
??? 就定義了三個(gè)private類型的實(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)
類成員變量
??? 用關(guān)鍵字static修飾的成員變量稱為類成員變量,一個(gè)類的所有對(duì)象共享該類的類成員變量。類成員變量可以用來(lái)保存和類相關(guān)的信息,或用來(lái)在一個(gè)類的對(duì)象間交流信息。
【例3.3】用類成員變量表示當(dāng)前該類共有多少個(gè)對(duì)象被定義。
public class ObjectNumber???? //類聲明
{
? private static int number = 0;?? //類成員變量
???
? 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í)例方法與類方法
???
類有兩種不同類型的方法:實(shí)例方法與類方法。類方法也稱作靜態(tài)方法。
??? (
1)實(shí)例方法
??? 沒(méi)用關(guān)鍵字static修飾的方法就是實(shí)例方法。實(shí)例方法只能通過(guò)對(duì)象來(lái)調(diào)用。實(shí)例方法既可以訪問(wèn)類成員變量,也可以訪問(wèn)類變量。例3.2中類Date的SetDate()方法、 Print()方法和IsLeapYear()方法都是實(shí)例方法。這些實(shí)例方法體中都訪問(wèn)了實(shí)例成員變量。
??? (2)
類方法
???
用關(guān)鍵字static修飾的方法稱為類方法。類方法通過(guò)類名來(lái)調(diào)用(也可以通過(guò)對(duì)象來(lái)調(diào)用)。類方法只能訪問(wèn)類變量,不能訪問(wèn)實(shí)例變量。類方法主要用來(lái)處理和整個(gè)類相關(guān)的數(shù)據(jù)。雖然類方法既可以用類名來(lái)調(diào)用,也可以用對(duì)象來(lái)調(diào)用,但用類名調(diào)用類方法程序的可讀性更好。
【例3.4】用類方法處理當(dāng)前該類共有多少個(gè)對(duì)象被定義。
程序設(shè)計(jì)如下:
public class ObjectNumber2???? ??? //類聲明
{
? private static int number = 0;?? //類成員變量
???
? public ObjectNumber2()???? ????? //構(gòu)造方法
? {
??? number++;
? }
???
? public static void Print()??? ?? //類方法
? {
??? 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ě)
???
類的各種方法(包括構(gòu)造方法和其他方法)都允許重寫(xiě)(也稱做重載)。所謂方法重寫(xiě)(overloading),是指一個(gè)方法名定義了多個(gè)方法實(shí)現(xiàn)。方法重寫(xiě)時(shí)要求,不同的方法,其參數(shù)類型或參數(shù)個(gè)數(shù)要有所不同。若類的某個(gè)方法被重寫(xiě),該類的對(duì)象在訪問(wèn)該方法時(shí),以對(duì)象調(diào)用該方法的參數(shù)個(gè)數(shù)和參數(shù)類型與類的同名方法進(jìn)行匹配,對(duì)象調(diào)用該方法的參數(shù)個(gè)數(shù)和參數(shù)類型與類定義中哪個(gè)方法的參數(shù)個(gè)數(shù)和參數(shù)類型完全一樣,則調(diào)用類中的哪個(gè)方法。
【例3.5】方法重寫(xiě)示例。
public class Date2??????????????????? //類聲明
{
? 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ù)類型不同。否則,系統(tǒng)無(wú)法識(shí)別與重寫(xiě)的哪個(gè)方法匹配。但是,如果兩個(gè)重寫(xiě)方法僅返回值的類型不同則不允許。
?
?
4、包
??? 面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)特點(diǎn)就是公共類資源可以重用。這樣,在設(shè)計(jì)一個(gè)軟件系統(tǒng)過(guò)程中設(shè)計(jì)的許多公共類(除包含有main方法的公共類外),就可以在以后的軟件系統(tǒng)設(shè)計(jì)中重復(fù)使用。但是,當(dāng)應(yīng)用軟件比較大時(shí),就有許多Java文件,這些Java文件統(tǒng)統(tǒng)放在一個(gè)文件夾中,給以后的軟件資源重用帶來(lái)了許多不便。Java解決此問(wèn)題的方法是包。
??? 包(package)是Java提供的文件(即公共類)的組織方式。一個(gè)包對(duì)應(yīng)一個(gè)文件夾,一個(gè)包中可以包括許多類文件。包中還可以再有子包,稱為包等級(jí)。
??? Java語(yǔ)言可以把類文件存放在可以有包等級(jí)的不同的包中。這樣,在軟件系統(tǒng)設(shè)計(jì)時(shí),就可以把相關(guān)的一組文件(即相關(guān)的一組公共類)存放在一個(gè)文件夾中,當(dāng)文件夾太大時(shí),還可以設(shè)計(jì)子文件夾按更細(xì)的分類方法存放相關(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ǔ)句所在文件所有的類屬于哪個(gè)包。
??? Java語(yǔ)言規(guī)定,如果一個(gè) Java文件中有package語(yǔ)句,那么package語(yǔ)句必須寫(xiě)在Java源程序的第一行。例如,下面的Java源程序MyClass.java中的類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ō)明這些類同屬于一個(gè)包。
??? 一個(gè)包還可以有子包,子包下還可以有子子包。在這種情況下,可以具體指明一個(gè)類所屬包的完整路徑。所以,完整的package語(yǔ)句格式為:
??? package? <包名>[.<子包名>[.<子子包名>…]];
??? 其中,在package語(yǔ)句中,圓點(diǎn)(.)起分隔作用;而在Windows的目錄中,圓點(diǎn)(.)和反斜杠(\)等義,即加一個(gè)圓點(diǎn)(.)就表示下一級(jí)目錄。
??? 當(dāng)然,要把一個(gè)類放在某個(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ì)因找不到類而給出出錯(cuò)信息。
?
2、包的使用方法
?
??? 包中存放的是編譯后的類文件(.class文件)。用戶可以在以后的程序中,通過(guò)import語(yǔ)句導(dǎo)入這些類,從而使用包中的這些類。
??? import語(yǔ)句的使用分兩種情況:
???
(1)導(dǎo)入某個(gè)包中的某個(gè)類;
???
(2)導(dǎo)入某個(gè)包中的全部類。
??? 這兩種情況分別用如下兩種形式的import語(yǔ)句:
??? import? MyPackage.MyClass; //導(dǎo)入包MyPackage中的MyClass類
??? import? MyPackage.*;? ???? //導(dǎo)入包MyPackage中的全部類,但不包含其子包
???
要說(shuō)明的是,Java中的包是按類似Windows文件的形式組織的,Windows文件用反斜杠(\)表示一條路徑下的子路徑,而Java用圓點(diǎn)(.)表示一個(gè)包的子包。
【例3.6】設(shè)計(jì)一個(gè)日期類及其測(cè)試程序。
要求:把日期類放在包MyPackage中,以便于以后重復(fù)使用。
程序設(shè)計(jì)如下:
//Date1.java文件
package MyPackage;?????? ????????????? //定義類所屬的包
public class Date1???????????????????? //類聲明
{
? 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類
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)入文件中使用的類。
總結(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)編譯包中類的.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)于包中類的訪問(wèn)權(quán)限已在3.2.1節(jié)討論,關(guān)于包中類的成員變量和方法的訪問(wèn)權(quán)限已在3.2.2節(jié)討論。本節(jié)分相同包中的類和不同包中的類兩種情況舉例說(shuō)明。
?
1.相同包中的類和類的成員的訪問(wèn)權(quán)限
【例3.7】相同包中的訪問(wèn)權(quán)限舉例。
程序設(shè)計(jì)如下:
//文件B1.java
package MyPackage;???? ??? //文件中定義的兩個(gè)類都在同一個(gè)包
class C1?????????????????? //C1聲明為缺省類
{
? 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類的public方法
? {
??? System.out.println("number = " + number + "\n" + "age = " + age);
? }
}
?
public class B1??????????? //B1聲明為public類??????????
{
? public void output()???? //B1類的方法output()
? {
??? C1 s1 = new C1(0,0);? //B1類可以訪問(wèn)同一個(gè)包中的默認(rèn)類C1
??? s1.number = 1;?? ????? //同一包的對(duì)象可以訪問(wèn)默認(rèn)類的默認(rèn)成員變量
??? s1.age = 25;???? ???? //同一包的對(duì)象可以訪問(wèn)默認(rèn)類的protected成員變量
??? s1.output();?????? ? ? //同一包的對(duì)象可以訪問(wèn)默認(rèn)類的public方法
? }
}
?
//文件D1.java
//類D1在當(dāng)前工作路徑下
import MyPackage.B1;???? ? //導(dǎo)入MyPackage包中的B1類
public class D1
{
?public static void main(String args[])
?{
??? B1 t1 = new B1();
??? t1.output();
? }
}
程序的運(yùn)行結(jié)果為:
number = 1
age = 25
程序說(shuō)明:D1類中只能定義B1類的對(duì)象,不能定義C1類的對(duì)象(因C1類定義為默認(rèn)類);但B1類中可以定義C1類的對(duì)象(因兩個(gè)類在同一個(gè)包中)。
?
2.不同包中的類和類的成員的訪問(wèn)權(quán)限
?
【例3.8】不同包中的訪問(wèn)權(quán)限舉例。
要求:把例3.7中的C1類和B1類分別放在兩個(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類放在MyPackage.MyPackage1包中(當(dāng)然,要建立相應(yīng)的文件夾),把B2類放在MyPackage包中。當(dāng)然,B2.java文件中要用import語(yǔ)句導(dǎo)入C2類。此時(shí),由于B2類和C2類不在同一個(gè)包中,而C2類定義為默認(rèn)類,所以編譯時(shí)語(yǔ)句:
??? C2 s1 = new C2(0,0);將出錯(cuò)
??? 把C2類定義為public類后,則不會(huì)出現(xiàn)上述錯(cuò)誤。但是,由于B2類和C2類不在同一個(gè)包中,所以編譯時(shí)語(yǔ)句:
??? s1.number = 1;
??? s1.age = 25;將出錯(cuò)
??? 這是由于C2類的age成員變量定義為protected,number定義為默認(rèn),而修飾為protected和默認(rèn)的成員變量不允許其他包中的C2類的對(duì)象調(diào)用;當(dāng)把C2類的age和numbe成員變量的修飾符改為public,編譯成功。
???
(2)如果把C2類放在MyPackage包中,把B2類放在MyPackage.MyPackage1包中,則編譯時(shí)會(huì)出錯(cuò)。這是由于JDK規(guī)定:在一個(gè)樹(shù)型結(jié)構(gòu)的包中,上層包可以導(dǎo)入下層包,而下層包不可以導(dǎo)入上層包。在下層包的類中要使用上層包的類時(shí),要在類前面加上包名。
?
4、系統(tǒng)定義的包
?
??? Java語(yǔ)言提供了許多方便用戶程序設(shè)計(jì)的基礎(chǔ)類,這些系統(tǒng)定義的類以包的形式保存在系統(tǒng)包文件夾中。如果要使用這些包中的類,必須在源程序中用import語(yǔ)句導(dǎo)入。其導(dǎo)入方法和前面介紹的導(dǎo)入自定義包方法類同。例如,要進(jìn)行圖形用戶界面設(shè)計(jì),需要導(dǎo)入系統(tǒng)包java.awt中的所有類,所以要在源程序中使用如下導(dǎo)入語(yǔ)句:
??? import? java.awt.*;???? //導(dǎo)入java.awt包中的所有類
??? 又例如,在進(jìn)行圖形用戶界面設(shè)計(jì)時(shí),還需要進(jìn)行事件處理,因此需要導(dǎo)入圖形用戶界面包java.awt中的所有類和事件處理包java.awt.event中的所有類,所以要在源程序中使用如下導(dǎo)入語(yǔ)句:
??? import java.awt.*;???? //導(dǎo)入java.awt包中的所有類
??? import java.awt.event.*;?? //導(dǎo)入java.awt.event包中的所有類
??? 讀者也許會(huì)想,從Java語(yǔ)言包的組織形式看,顯然,java.awt.event包是java.awt包的子包,那么,僅有第一條導(dǎo)入語(yǔ)句似乎就可以了,第二條導(dǎo)入語(yǔ)句似乎沒(méi)有必要。
??? 讀者需要注意:第一條導(dǎo)入語(yǔ)句只導(dǎo)入了java.awt包中的所有類,并沒(méi)有導(dǎo)入java.awt包的子包,因此,也就沒(méi)有導(dǎo)入子包中的類。
?
?
5、內(nèi)部類
?
??? 一個(gè)類被嵌套定義于另一個(gè)類中,稱為內(nèi)部類(Inner Classes)或內(nèi)隱類。包含內(nèi)部類的類稱為外部類。
??? 內(nèi)部類中還有一種更特殊的形式——匿名類,匿名類和內(nèi)部類的功能類似。這里我們只討論內(nèi)部類,不討論匿名類。
??? 內(nèi)部類與前面討論的非內(nèi)部類的設(shè)計(jì)方法基本相同,但除外部類外的其他類無(wú)法訪問(wèn)內(nèi)部類。當(dāng)一個(gè)類只在某個(gè)類中使用,并且不允許除外部類外的其他類訪問(wèn)時(shí),可考慮把該類設(shè)計(jì)成內(nèi)部類。
?
【例3.9】設(shè)計(jì)一個(gè)人員類,要求自動(dòng)生成人員的編號(hào)。
設(shè)計(jì)思想:由于要自動(dòng)生成人員的編號(hào),因此可設(shè)計(jì)一個(gè)static成員變量,當(dāng)每生成一個(gè)對(duì)象時(shí),該成員變量自動(dòng)加1;由于這樣的處理只是作用于外部類,所以把該處理過(guò)程用內(nèi)部類的方法來(lái)實(shí)現(xiàn)。
程序設(shè)計(jì)如下:
public class PeopleCount??? ??? //外部類PeopleCount
{
? private String Name;
? private int ID;?????????????? //外部類的私有成員變量
? private static int count = 0; //外部類的static私有成員變量???
? public class People?????????? //內(nèi)部類People
? {
??? public People()???? ??????? //內(nèi)部類的構(gòu)造方法
??? {
????? count++;????? ??????????? //訪問(wèn)外部類的成員變量
????? ID = count;???? ????????? //訪問(wèn)外部類的成員變量
???? }
???? public void output()?? ??? //內(nèi)部類的方法
???? {
?????? System.out.println(Name + "的ID為:" + ID);
???? }
?? }
?? public PeopleCount(String sn) //外部類的構(gòu)造方法
?? {
???? Name = sn;
?? }
?
?? public void output()????????? //外部類的方法
?? {
???? People p = new People();??? //建立內(nèi)部類對(duì)象p
???? p.output();???????????????? //通過(guò)p調(diào)用內(nè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ō)明:
??? 在外部類PeopleCount內(nèi)嵌套定義了一個(gè)內(nèi)部類People,當(dāng)定義了外部類對(duì)象p1和p2后,調(diào)用p1或p2的方法output時(shí),該方法將首先定義一個(gè)內(nèi)部類對(duì)象,內(nèi)部類對(duì)象的構(gòu)造方法將先把外部類的static成員變量count加1,然后把count的值賦給人員編號(hào)成員變量PeopleID,然后輸出PeopleID的值。
??? 外部類與內(nèi)部類的訪問(wèn)原則是:在外部類中,一般通過(guò)一個(gè)內(nèi)部類的對(duì)象來(lái)訪問(wèn)內(nèi)部類的成員變量或方法;在內(nèi)部類中,可以直接訪問(wèn)外部類的所有成員變量和方法(包括靜態(tài)成員變量和方法、實(shí)例成員變量和方法及私有成員變量和方法)。
??? 內(nèi)部類具有以下特性:
? 內(nèi)部類作為外部類的成員。Java將內(nèi)部類作為外部類的一個(gè)成員,因此內(nèi)部類可以訪問(wèn)外部類的私有成員變量或方法。
? 內(nèi)部類的類名只能用在外部類和內(nèi)部類自身中。當(dāng)外部類引用內(nèi)部類時(shí),必須給出完整的名稱,且內(nèi)部類的類名不能與外部類的類名相同。
??? 在實(shí)際的Java程序設(shè)計(jì)中,內(nèi)部類主要用來(lái)實(shí)現(xiàn)接口。
?
?
6、類的封裝性
?
??? 面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言的一個(gè)重要特性是其封裝性,Java語(yǔ)言是按類劃分程序模塊的,Java語(yǔ)言很好地實(shí)現(xiàn)了類的封裝性。
??? 保證大型軟件設(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ì)中,保證模塊正確性的基本方法是類的封裝性。類的封裝性是指類把成員變量和方法封裝為一個(gè)整體,這就劃分了模塊的界限。
??? 保證模塊正確性的措施則是由信息的隱藏性來(lái)實(shí)現(xiàn)的。類包括成員變量和方法兩部分。那些允許其他包程序訪問(wèn)和修改的成員變量可以定義為public類型。那些只允許同在一個(gè)包中的其他類以及該類的子類訪問(wèn)和修改的成員變量可以定義為protected類型。那些不允許其他類(內(nèi)部類除外)訪問(wèn)和修改的成員變量可以定義為private類型。我們說(shuō),private類型和protected類型的成員變量有效地隱藏了類的不能被隨意修改的成員變量信息,從而保證了共享的類模塊的正確性。類的封裝性和信息的隱藏性是雙胞胎,兩者是結(jié)合在一起的。
??? 同樣,那些允許其他包程序訪問(wèn)的方法可以定義為public類型。那些只允許同在一個(gè)包中的其他類以及該類的子類訪問(wèn)的方法可以定義為protected類型。那些不允許其他類(內(nèi)部類除外)訪問(wèn)的方法可以定義為private類型。類方法的不同類型定義,給調(diào)用者提供了權(quán)限明了的調(diào)用接口。
??? 和別的面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言(如C++語(yǔ)言)相比,Java語(yǔ)言增加了包的概念。這樣,同一個(gè)包中類之間的信息傳遞就比較方便。
?
7、設(shè)計(jì)舉例
?
??? 本節(jié)給出一個(gè)較為復(fù)雜的程序設(shè)計(jì)舉例。
【例3.10】設(shè)計(jì)一個(gè)包括矩陣加和矩陣減運(yùn)算的矩陣類,并設(shè)計(jì)一個(gè)測(cè)試程序完成簡(jiǎn)單的測(cè)試。為簡(jiǎn)化設(shè)計(jì)代碼,矩陣的元素值在構(gòu)造方法中用隨機(jī)函數(shù)隨機(jī)給出。
程序設(shè)計(jì)如下:
//MyMatrix.java文件
public class MyMatrix????? //矩陣類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è)試類
{
?? 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