摘 要:面向?qū)ο笫钱?dāng)前主流的程序設(shè)計(jì)方法,是軟件開(kāi)發(fā)過(guò)程中重要的理論支撐。正確理解對(duì)象與類(lèi)的概念,以及正確的使用對(duì)象對(duì)于學(xué)習(xí)面向?qū)ο缶幊叹哂兄匾饔?。文中以面向?qū)ο蟪绦蛟O(shè)計(jì)中“對(duì)象”作為主體貫穿全文,分別從對(duì)象的理解、Java語(yǔ)言中對(duì)象管理、對(duì)象如何正確應(yīng)用三個(gè)方面進(jìn)行闡述。文章內(nèi)容精煉、源于實(shí)戰(zhàn),對(duì)于幫助理解和掌握J(rèn)ava面向?qū)ο蟪绦蛟O(shè)計(jì)具有較好的參考價(jià)值。
關(guān)鍵詞:Java;對(duì)象;面向?qū)ο?;OOP
中圖分類(lèi)號(hào):TP312 文獻(xiàn)標(biāo)識(shí)碼:A
Abstract:As the current mainstream in programming,the object-oriented method is important theoretical support in the process of software development.It is important to thoroughly understand the concepts of objects and classes,and properly apply objects in programming.Focusing on the object in the object-oriented programming,the paper elaborates on the understanding of the object,the object management in the Java programming language,and how to apply the object correctly.With the efficient language and practical experience,the paper is of good reference to the understanding and application of Java object-oriented programming.
Keywords:java;object;oject oriented;Object-Oriented Programming
1 引言(Introduction)
面向?qū)ο笫钱?dāng)前主流編程語(yǔ)言的共同的特征,如Java、C#語(yǔ)言。面向?qū)ο笊婕暗杰浖_(kāi)發(fā)的各個(gè)階段,具體包括面向?qū)ο蠓治鯫OA、面向?qū)ο笤O(shè)計(jì)OOD、面向?qū)ο缶幊蘋(píng)OP,形成了完整的面向?qū)ο蟮能浖こ汤碚?、方法和工具[1]。
在學(xué)習(xí)面向?qū)ο缶幊陶Z(yǔ)言時(shí),首先學(xué)習(xí)的就是對(duì)象的概念,能夠正確的理解對(duì)象和使用對(duì)象對(duì)于之后的面向?qū)ο筇匦詫W(xué)習(xí)具有很大幫助。本文從對(duì)象與類(lèi)的理解、對(duì)象的原理和對(duì)象的使用三個(gè)方面循序漸進(jìn)闡述,較全面的介紹了關(guān)于對(duì)象概念、對(duì)象存儲(chǔ)原理及在編程中正確使用對(duì)象的技巧等內(nèi)容,內(nèi)容對(duì)于理解面向?qū)ο笏枷牒蛯W(xué)習(xí)面向?qū)ο缶幊叹哂休^大幫助。
2 理解對(duì)象與類(lèi)(Understanding objects and classes)
掌握面向?qū)ο蟪绦蛟O(shè)計(jì)首先要正確理解面向?qū)ο笏枷?,面向?qū)ο笏枷氲暮诵氖钦_理解“萬(wàn)物皆對(duì)象”這句話(huà)。在編程時(shí)運(yùn)用“萬(wàn)物皆對(duì)象”的思想是指:通過(guò)使用面向?qū)ο蟮姆绞?,將現(xiàn)實(shí)世界中的實(shí)體看作為對(duì)象,對(duì)這些實(shí)體的特性進(jìn)行描述,并分析出實(shí)體具有的功能或職責(zé)。描述實(shí)體特性的過(guò)程即為提取對(duì)象屬性的過(guò)程,分析實(shí)體的功能或職責(zé)的過(guò)程即為提取對(duì)象方法的過(guò)程,完整描述一個(gè)對(duì)象即從屬性和方法兩個(gè)層面,提取對(duì)象屬性和方法的過(guò)程即為使用面向?qū)ο笏枷脒M(jìn)行分析和抽象的過(guò)程。
在把客觀實(shí)體抽象為對(duì)象后,還要繼續(xù)分析各對(duì)象之間的關(guān)系,最后將對(duì)象的抽象結(jié)果描述出來(lái)形成一段文本,此文本即為面向?qū)ο笏枷胫械摹邦?lèi)class”。從以上過(guò)程可以發(fā)現(xiàn),類(lèi)是對(duì)象的屬性、方法、對(duì)象之間關(guān)系的描述。對(duì)象是現(xiàn)實(shí)世界中可以描述的實(shí)體,是具體的;類(lèi)是對(duì)象的描述,是不具體的,在編程時(shí)類(lèi)體現(xiàn)為一段代碼文本。
以下以駕駛員和汽車(chē)為例闡述類(lèi)與對(duì)象的關(guān)系與區(qū)別?,F(xiàn)實(shí)世界中汽車(chē)與駕駛員皆為生活中常見(jiàn)的實(shí)體。當(dāng)我們看到一輛汽車(chē)時(shí)會(huì)自然聯(lián)想到汽車(chē)的品牌、型號(hào)等屬性信息,分析其職責(zé)(功能)可以得出汽車(chē)可以行駛、停止。但汽車(chē)不會(huì)自己行駛或停止,它需要駕駛員執(zhí)行啟動(dòng)、掛擋等操作,可以看出汽車(chē)與駕駛員二者是存在依賴(lài)關(guān)系的。分析駕駛員對(duì)象可以得出駕駛員具有駕駛證照號(hào)碼、性別、年齡等屬性信息,駕駛員具有“駕駛”能力。分析汽車(chē)對(duì)象可以得出汽車(chē)具有品牌、型號(hào)、價(jià)格等屬性,汽車(chē)具有行駛和停止的功能。經(jīng)過(guò)上述分析,可以抽象出汽車(chē)類(lèi)和駕駛員類(lèi),以及兩個(gè)類(lèi)間的依賴(lài)關(guān)系,其類(lèi)圖如圖1所示。
經(jīng)過(guò)對(duì)汽車(chē)與駕駛員兩個(gè)對(duì)象的分析,繼而再抽象出兩個(gè)類(lèi)可以看出,面向?qū)ο笏枷肱c人類(lèi)的思維方式極為相似,對(duì)于初學(xué)者而言并不難入門(mén),但對(duì)于一些較為抽象的場(chǎng)景而言初學(xué)者通常會(huì)感覺(jué)稍有壓力。下面以開(kāi)發(fā)一款簡(jiǎn)單人機(jī)猜數(shù)字游戲?yàn)槔^續(xù)理解對(duì)象與類(lèi)的概念。人機(jī)猜數(shù)字游戲的規(guī)則為:機(jī)器隨機(jī)生成一個(gè)數(shù)值,玩家輸入猜的數(shù)字且每一局最多猜三次,若三次都未猜中則本局失敗。游戲根據(jù)猜的數(shù)字位數(shù)分為三個(gè)級(jí)別,1位數(shù)為初級(jí)、2位數(shù)為中級(jí)、3位數(shù)為高級(jí)。根據(jù)游戲規(guī)則的描述可快速分析出本案例至少包括人類(lèi)玩家對(duì)象和機(jī)器玩家對(duì)象。人類(lèi)玩家對(duì)象具有玩家名稱(chēng)、當(dāng)前游戲級(jí)別屬性,功能職責(zé)為輸入猜的數(shù)字。機(jī)器玩家對(duì)象職責(zé)為根據(jù)當(dāng)前人類(lèi)玩家的級(jí)別生成相應(yīng)位數(shù)的隨機(jī)數(shù)字。根據(jù)上述分析可以看到只有人類(lèi)玩家和機(jī)器玩家對(duì)象還無(wú)法實(shí)現(xiàn)游戲功能,因?yàn)槟壳斑€沒(méi)有控制游戲進(jìn)度的對(duì)象和描述游戲級(jí)別信息的對(duì)象。因此,在進(jìn)一步分析后抽象出游戲?qū)ο蠛图?jí)別對(duì)象。游戲?qū)ο笙喈?dāng)于裁判,可控制游戲的啟動(dòng)及停止、判斷所猜數(shù)字是否正確和設(shè)置當(dāng)前人類(lèi)玩家的級(jí)別。游戲級(jí)別對(duì)象則用于保存級(jí)別的參數(shù)信息,包括級(jí)別名稱(chēng)、級(jí)別對(duì)應(yīng)數(shù)字的位數(shù)。綜上所述,本案例最后抽取出游戲類(lèi)、機(jī)器玩家類(lèi)、人類(lèi)玩家類(lèi)、游戲級(jí)別類(lèi)。其中游戲類(lèi)與機(jī)器玩家類(lèi)、人類(lèi)玩家類(lèi)具有關(guān)聯(lián)關(guān)系,機(jī)器玩家類(lèi)與人類(lèi)玩家類(lèi)具有依賴(lài)關(guān)系,人類(lèi)玩家類(lèi)與游戲等級(jí)類(lèi)具有關(guān)聯(lián)性,其類(lèi)圖如圖2所示。
3 Java對(duì)象管理(Java object management)
Java語(yǔ)言編寫(xiě)的程序運(yùn)行在Java虛擬機(jī)之上,基于此特點(diǎn)才實(shí)現(xiàn)了Java程序的平臺(tái)無(wú)關(guān)性和良好的可移植性[2,3]?;谔摂M機(jī)平臺(tái)運(yùn)行的另一個(gè)好處是在編寫(xiě)程序時(shí)不再需要考慮C或C++語(yǔ)言中的手動(dòng)內(nèi)存管理問(wèn)題,Java虛擬機(jī)會(huì)自動(dòng)進(jìn)行內(nèi)存的申請(qǐng)和釋放。在Java虛擬機(jī)中,存放對(duì)象的區(qū)域是內(nèi)存占用比例最高的,這個(gè)區(qū)域稱(chēng)為堆heap[4,5]。因此能夠理解對(duì)象在堆中的存儲(chǔ)原理對(duì)編寫(xiě)高效的Java程序具有很大幫助。
學(xué)習(xí)Java對(duì)象管理的主要內(nèi)容是學(xué)習(xí)對(duì)象的生命周期,即對(duì)象的創(chuàng)建、使用和銷(xiāo)毀三個(gè)階段。
3.1 對(duì)象創(chuàng)建
創(chuàng)建一個(gè)Java對(duì)象需要經(jīng)過(guò)三個(gè)過(guò)程分別為加載類(lèi)文件、分配內(nèi)存、對(duì)象初始化[5,6]。
(1)加載類(lèi)文件:類(lèi)是對(duì)對(duì)象的描述,在使用new關(guān)鍵字創(chuàng)建對(duì)象時(shí),虛擬機(jī)會(huì)先檢查對(duì)應(yīng)的類(lèi)文件是否已經(jīng)被加載,若未加載則會(huì)先加載類(lèi)文件到虛擬機(jī)中。Java語(yǔ)言提供了多種類(lèi)加載器classloader用于加載類(lèi)文件(.class文件)到虛擬機(jī)中[6,7],分別包括:
a.啟動(dòng)加載器:最頂層的類(lèi)加載器,用于加載核心類(lèi)庫(kù),如java語(yǔ)言提供的rt.jar等;
b.擴(kuò)展類(lèi)加載器:用于加載Java目錄下ext目錄中的所有類(lèi)庫(kù)文件;
c.系統(tǒng)類(lèi)加載器:負(fù)責(zé)加載應(yīng)用程序classpath目錄下所有類(lèi)文件,此目錄下類(lèi)文件即為開(kāi)發(fā)者自主編寫(xiě)的程序文件[8]。
(2)分配內(nèi)存:當(dāng)類(lèi)文件加載完后,對(duì)象在創(chuàng)建時(shí)需使用的初始內(nèi)存大小就確定了。虛擬機(jī)在堆空間中劃分出相應(yīng)大小的存儲(chǔ)空間。當(dāng)前虛擬機(jī)在內(nèi)存分配方法上采用指針碰撞法或空閑列表法?!爸羔樑鲎病狈僭O(shè)Java堆中的內(nèi)存是絕對(duì)規(guī)整的,所有用過(guò)的內(nèi)存都放在一邊,空閑的內(nèi)存放在另一邊,中間放一個(gè)指針作為分界點(diǎn)。當(dāng)需要分配內(nèi)存時(shí)只需要把指針向空閑內(nèi)存方向移動(dòng)對(duì)象大小相等的距離即可。如果Java堆中的內(nèi)存并不規(guī)整,那么虛擬機(jī)需要維護(hù)一個(gè)列表用來(lái)記錄哪些內(nèi)存塊可用。當(dāng)需要分配內(nèi)存時(shí)從列表中找出一個(gè)足夠大的空間劃分給對(duì)象實(shí)例,這就是“空閑列表”。
(3)對(duì)象初始化:虛擬機(jī)在對(duì)象內(nèi)存分配完成后會(huì)給對(duì)象內(nèi)部的屬性分配初始值,此初始值如果沒(méi)有手動(dòng)設(shè)置則會(huì)被設(shè)置為數(shù)據(jù)類(lèi)型默認(rèn)初始值(例如int數(shù)據(jù)類(lèi)型的初始值為0)。最后調(diào)用類(lèi)的構(gòu)造方法完成對(duì)象初始化操作。
3.2 對(duì)象使用
當(dāng)創(chuàng)建好對(duì)象后,需要通過(guò)引用方式來(lái)訪問(wèn)使用對(duì)象,常見(jiàn)的有兩種方式,第一種是句柄,第二種是直接指針。
(1)句柄
Java堆中專(zhuān)門(mén)劃分一部分內(nèi)存作為句柄池,Java棧中的引用存儲(chǔ)的是對(duì)象的句柄地址,而句柄地址存儲(chǔ)了對(duì)象實(shí)例數(shù)據(jù)與數(shù)據(jù)類(lèi)型各自的具體地址信息。
使用句柄的好處就是引用中存儲(chǔ)的是固定的句柄地址,在對(duì)象被移動(dòng)(垃圾收集時(shí)移動(dòng)對(duì)象)時(shí)只會(huì)改變句柄中實(shí)例數(shù)據(jù)指針,而引用不需要更改。
(2)直接指針
直接指針就是Java棧中的引用直接存儲(chǔ)對(duì)象的內(nèi)存地址。使用直接指針最大的好處就是訪問(wèn)速度快,它節(jié)省了一次指針定位的時(shí)間開(kāi)銷(xiāo)。Sun的Hot Spot虛擬機(jī)使用的直接指針訪問(wèn)對(duì)象。
(3)對(duì)象銷(xiāo)毀
對(duì)象的銷(xiāo)毀回收由虛擬機(jī)負(fù)責(zé)執(zhí)行,虛擬機(jī)判斷一個(gè)對(duì)象是否可以被回收的算法包括引用計(jì)數(shù)法和根集算法。引用計(jì)數(shù)法為虛擬機(jī)內(nèi)部為每個(gè)對(duì)象保存一個(gè)引用數(shù)量,當(dāng)一個(gè)對(duì)象的引用數(shù)量為0時(shí),則虛擬機(jī)會(huì)在下次垃圾回收時(shí)將此對(duì)象回收。由于引用計(jì)數(shù)算法無(wú)法解決對(duì)象之間引用閉環(huán)問(wèn)題,因此出現(xiàn)了根集算法。根集算法原理是從GCRoot(如一個(gè)靜態(tài)變量)開(kāi)始遍歷引用關(guān)系,最后對(duì)于無(wú)法被遍歷到的對(duì)象則會(huì)被回收[9,10]。
4 對(duì)象的正確使用(Proper use of object)
掌握J(rèn)ava虛擬機(jī)內(nèi)部原理可有效幫助開(kāi)發(fā)人員編寫(xiě)出高效的Java程序,但虛擬機(jī)內(nèi)部原理的復(fù)雜性導(dǎo)致學(xué)習(xí)成本較高、入門(mén)較難。因此對(duì)于初學(xué)者而言可以邊學(xué)習(xí)邊參考一些最佳實(shí)踐,從而寫(xiě)出高質(zhì)量Java代碼。在編碼時(shí)正確使用對(duì)象主要包括以下幾點(diǎn):
4.1 盡量復(fù)用對(duì)象,不重復(fù)創(chuàng)建
重復(fù)創(chuàng)建對(duì)象將導(dǎo)致內(nèi)存占用增大,浪費(fèi)內(nèi)存空間。解決方法是重用已創(chuàng)建的對(duì)象和通過(guò)代碼方式限制避免創(chuàng)建重復(fù)對(duì)象。
(1)使用單例模式。單例模式的作用是確保一個(gè)類(lèi)只能創(chuàng)建出一個(gè)對(duì)象。實(shí)現(xiàn)上主要為:a.顯示聲明私有構(gòu)造方法,禁止使用者使用new關(guān)鍵字創(chuàng)建對(duì)象;b.在類(lèi)中創(chuàng)建一個(gè)自身類(lèi)型的對(duì)象,并將其定義為私有的和靜態(tài)的,此變量即為供外部使用的唯一的對(duì)象;c.定義一個(gè)全局方法,此方法向外部提供唯一的實(shí)例變量。
(2)重用對(duì)象。適當(dāng)使用new關(guān)鍵字,在創(chuàng)建完一個(gè)對(duì)象后,后期考慮不再創(chuàng)建新對(duì)象,而是將之前創(chuàng)建對(duì)象內(nèi)部存儲(chǔ)數(shù)據(jù)更新以供下次使用。例如字符串變量,虛擬機(jī)對(duì)于字符串單獨(dú)設(shè)置了常量池進(jìn)行存儲(chǔ),因此對(duì)于代碼String s=hello”和String s=new String(“hello”)而言,前者的效率更高。
4.2 及時(shí)清空過(guò)期對(duì)象
雖然Java虛擬機(jī)提供了自動(dòng)化內(nèi)存管理,實(shí)現(xiàn)了自動(dòng)垃圾回收,但是為了提高程序運(yùn)行效率,當(dāng)在代碼中某對(duì)象不再使用時(shí),應(yīng)手動(dòng)設(shè)置對(duì)象值為null,這樣在下次垃圾回收時(shí)此對(duì)象即可被回收。例如在當(dāng)編寫(xiě)數(shù)據(jù)訪問(wèn)代碼時(shí),對(duì)于Connection對(duì)象的釋放即應(yīng)在使用后立即清除引用,正確寫(xiě)法如下。
Connection conn=null; //創(chuàng)建對(duì)象,默認(rèn)值為空
try{
conn=DriverManager.getConnection(url,username,pwd); //初始化conn對(duì)象
// ...
}finally {
if(null!=conn) {
try{conn.close();conn=null;}catch(Exception e) {;} //使用后立即清除引用
}
}
5 結(jié)論(Conclusion)
理解面向?qū)ο笏枷氲暮诵氖钦_理解對(duì)象概念,從對(duì)象到類(lèi),從類(lèi)再到接口等抽象過(guò)程。面向?qū)ο笏枷肴筇匦园ǚ庋b、繼承和多態(tài),這三大特性在學(xué)習(xí)和開(kāi)發(fā)過(guò)程中是密不可分的,在分析和設(shè)計(jì)對(duì)象與類(lèi)時(shí)以高內(nèi)聚、低耦合為原則,以提高代碼的復(fù)用性。Java程序在運(yùn)行時(shí)直接關(guān)系到性能的是虛擬機(jī)內(nèi)部對(duì)象的管理,正確理解對(duì)象的生命周期和在代碼中正確使用對(duì)象對(duì)于編程高效的Java程序具有極大幫助。因此從理解對(duì)象的概念、學(xué)習(xí)對(duì)象的正確使用入手,繼而進(jìn)入面向?qū)ο蠓治?、設(shè)計(jì)和編程領(lǐng)域,在開(kāi)發(fā)中才能更好更快地提高對(duì)于系統(tǒng)的分析和設(shè)計(jì)能力。
參考文獻(xiàn)(References)
[1] Adam Drozdek.Object-Oriented Programming and Representation of Objects[J].Studies in Logic,Grammar and Rhetoric,2015,40(1):293-302.
[2] Savrun-Yeni,et al.Efficient Hosted Interpreters on the JVM[J].Acm Transactions on Architecture & Code Optimization,2014,11(1):9.
[3] Maplesden,et al.Performance Analysis for Object-Oriented Software:A Systematic Mapping[J].IEEE Transactions on Software Engineering,2015,41(7):691-710.
[4] 嚴(yán)仲興.Java面向?qū)ο蟪绦蛟O(shè)計(jì)[M].北京:高等教育出版社,
2005.
[5] 黃俊爽,等.淺談Java面向?qū)ο蟪绦蛟O(shè)計(jì)[J].科技信息,2010,
13:463;465.
[6] 李永遠(yuǎn).JAVA虛擬機(jī)相關(guān)技術(shù)研究與實(shí)踐[J].信息通信,2015,
05:120.
[7] 馮宇.分析Java平臺(tái)的核心——虛擬機(jī)[J].網(wǎng)絡(luò)安全技術(shù)與應(yīng)用,2015(05):134;138.
[8] 崔行臣,趙佟.Java動(dòng)態(tài)類(lèi)加載機(jī)制分析及其應(yīng)用[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2013(07):187-191.
[9] 杜天宇,景慎艷.Java虛擬機(jī)的系統(tǒng)優(yōu)化研究[J].電腦知識(shí)與技術(shù),2016(01):72-73.
[10] 任嘉光.Java性能優(yōu)化技術(shù)綜述[J].信息化建設(shè),2016(06):121.
作者簡(jiǎn)介:
邢如意(1982-),男,碩士,講師,系統(tǒng)分析師.研究領(lǐng)域:軟件工程,分布式系統(tǒng)設(shè)計(jì)與開(kāi)發(fā).