面向對象及其三大特性
Author JYBlog
Email jcAuthor@163.com
本博客GitHub開源(jcNaruto/JYBlog)刘陶,
JYBlog每周五,周三公眾號等多平臺同步更新腰根,歡迎討論交流~
#.補充
面向過程在開發(fā)中也可以使用結構體等操作來實現(xiàn)高內聚低耦合抓督,也可以寫出遵循開閉原則的代碼惠赫,抽象的看面向對象颁褂,有點像形而上的編程思維,也是編程范式之一检盼,也就是在編寫動態(tài)迭代的軟件工程中面向對象的編程思維會提高工程可擴展性肯污,維護性,甚至降低工作量吨枉,高手使用面向過程一樣也可以寫出擴展性蹦渣,可維護性很好的代碼(工作量會大些,但可能會帶來性能或其他方面的收獲)东羹,再者映射到具體的面向對象的語言剂桥,例如Java語言實現(xiàn)了面向對象的編程思維,使得開發(fā)者更加方便的接觸面向對象思維属提。
1.概述
面向過程將解決問題的步驟寫在函數(shù)里面权逗,讓計算機有步驟的按順序解決問題美尸,在大型的軟件工程開發(fā)中這樣的過程化會造成維護困難,因為在軟件開發(fā)中需求的改動是常見的事斟薇,例如AB兩人同時開發(fā)一個軟件师坎,要旋轉三個圖形,正方形堪滨,三角形胯陋,圓形,A采用面向過程開發(fā)袱箱,B采用面向對象開發(fā)
//A開發(fā)
rotate(shapeName){
//旋轉
}
//B開發(fā)
class Square{
....
public void rotate(){
//旋轉
}
}
class Triangle{
....
public void rotate(){
//旋轉
}
}
class Round{
....
public void rotate(){
//旋轉
}
}
此時你會發(fā)現(xiàn)面向過程的優(yōu)越性遏乔,代碼量要少的多,但是現(xiàn)在需求改變发笔,要添加一個六邊形盟萨,且六邊形的旋轉方式與之前三個不一致
//A開發(fā)新增的需求
rotate(shapeName){
if(hexagon){
//六邊形旋轉
}else{
//旋轉
}
}
面向過程在需求動態(tài)改變的時候要修改已測試過的代碼,也就是隨著需求的每次更改都要重新測試了讨,十分耗費資源
例如互聯(lián)網中產品的迭代伴隨著一生捻激,比如某產品到現(xiàn)在還要因為迭代而測試多余的代碼會使得整個項目變得異常臃腫緩慢
//B開發(fā)新增的需求
.....
class Hexagon{
....
public void rotate(){
//旋轉
}
}
面向對象只需要在增加一個類就行了,此時面向對象的優(yōu)越性就體現(xiàn)出來前计,可以不修改已近測試過的代碼胞谭,需求是會不停的更改的,每改一次就只需要測試新增的類即可
也就是說在軟件工程動態(tài)的迭代的過程中男杈,面向對象的優(yōu)越性體現(xiàn)出來丈屹,但是此時還存在一個問題,面向對象的代碼量比面向過程要高的多势就,為了解決這個問題可以使用面向對象的繼承特性泉瞻,同時為了降低程序的耦合性和提高代碼的內聚性面向對象中還有封裝以及提高了程序可擴展性的多態(tài)。
2.封裝(Encapsulation)
class Student{
public int age;
}
class Teacher{
public void score(){
Student student = new Student();
student.age = -99;//不安全
}
}
以上就是不封裝的結果苞冯,完全脫離實際業(yè)務情況,并且student的age filed暴露給了所有的其他對象侧巨,為了解決此種情況舅锄,一般推薦將你的實例變量標記為private,然后對每個filed提供唯一的public的setter(設置)司忱,getter(獲然史蕖)方法,強制其他的對象在設置或者獲取該field時一定要經過對應的setter坦仍,getter方法鳍烁,并且在setter或者getter方法內部你還可以根據(jù)業(yè)務需求進行一定的邏輯判斷
class Student{
private int age;
public void setAge(int age){
if(age > 105 || age <0){
thorw new RuntimeException("age invalid");//在set方法內做業(yè)務邏輯校驗
}
this.age = age;
}
public int getAge(){
return age;
}
}
class Teacher{
public void score(){
Student student = new Student();
student.setAge(12);
}
}
注意
- getter,setter方法的命名是有規(guī)范的(規(guī)范就是最好遵守,不遵守也可以繁扎,但是可能會付出高昂的代價幔荒,軟件工程中可能就是要自己造所有的輪子)糊闽,xxx getPropertyName(), void setPropertyName(xxx propertyName),如果是filed 是boolean類型get方法命名為 boolean isPropertyName(),并且有getter和setter方法的filed可以稱之為屬性。
- 實際的開發(fā)中爹梁,很少會在setter和getter方法做校驗右犹,因此有人便提出可以省略去getter和setter直接將filed設置為public,這是錯誤的觀點姚垃,因為即使這兩個方法只是直接返回或者設置了filed的值念链,但還是為field提供了統(tǒng)一訪問路徑,假如某一天新需求是要對某個參數(shù)做校驗积糯,直接在setter方法中設置即可掂墓,但是如果直接外暴filed的public屬性,無數(shù)的xxx.field很難在進行校驗看成。也就是說getter梆暮,setter方法提高了程序的內聚性降低了模塊的耦合性,即使沒有校驗邏輯也要加上绍昂。如果一個class的所有屬性對應了setter啦粹,getter方法,則該class可以稱之為Java bean窘游,多用于傳輸數(shù)據(jù)唠椭。
- 一般敏感數(shù)據(jù)或者外部不不必感知的復雜邏輯方法也要使用private封裝,以降低耦合度忍饰,降低使用成本贪嫂。
3.繼承(Inheritance)
回到之前開發(fā)圖形旋轉的例子,多個類中有重復的rotate方法艾蓝,此時就可以抽取一個父類力崇,父類中具有那個rotate方法,子類通過extends就可以繼承父類赢织,然后具有那個rotate方法亮靴,但是六邊形的旋轉方式不一樣,這時就可以通過@Override來重新實現(xiàn)自己的rotate方法于置。
注意:
繼承表面上看是代碼復用技術使模塊具備復用性茧吊,同時也在子父類之間構建了某種協(xié)議,但普通類繼承和抽象類繼承和interface implements配合在一起使用八毯,可以用來設計出規(guī)范性搓侄,復用性很強的類架構。
今后如果在更改被抽取的代碼的話话速,只需更改這一處即可讶踪,并且只需要重新編譯父類即可。
java 單繼承泊交,因為要避免多繼承的致命方塊問題乳讥,但是采用了接口來彌補了單繼承的不足柱查。
super表示父類,super.filedName訪問父類filed,super()為調用父類構造方法雏婶,任何類的構造方法第一行都是這個物赶,如果沒有編譯器會自動加上。
繼承有一個局限就是子類無法訪問父類的private 字段或者方法留晚,改成protected,使其可以讓子類或者子子類訪問酵紫。
Object是Java中的總父類,是對萬事萬物的抽象错维。
區(qū)分組合和繼承奖地,組合就是將引用類型變量作為field,is 用繼承赋焕,has用組合参歹,通過李氏替換原則判斷是否滿足is關系,既任何父類出現(xiàn)的地方對應的子類都能夠出現(xiàn)隆判,成功編譯運行犬庇。
final修飾class和修飾方法對繼承的影響。
-
可以將子類向上轉型為父類侨嘀,稱為向上轉型臭挽,也可以將父類轉為子類,但可能會報錯咬腕,因為子類擴展的方法父類沒有欢峰,向下轉型的時候可以用instanceof做判斷運算符左邊的實例是否為右邊的類型或者為右邊的子類
class Person{ public int age; public String name; public void say(){ System.out.println(age + "=====" + name); } } class Student extends Person{ public int score; @Override public void say(){ System.out.println(age + "=====" + name + "=======" + score); } } public class InheritTest { public static void main(String[] args){ Person upStudent = new Student();//向上轉型,ok Student downStudent1 = new Person();//向下轉型涨共,ClassCastException if(upStudent instanceof Student){//instanceof 運算符纽帖,可以判斷左邊的實例是否為右邊的類型或者為右邊的子類 Student downStudent2 = (Student)upStudent;//此時向下轉型成功 } //調用的是Person還是Student的say方法 upStudent.say(); //實際上調用的是Student的方法,這就是多態(tài) } }
繼承的使用成本十分低举反,但是這帶來一個負面問題懊直,濫用繼承,出現(xiàn)方法污染和方法爆炸照筑,方法污染是指父類具備的功能傳給子類吹截,但是子類不具備該功能,方法爆炸是指某個子類方法過多凝危,不宜與代碼編寫和調式,因此謹慎使用繼承晨逝,避免濫用
4. 多態(tài)(Polymorphic)
子類向上轉型之后蛾默,子類同時也覆寫了對應的方法,此時調用的是子類中的方法捉貌,但是變量的標識符確實父類類型的支鸡,因此得出結論Java的實例方法調用是基于運行時的實際類型的動態(tài)調用冬念,而不是申明的變量類型,這就是多態(tài)牧挣。其含義就是運行期動態(tài)的決定調用的是子類的方法還是父類的方法急前。
多態(tài)強大的功能允許不修改父類代碼和業(yè)務邏輯代碼,就通過擴展子類來實現(xiàn)功能的擴展瀑构。
舉個栗子
不同學院的保研排名計算方式不一致裆针,各個學院有各個學院的算法
class Push{
public int[] scores;
public void getFinalScore(){
System.out.println(scores*0.1 + scores*0.4 + scores*0.5);
}
}
class MathStudent extends Push{
@Override
public void getFinalScore(){
System.out.println(scores*0.9 + scores*0 + scores*0.1);
}
}
class CSStudent extends Push{
@Override
public void getFinalScore(){
System.out.println(scores*0.3 + scores*0.4 + scores*0.3);
}
}
public class Caculator{
public void getFinalScores(Push[] pushs){
for(Push push : pushs){//利用多態(tài),getFinalScores只和Push父類相關寺晌,當要新增某個學院的學生時只需要世吨,新增子類并實現(xiàn)其getFinalScore方法即可,并將其實例傳入呻征,其他的代碼都不需要修改
push.getFinalScore();
}
}
}
類似的interface和實現(xiàn)類的之間也可以實現(xiàn)多態(tài)
多態(tài)使模塊在復用的基礎上具備了更強的可擴展性
注:重載和多態(tài)沒有任何的關系耘婚,重載根本沒有覆蓋任何方法,并且重載在編譯時期已經可以確定要調用的方法陆赋,談不上動態(tài)c宓弧!攒岛!
5. 總結
面向對象三大特性
- 封裝赖临,提高程序內聚,降低耦合阵子,降低程序維護使用成本思杯。
- 繼承,使模塊間可以復用挠进,少寫代碼色乾,是多態(tài)的基礎,同時在子父類之間產生了一種協(xié)議领突。
- 多態(tài)暖璧,使模塊在復用的基礎上具備了更強的可擴展性。
6.面向對象與抽象
面向對象就是通過對象把現(xiàn)實世界映射到計算機模型的一種編程方法君旦,是抽象思維的一種體現(xiàn)澎办。
抽象是計算機科學中及其重要的一種思維。
? 2.1 拋棄細節(jié)金砍,直接使用現(xiàn)成的api(指令集是CPU的抽象局蚀,編程時并不需要了解CPU底層的集成電路細節(jié),直接使用指令集即可恕稠,大大降低了計算機的使用成本)琅绅。
? 2.2 面向對象中抽象分為歸納演繹,歸納:從具體到本質鹅巍,個性到共性千扶,演繹:從本質到具體料祠,共性到個性。
7.致謝
- 碼出高效Java開發(fā)手冊 楊冠寶(孤盡) 高号煨撸慧(鳴莎)
- HeadFirst Java Bert Bates Kathy Sierra