java理念_java温故而知新(9)OOP(面向对象编程)理念
Object ??Oriented ??Programming ??(面向?qū)ο蟮某绦蛟O(shè)計)
1、定義
面向?qū)ο蟪绦蛟O(shè)計(OOP)的具體定義很難下,也很容易因此而引起爭論,在 ??Object-Oriented ??Frequently ?Asked ??Questions ??(OO ??FAQ) ??中就有好幾種不同的定義。這里就按照 ??Grady ??Booch ??[1994] ??的定義:“面向?qū)ο蟪绦蛟O(shè)計是程序結(jié)構(gòu)的一種實現(xiàn)方法,在這種方法下,程序由互相協(xié)作的對象組成,這些對象是某個類的實例,而這些類又是通過繼承關(guān)系組成的類的分級結(jié)構(gòu)的成員?!?/p>
2、要素
面向?qū)ο蟮脑O(shè)計(object ??oriented ??design)都包含以下幾個要素:
對象(Object):包含一定的數(shù)據(jù)結(jié)構(gòu)和狀態(tài)的實體。
操作(Operation):作用于對象的行為,如訪問和處理對象的狀態(tài)。
封裝(Encapsulation):定義對象和操作,只提供抽象的接口,并隱藏它們的具體實現(xiàn)。
繼承(Inheritance):通過繼承現(xiàn)有類型的性質(zhì),創(chuàng)建新的數(shù)據(jù)類型,而不影響原有數(shù)據(jù)類型。
多態(tài)性(Polymorphism):判定數(shù)據(jù)類型集合中各類型的區(qū)別,使程序可以按照它們的共同特性來書寫。
OOP 的繼承從理論上講是模仿人的思考方法,將對象分類,如:car,bus ??這兩個數(shù)據(jù)類型是從數(shù)據(jù)類型vehicle ??繼承而來的,它們作為 ??vehicle ??的一類,自然繼承了 ??vehicle ??的特性,同時具有自身獨(dú)有的特性;而 ??wheel ??卻不是 ??vehicle ??的一類,只是 ??vehicle ??的一個組成部份,因此不是從 ??vehicle ??繼承而來。同樣,vehicle ??有一些操作,如 ??start,reverse, ??car和bus也繼承下來,如果必要,也可加上自己獨(dú)有的操作,如 ??drive_at_200mph。但在實際程序中,人們往往忽視了面向?qū)ο蟮膬?nèi)涵,甚止于 ??C++ ??這些語言玩了好幾年,也只是用上了面向?qū)ο蟮恼Z法,而沒有形成面向?qū)ο蟮乃伎挤椒ā?/p>
3、要素詳解
1)對象與對象引用
為便于說明,我們先定義一個簡單的類:
classVehicle {intpassengers;intfuelcap;intmpg;
}
有了這個模板,就可以用它來創(chuàng)建對象:
Vehicle veh1 = new Vehicle();
通常把這條語句的動作稱之為創(chuàng)建一個對象,其實,它包含了四個動作。
1)右邊的“new Vehicle”,是以Vehicle類為模板,在堆空間里創(chuàng)建一個Vehicle類對象(也簡稱為Vehicle對象)。
2)末尾的()意味著,在對象創(chuàng)建后,立即調(diào)用Vehicle類的構(gòu)造函數(shù),對剛生成的對象進(jìn)行初始化。構(gòu)造函數(shù)是肯定有的。如果你沒寫,Java會給你補(bǔ)上一個默認(rèn)的構(gòu)造函數(shù)。
3)左邊的“Vehicle veh?1”創(chuàng)建了一個Vehicle類引用變量。所謂Vehicle類引用,就是以后可以用來指向Vehicle對象的對象引用。
4)“=”操作符使對象引用指向剛創(chuàng)建的那個Vehicle對象。
我們可以把這條語句拆成兩部分:
Vehicle veh1;
veh1 = new Vehicle();
效果是一樣的。這樣寫,就比較清楚了,有兩個實體:一是對象引用變量,一是對象本身。
在堆空間里創(chuàng)建的實體,與在數(shù)據(jù)段以及??臻g里創(chuàng)建的實體不同。盡管它們也是確確實實存在的實體,但是,我們看不見,也摸不著。不僅如此,
我們仔細(xì)研究一下第二句,找找剛創(chuàng)建的對象叫什么名字?有人說,它叫“Vehicle”。不對,“Vehicle”是類(對象的創(chuàng)建模板)的名字。
一個Vehicle類可以據(jù)此創(chuàng)建出無數(shù)個對象,這些對象不可能全叫“Vehicle”。
對象連名都沒有,沒法直接訪問它。我們只能通過對象引用來間接訪問對象。
為了形象地說明對象、引用及它們之間的關(guān)系,可以做一個或許不很妥當(dāng)?shù)谋扔?。對象好比是一只很大的氣?#xff0c;大到我們抓不住它。引用變量是一根繩, 可以用來系汽球。
如果只執(zhí)行了第一條語句,還沒執(zhí)行第二條,此時創(chuàng)建的引用變量veh1還沒指向任何一個對象,它的值是null。引用變量可以指向某個對象,或者為null。
它是一根繩,一根還沒有系上任何一個汽球的繩。執(zhí)行了第二句后,一只新汽球做出來了,并被系在veh1這根繩上。我們抓住這根繩,就等于抓住了那只汽球。
再來一句:
Vehicle veh2;
就又做了一根繩,還沒系上汽球。如果再加一句:
veh2 = veh1;
系上了。這里,發(fā)生了復(fù)制行為。但是,要說明的是,對象本身并沒有被復(fù)制,被復(fù)制的只是對象引用。結(jié)果是,veh2也指向了veh1所指向的對象。兩根繩系的是同一只汽球。
如果用下句再創(chuàng)建一個對象:
veh2 = new Vehicle();
則引用變量veh2改指向第二個對象。
從以上敘述再推演下去,我們可以獲得以下結(jié)論:
(1)一個對象引用可以指向0個或1個對象(一根繩子可以不系汽球,也可以系一個汽球);
(2)一個對象可以有N個引用指向它(可以有N條繩子系住一個汽球)。
如果再來下面語句:
veh1 = veh2;
按上面的推斷,veh1也指向了第二個對象。這個沒問題。問題是第一個對象呢?沒有一條繩子系住它,它飛了。多數(shù)書里說,它被Java的垃圾回收機(jī)制回收了。
這不確切。正確地說,它已成為垃圾回收機(jī)制的處理對象。至于什么時候真正被回收,那要看垃圾回收機(jī)制的心情了。
由此看來,下面的語句應(yīng)該不合法吧?至少是沒用的吧?
new Vehicle();
不對。它是合法的,而且可用的。譬如,如果我們僅僅為了打印而生成一個對象,就不需要用引用變量來系住它。最常見的就是打印字符串:
System.out.println(“I am Java!”);
字符串對象“I am Java!”在打印后即被丟棄。有人把這種對象稱之為臨時對象。
對象與引用的關(guān)系將持續(xù)到對象回收。
2)封裝
封裝從字面上來理解就是包裝的意思,專業(yè)點(diǎn)就是信息隱藏,是指利用抽象數(shù)據(jù)類型將數(shù)據(jù)和基于數(shù)據(jù)的操作封裝在一起,使其構(gòu)成一個不可分割的獨(dú)立實體,數(shù)據(jù)被保護(hù)在抽象數(shù)據(jù)類型的內(nèi)部,盡可能地隱藏內(nèi)部的細(xì)節(jié),只保留一些對外接口使之與外部發(fā)生聯(lián)系。系統(tǒng)的其他對象只能通過包裹在數(shù)據(jù)外面的已經(jīng)授權(quán)的操作來與這個封裝的對象進(jìn)行交流和交互。也就是說用戶是無需知道對象內(nèi)部的細(xì)節(jié)(當(dāng)然也無從知道),但可以通過該對象對外的提供的接口來訪問該對象。
對于封裝而言,一個對象它所封裝的是自己的屬性和方法,所以它是不需要依賴其他對象就可以完成自己的操作。
使用封裝有三大好處:
1、良好的封裝能夠減少耦合。
2、類內(nèi)部的結(jié)構(gòu)可以自由修改。
3、可以對成員進(jìn)行更精確的控制。
4、隱藏信息,實現(xiàn)細(xì)節(jié)。
現(xiàn)在我們從程序的角度來分析封裝帶來的好處。如果我們不使用封裝(該對象就沒有setter()和getter()),那么Husband類應(yīng)該這樣寫:
public classHusband {publicString name ;publicString sex ;public intage ;publicWife wife;
}
我們應(yīng)該這樣來使用它:
Husband husband = newHusband();
husband.age= 30;
husband.name= "張三";
husband.sex= "男"; //貌似有點(diǎn)兒多余
但是那天如果我們需要修改Husband,例如將age修改為String類型的呢?你只有一處使用了這個類還好,如果你有幾十個甚至上百個這樣地方,你是不是要改到崩潰。如果使用了封裝,我們完全可以不需要做任何修改,只需要稍微改變下Husband類的setAge()方法即可。
public classHusband {/** 對屬性的封裝
* 一個人的姓名、性別、年齡、妻子都是這個人的私有屬性*/
privateString name ;privateString sex ;private String age ; /*改成 String類型的*/
privateWife wife;publicString getAge() {returnage;
}public void setAge(intage) {//轉(zhuǎn)換即可
this.age =String.valueOf(age);
}/**省略其他屬性的setter、getter **/
其他的地方依然那樣引用(husband.setAge(22))保持不變。
到了這里我們確實可以看出:封裝確實可以使我們?nèi)菀椎匦薷念惖膬?nèi)部實現(xiàn),而無需修改使用了該類的客戶代碼。
我們再看另一個好處:可以對成員變量進(jìn)行更精確的控制。
還是那個Husband,一般來說我們在引用這個對象的時候是不容易出錯的,但是有時你迷糊了,寫成了這樣:
Husband husband = newHusband();
husband.age= 300;
也許你是因為粗心寫成了,你發(fā)現(xiàn)了還好,如果沒有發(fā)現(xiàn)那就麻煩大了,逼近誰見過300歲的老妖怪啊!
但是使用封裝我們就可以避免這個問題,我們對age的訪問入口做一些控制(setter)如:
public classHusband {/** 對屬性的封裝
* 一個人的姓名、性別、年齡、妻子都是這個人的私有屬性*/
privateString name ;privateString sex ;private int age ; /*改成 String類型的*/
privateWife wife;public intgetAge() {returnage;
}public void setAge(intage) {if(age > 120){
System.out.println("ERROR:error age input...."); //提示錯誤信息
}else{this.age =age;
}
}/**省略其他屬性的setter、getter **/}
上面都是對setter方法的控制,其實通過使用封裝我們也能夠?qū)ο蟮某隹谧龀龊芎玫目刂?。例如性別我們在數(shù)據(jù)庫中一般都是已1、0方式來存儲的,但是在前臺我們又不能展示1、0,這里我們只需要在getter()方法里面做一些轉(zhuǎn)換即可。
publicString getSexName() {if("0".equals(sex)){
sexName= "女";
}else if("1".equals(sex)){
sexName= "男";
}else{
sexName= "人妖???";
}returnsexName;
}
在使用的時候我們只需要使用sexName即可實現(xiàn)正確的性別顯示。同理也可以用于針對不同的狀態(tài)做出不同的操作。
publicString getCzHTML(){if("1".equals(zt)){
czHTML= "啟用";
}else{
czHTML= "禁用";
}returnczHTML;
}
3)繼承
我們可以把JAVA中的類分為以下三種:
類:使用class定義且不含有抽象方法的類。
抽象類:使用abstract class定義的類,它可以含有,也可以不含有抽象方法。
接口:使用interface定義的類。
在這三種類型之間存在下面的繼承規(guī)律:
類可以繼承(extends)類,可以繼承(extends)抽象類,可以繼承(implements)接口。
抽象類可以繼承(extends)類,可以繼承(extends)抽象類,可以繼承(implements)接口。
接口只能繼承(extends)接口。
繼承是使用已存在的類的定義作為基礎(chǔ)建立新類的技術(shù),新類的定義可以增加新的數(shù)據(jù)或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。這種技術(shù)使得復(fù)用以前的代碼非常容易,能夠大大縮短開發(fā)周期,降低開發(fā)費(fèi)用。
繼承是為了重用父類代碼,同時為實現(xiàn)多態(tài)性作準(zhǔn)備。
繼承是所有OOP語言不可缺少的部分,在java中使用extends關(guān)鍵字來表示繼承關(guān)系。當(dāng)創(chuàng)建一個類時,總是在繼承,如果沒有明確指出要繼承的類,就總是隱式地從根類Object進(jìn)行繼承。比如下面這段代碼:
classPerson {publicPerson() {
}
}class Man extendsPerson {publicMan() {
}
}
類Man繼承于Person類,這樣一來的話,Person類稱為父類(基類),Man類稱為子類(導(dǎo)出類)。如果兩個類存在繼承關(guān)系,則子類會自動繼承父類的方法和變量,在子類中可以調(diào)用父類的方法和變量。在java中,只允許單繼承,也就是說 一個類最多只能顯示地繼承于一個父類。但是一個類卻可以被多個類繼承,也就是說一個類可以擁有多個子類。
1.子類繼承父類的成員變量
當(dāng)子類繼承了某個類之后,便可以使用父類中的成員變量,但是并不是完全繼承父類的所有成員變量。具體的原則如下:
1)能夠繼承父類的public和protected成員變量;不能夠繼承父類的private成員變量;
2)對于父類的包訪問權(quán)限成員變量,如果子類和父類在同一個包下,則子類能夠繼承;否則,子類不能夠繼承;
3)對于子類可以繼承的父類成員變量,如果在子類中出現(xiàn)了同名稱的成員變量,則會發(fā)生隱藏現(xiàn)象,即子類的成員變量會屏蔽掉父類的同名成員變量。如果要在子類中訪問父類中同名成員變量,需要使用super關(guān)鍵字來進(jìn)行引用。
2.子類繼承父類的方法
同樣地,子類也并不是完全繼承父類的所有方法。
1)能夠繼承父類的public和protected成員方法;不能夠繼承父類的private成員方法;
2)對于父類的包訪問權(quán)限成員方法,如果子類和父類在同一個包下,則子類能夠繼承;否則,子類不能夠繼承;
3)對于子類可以繼承的父類成員方法,如果在子類中出現(xiàn)了同名稱的成員方法,則稱為覆蓋,即子類的成員方法會覆蓋掉父類的同名成員方法。如果要在子類中訪問父類中同名成員方法,需要使用super關(guān)鍵字來進(jìn)行引用。
注意:隱藏和覆蓋是不同的。隱藏是針對成員變量和靜態(tài)方法的,而覆蓋是針對普通方法的。(后面會講到)
3.構(gòu)造器
子類是不能夠繼承父類的構(gòu)造器,但是要注意的是,如果父類的構(gòu)造器都是帶有參數(shù)的,則必須在子類的構(gòu)造器中顯示地通過super關(guān)鍵字調(diào)用父類的構(gòu)造器并配以適當(dāng)?shù)膮?shù)列表。如果父類有無參構(gòu)造器,則在子類的構(gòu)造器中用super關(guān)鍵字調(diào)用父類構(gòu)造器不是必須的,如果沒有使用super關(guān)鍵字,系統(tǒng)會自動調(diào)用父類的無參構(gòu)造器??聪旅孢@個例子就清楚了:
classShape {protectedString name;publicShape(){
name= "shape";
}publicShape(String name) {this.name =name;
}
}class Circle extendsShape {private doubleradius;publicCircle() {
radius= 0;
}public Circle(doubleradius) {this.radius =radius;
}public Circle(doubleradius,String name) {this.radius =radius;this.name =name;
}
}
這樣的代碼是沒有問題的,如果把父類的無參構(gòu)造器去掉,則下面的代碼必然會出錯:
改成下面這樣就行了:
4.super
super主要有兩種用法:
1)super.成員變量/super.成員方法;
2)super(parameter1,parameter2....)
第一種用法主要用來在子類中調(diào)用父類的同名成員變量或者方法;第二種主要用在子類的構(gòu)造器中顯示地調(diào)用父類的構(gòu)造器,要注意的是,如果是用在子類構(gòu)造器中,則必須是子類構(gòu)造器的第一個語句。
4)多態(tài)
方法的重寫、重載與動態(tài)連接構(gòu)成多態(tài)性。Java之所以引入多態(tài)的概念,原因之一是它在類的繼承問題上和C++不同,后者允許多繼承,這確實給其帶來的非常強(qiáng)大的功能,但是復(fù)雜的繼承關(guān)系也給C++開發(fā)者帶來了更大的麻煩,為了規(guī)避風(fēng)險,Java只允許單繼承,派生類與基類間有IS-A的關(guān) 系(即“貓”is a “動物”)。這樣做雖然保證了繼承關(guān)系的簡單明了,但是勢必在功能上有很大的限制,所以,Java引入了多態(tài)性的概念以彌補(bǔ)這點(diǎn)的不足,此外,抽象類和接口也是解決單繼承規(guī)定限制的重要手段。同時,多態(tài)也是面向?qū)ο缶幊痰木杷凇?/p>
多態(tài)又分為設(shè)計時多態(tài)和運(yùn)行時多態(tài),例如重載又被稱為設(shè)計時多態(tài),而對于覆蓋或繼承的方法,JAVA運(yùn)行時系統(tǒng)根據(jù)調(diào)用該方法的實例的類型來決定選擇調(diào)用哪個方法則被稱為運(yùn)行時多態(tài)??偠灾?#xff0c;面向?qū)ο蟮脑O(shè)計的典型特點(diǎn)就是繼承,封裝和多態(tài),這些特點(diǎn)也是面向?qū)ο笾阅苋绱耸⑿械年P(guān)鍵所在。
對于多態(tài),可以總結(jié)它為:
一、使用父類類型的引用指向子類的對象;該引用只能調(diào)用父類中定義的方法和變量;
二、如果子類中重寫了父類中的一個方法,那么在調(diào)用這個方法的時候,將會調(diào)用子類中的這個方法;(動態(tài)連接、動態(tài)調(diào)用)
三、變量不能被重寫(覆蓋),”重寫“的概念只針對方法。
重寫,英文名是overriding,是指在繼承情況下,子類中定義了與其基類中方法具有相同型構(gòu)的新方法,就叫做子類把基類的方法重寫了。這是實現(xiàn)多態(tài)必須的步驟。
重載,英文名是overloading,是指在同一個類中定義了一個以上具有相同名稱,但是型構(gòu)不同的方法。在同一個類中,是不允許定義多于一個的具有相同型構(gòu)的方法的。
總結(jié)
以上是生活随笔為你收集整理的java理念_java温故而知新(9)OOP(面向对象编程)理念的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql profiling表_mys
- 下一篇: java 数字的位数_Java判断数字位