在還不清楚怎樣面向對象?一文中,已經簡單介紹了面向對象的基本思想和三大特性,可是不夠詳細。本文再來具體探究一下面向對象。java
面向過程編程(Procedure Oriented Programming,POP)是一種以過程爲中心的編程思想,開發人員在解決問題時更專一於過程。編程
當咱們遇到一個問題時,只須要分析出第1步要作什麼、第2步要作什麼……直到解決問題,而後把這些步驟一步步地實現便可。安全
好比,我如今要編寫一個面向過程的程序來模擬「我要讓個人好朋友行小觀去幫我買瓶水」這個問題,以下:網絡
在從「給行小觀錢」到「行小觀把水給我」的整個過程,我和行小觀都專一於完成每個步驟(事件),行小觀只是一個執行我事先描述好的步驟的無思想的工具人而已。工具
若是我還有其餘問題須要行小觀幫忙,那麼我就還得把如何完成這些問題的詳細步驟全都告訴他,這麼麻煩那我還找別人幫忙幹什麼呢?還不如我本身去作。並且有些問題我本身也不會作,那怎麼辦?因此行小觀並非一個合格的工具人。this
與面向過程編程不一樣,面向對象編程(Object Oriented Programming,OOP)是一種以對象爲中心的編程思想,對象是現實世界中的一個個事物的實體。code
對象包含了用戶可使用(公開)的功能部分和對用戶隱藏的實現部分。jwt
在開發過程當中,咱們可使用功能部分來解決問題,可是並不關心功能是怎樣實現的。對象
仍是上面的那個買水的例子,使用面向對象來實現:blog
在這個例子中,我只須要給行小觀錢,而後他就能幫我買水。我相信我請行小觀有「買水的能力」,他必定能幫我買到水。至於去哪買?怎麼買?行小觀本身知道,我並不關心他是怎麼買到水的,由於個人目的很簡單:「我想要一瓶水」。
若是我有其餘問題須要行小觀幫忙,不管這些問題我會不會作,直接告訴他就行了,他會幫我完成。
我只專一於問題自己,具體的操做我並不關心。如今行小觀是一個合格的工具人了。
這裏經過一個你們耳熟能詳的神話——女媧造人,來講明類和對象之間的關係。
相傳女媧以泥土仿照本身摶土造人,創造並構建人類社會。
在這個神話裏,「女媧」是一個藍圖、模板,「人」是依據該藍圖被創造出來的個體。
「女媧」能夠看作類(Class),「人」能夠看作對象(Object)。
跳出神話,來到真實世界。
咱們目能所及的事物均可以看作是「對象」,好比說你用的桌子、坐的椅子、玩的電腦、養的狗……這些一個個真實存在,你能摸到的物品都是對象。
狗有千千萬……高的、矮的、胖的、瘦的、黑色的、白色的等各不相同,可是總能在這些不一樣的狗之中找到相同的特性,這些不一樣品種的狗咱們把它統稱爲「狗」。「狗」即爲類,而咱們養的真實存在的狗爲對象。
總結一下:
類是對一類具備共同特徵的事物的抽象,是一類事物的統稱,是一個抽象概念(好比「人類」這個名詞)。
對象是這類事物相對應的具體存在的實體,是一個具體存在的事物(好比「行小觀」這個具體的人)。
類是建立單個對象時的藍圖、模板。
當咱們說到「狗」這個類的時候,會很天然地想到和狗相關的一些特色和習性。
好比,名字、品種、顏色、年齡等,這些是屬性。
還有,吠叫、看門等,這些是行爲。
一個類包括了屬性和行爲,行爲能夠操縱屬性。
對應到代碼中,屬性即爲成員變量,行爲即爲成員方法,成員方法能夠操縱成員變量。
下面是一個具體的類:
程序2-1 /** * 狗類 * @author Xing Xiaoguan */ public class Dog { //屬性——成員變量 String name; int age; int legs; //行爲——成員方法 //行爲——吠叫 public void say() { System.out.println("我是" + name + "汪汪汪"); } //行爲——看門 public void watchDoor() { System.out.println("趕走陌生人"); } }
類是一個抽象概念,而對象則是一個具體的實例。
以狗爲例,咱們養的不多是「一類狗」,而是在和一隻「具體的狗」玩耍,好比說哮天犬。
回到女媧(類)造人(對象)這個神話中,人是以女媧爲模板被造出來的,女媧造人的過程,即由類構造對象的過程稱爲建立類的實例(instance)。
程序2-2 public static void main(String[] args) { Dog dog = new Dog();//建立類的實例,對象dog dog.name = "哮天犬"; dog.age = 2; dog.legs = 4; dog.say(); }
對於被建立出來的對象而言,它們都不同,每個特定的對象(實例)都有一組特定的屬性值(成員變量),這些屬性值的集合就是這個對象的當前狀態,只要對象使用者經過行爲(成員方法)向該對象發送消息,這些狀態就可能被改變。
上面這句話怎麼理解?
如今有兩隻狗(兩個對象):哮天犬和哮地犬,這兩個對象的名字、年齡等屬性不一樣,即當前狀態不一樣。每隻狗都有一個行爲:能夠「每過一年,年齡增加1歲」,當經過該行爲向哮天犬發送消息時,哮天犬的狀態就被改變了。
能夠看出,一個對象由狀態(state)和行爲(behavior)組成,對象在成員變量中存儲狀態,經過成員方法公開其行爲。
研究一個對象,咱們要去關注它處於什麼狀態?具備哪些行爲?
對象的三個主要特性:
封裝(encapsulation)是Java面向對象的三大特行之一。
一個對象具備屬性和行爲,封裝把其屬性和行爲組合在了一塊兒,可是爲何須要封裝?
上文已經介紹了,一個對象由其狀態和行爲組成。咱們回看程序2-1
,雖然這段代碼表示出了Dog
類,可是有一個很大的問題:建立出來的Dog對象的狀態很容易被改變。
好比咱們能夠直接修改程序2-2
中的對象的狀態:
dog.name = "哮地犬"; dog.legs = 3;
你的狗的名字被不懷好意的人給改了,腿也少了一條!這種事情是危險、可怕的!
咱們但願別人可以「知道」本身的狗叫什麼名字、有幾條腿等信息,可是又要防止不懷好意的人隨便「傷害」本身的狗,怎麼辦呢?答案是封裝!
將對象的狀態和行爲封裝起來,使用該對象的用戶只能經過對象自己提供的方法來訪問該對象的狀態。前面也說過,對象的當前狀態可能會改變,可是這種改變不是對象自發的,必須經過調用對象自己提供的方法來改變。若是不經過調用方法就能改變對象狀態,只能說明封裝性被破壞了。
換句話說,咱們將對象的屬性(狀態)對外隱藏起來,這些狀態可否被訪問或修改,由對象本身來決定,決定的方式就是「給對象的使用者提供可調用的方法,用戶經過這些方法來進行訪問和修改」。
程序2-1
能夠改進爲:
程序3-1 /** * 封裝後的狗類 * @author Xing Xiaoguan */ public class Dog { private String name; private int age; private int legs; public String getName() { return name; } public int getAge() { return age; } public int getLegs() { return legs; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } //行爲——吠叫 public void say() { System.out.println("我是" + name + "汪汪汪"); } //行爲——看門 public void watchDoor() { System.out.println("趕走陌生人"); } }
乍一看多了許多代碼,其實就多了兩個部分:
private
修飾符來修飾成員變量,將其私有化,確保只能在本類內被訪問到,實現隱藏。getter
方法)和修改方法(setter
方法)。其中咱們給用戶可以訪問或修改的成員變量都編寫上對應的setter或getter方法,如name
。用戶不能訪問或修改的成員變量不寫setter或getter方法便可。
/** * 實例化一隻小狗,並和它玩 */ public class Play { public static void main(String[] args) { Dog dog = new Dog(); dog.setName("哮天犬");; dog.setAge(2); dog.say(); dog.watchDoor(); } }
如今,用戶不能直接訪問或修改對象的狀態,必須經過提供的方法。對象並無給legs
變量提供setter
方法,這樣用戶就只能訪問狗有幾條腿,可是不能修改。狗腿被人「偷」了的事情也不會再發生了。
並且,當咱們使用setter方法修改爲員變量時,能夠進行其餘的操做,如錯誤檢查。好比設置age
變量時:
//狗的平均壽命爲10~15年,太大了不合理 public void setName(String name) { if (name > 0 && name < 30) this.name = name; }
如今,Dog
類就比較安全了。
由上面的代碼能夠看出,若是要訪問或修改爲員變量,須要:
封裝還有一個優勢就是:對外隱藏了具體實現,這樣的好處就是:咱們能夠修改內部實現,除了修改了該類的方法外,不會影響其餘代碼。
封裝使對象對外變成了一個「黑箱」,用戶只會使用,但不清楚內部狀況。
前面買水的例子也體現了封裝思想:行小觀買水的方式有不少,走路去、騎車去、甚至找比人幫忙,可是他改變買水的方式並不會對我形成影響。
生活中除了狗,還有許多其餘動物,好比貓、兔子……
程序3-2 /** * 貓類 * @author Xing Xiaoguan */ public class Cat { private String name; private int age; private int legs; private String owner;//主人 //getters and setters…… //行爲——叫 public void say() { System.out.println("我是" + name + "喵喵喵"); } //行爲——捉老鼠 public void catchMouse() { System.out.println("捉到一隻老鼠"); } }
程序2-5 /** * 兔子類 * @author Xing Xiaoguan */ public class Rabbit { private String name; private int age; private int legs; private String home;//住址 //getters and setters…… //行爲——叫 public void say() { System.out.println("我是" + name + "咕咕咕"); } //行爲——搗藥 public void makeMedicine() { System.out.println("在" + home + "搗藥"); } }
寫完這兩個類,發現有許多屬性和方法是重複的,若是須要再寫100個動物的類,那得
這些動物形態萬千,可是它們都被統稱爲「動物」,也就是說,咱們仍能在它們身上找出相同的特色,好比它們都有名字、年齡、腿、能發出聲音……
前面介紹類的時候已經說了,類是對一類具備共同特徵的事物的抽象,因此此時咱們還能從狗、貓、兔子這些類中再抽象出一個類——動物類。
程序3-3 /** * 動物類 * @author Xing Xiaoguan */ public class Animal { private String name; private Integer age; private Integer legs; public void say() { System.out.println("我是"+name+"發出聲響"); } //setters and getters…… }
這個更抽象的類就是父類,而狗、貓、兔子類是子類。子類可使用extends
關鍵字繼承父類的屬性和方法,這意味着相同的代碼只須要寫一遍。
程序3-4 /** * 狗類繼承父類 * @author Xing Xiaoguan */ public class Dog extends Animal{ //行爲——吠叫 public void say() { System.out.println("我是" + getName() + "汪汪汪"); } //行爲——看門 public void watchDoor() { System.out.println("趕走陌生人"); } }
程序3-5 /** * 貓類繼承父類 * @author Xing Xiaoguan */ public class Cat extends Animal { private String owner; public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } //行爲——喵喵叫 public void say() { System.out.println("我是" + getName() + "喵喵喵"); } //行爲——捉老鼠 public void catchMouse() { System.out.println("捉到一隻老鼠"); } }
程序3-6 /** * 兔子類繼承父類 * @author Xing Xiaoguan */ public class Rabbit extends Animal { private String home; public String getHome() { return home; } public void setHome(String home) { this.home = home; } //行爲——叫 public void say() { System.out.println("我是" + getName() + "咕咕咕"); } //行爲——搗藥 public void makeMedicine() { System.out.println("在" + home + "搗藥"); } }
觀察上面的子類和父類,能夠發現:
使用繼承的好處:
看下面一段代碼,咱們來直觀體驗什麼是多態。
程序3-7 public static void main(String[] args) { Animal dog = new Dog(); dog.setName("哮天犬"); dog.say(dog.getName()); Animal cat = new Cat(); cat.setName("加菲貓"); cat.say(); Animal rabbit = new Rabbit(); rabbit.setName("玉兔"); rabbit.say(); } 運行,輸出: 我是哮天犬汪汪汪 我是加菲貓喵喵喵 我是玉兔咕咕咕
Dog
、Cat
和Rabbit
都是繼承了Animal
父類。Dog
、Cat
和Rabbit
都重寫了Animal
的say(String name)
方法。使用多態應當注意一下幾點:Animal dog = new Dog()
在多態中,子類對象只能調用父類中定義的方法,不能調用子類中獨有的方法。
好比dog
不能調用watchDoor()
方法。
在多態中,子類能夠調用父類的全部方法。
在多態中,子類若是重寫了父類的方法,那麼子類調用該方法時,調用的是子類重寫的方法。
在上面的代碼中,狗、貓、兔子對象都運行了say方法,可是輸出不一樣。
由此看出,不一樣的對象的同一行爲具備不一樣的表現形式,這就是多態。
在實際的本例中,咱們能夠理解爲:動物Animal
,他們都會叫出聲。若是是狗,則叫的是汪汪汪;若是是貓,則叫的是喵喵喵;若是是兔子,則叫的是咕咕咕。
面向對象思想使咱們在編程更加貼近現實世界,類是現實世界的抽象,對象則是一個個具體的事物。
封裝使一個個具體的事物更加獨立,繼承則使一些相似的事物之間具備聯繫,而多態則使事物的行爲更加靈活多樣。
面向對象編程提升了軟件的重用性、靈活性、擴展性。
若有錯誤,還請指正
參考資料:
The Java Tutorials
維基百科
百度百科
Java核心技術 卷1
文章首發於公衆號「行人觀學」