面對對象概述
今天主要分享一下java除語法之外核心的部分——面對對象,之前聽過好多小夥伴都說理解不了,調侃面向對象簡直是面向深淵,但其實只是一種思想的轉變,希望這篇文章可以能讓你理解面向對象的這種思想
理解面對對象
首先面對象,是一種思考的方法,是一種思想,是通過早起的面向過程衍化而來,面對過程:強調的是功能行爲,面對對象:講功能封裝成對象,強調的是具備功能的對象
一個很經典的案例,把大象裝進冰箱裏需要分幾個步驟呢,如果按照面向過程的思維方式就是三步:1.把冰箱門打開 2.把大象塞進去 3.關上冰箱門,這裏我們強調的是過程,打開動作、存儲(塞大象)動作、關閉動作,注重的過程當中涉及的行爲,也就是功能,那如果按照面對對象的思想就是:打開、存儲、關閉都是冰箱的一個行爲,也就是說我們只需要操作冰箱具備的功能就可以了,我們就把這些功能定義在冰箱當中,冰箱就是一個實實在在存在的東西,我們就稱之爲——對象,現在再去看,就已經不是打開、存儲和關閉了,而是一個事物——冰箱本身,而冰箱就已經具備了,打開、存儲和關閉,也就是說,有了冰箱,就有了這些功能,看下圖
左邊(面對過程)強調的是打開、存儲和關閉的動作,而右邊(面對對象)強調的就是對象本身,這樣做會把複雜的事情變得簡單化,面向過程的話需要注重好多的動作,而面向過程我們只需要注重一個事物,那就是對象本身,從一個執行者(面對過程)的角色,變成了一個指揮者(面對對象)
類和對象的關係
講解面對對象需要圍繞面對對象的三大特徵:封裝、繼承、多態來講解,在下文這些特徵都會依次講解,請耐心閱讀
類:java語言用來描述事物的一種方法,對現實中生活事物的一種描述
對象:這類事物實實在在存在的個體
舉個栗子:比如一個男人和一個女人,我們需要來描述這個對象,那其實男人和女人都是我們現實生活中的事物,描述其實就是對具體對象提取共性的事物,籠統地講就是對具體事物的一個抽象,也就是說我們只要找到男人和女人的共性事物來描述就可以了,比如男人和女人都是人類,他們都具備吃飯、睡覺、思考的功能,雖然這樣描述起來不是很具體,但是這類事物就已經大致描述完了,因爲如果要具體描述的話,就要落實到每個人身上,比如每個人都有不同的姓名、年齡、身高、思維方式等。這裏的人類就是一個類,具體的每個人就是一個對象。
將上面的內容映射到java裏面,描述就是通過class定義的類,具體對象就是對應java在堆內存中用new建立的對象實體
現在咱們實戰演練一下,小測試:描述一個汽車;
public class Study{
public static void main(String[] args){
//生產汽車,通過new操作符來完成
Car c = new Car();//產生一個汽車對象c 這個對象就已經具備了顏色和輪胎數量
//講已有的車的顏色改成藍色,指揮對象使用,指揮方式:對象名.成員
c.color="blue";
c.run();//輸出內容
}
}
//類的概念可以理解爲是車的設計圖紙
//對象的概念就是每一輛生產出的車
//定義類就是在描述事物,就是在描述屬性和行爲
//而屬性和行爲就是類中的成員 成員變量和成員方法
class Car{
//描述汽車的顏色
String color = "red";
//輪胎數量
int nums = 4;
//行爲(行爲對應的是一個方法)
void run(){
System.out.println("顏色:"+color+"---輪胎數量:"+nums);
}
}
面對對象三大特性之——封裝
概念&案例
封裝是指隱藏對象的屬性和實現細節,僅對外提供公共的訪問方式,便於使用,提高重用性和安全性
方法就是java中最小的封裝體
舉個栗子:我有一個編寫教程功能是對外提供的,你不用在意我這個功能是怎麼實現的,也就是說你看我教程不用知道我是怎麼學到的這些知識,你只需要調用我的編寫教程的功能,增加一些你的知識量,除此之外我還有打籃球功能,但是我打籃球的這個功能是不對外提供的,也就是說你無法調用的我籃球功能和你打籃球。
老樣子,來個經典案例,java中23種設計模式之一的——單例設計模式:
一個類裏面只允許有一個對象,也就是說多個對象操作的類都是同一個,在堆空間內只有一個實體,保證對象在內存中的唯一性,這種設計模式就是實現了一個類在內存中只有一個對象
public class Study{
public static void main(String[] args){
Single s = Single.getInsatance();//使用類名獲取靜態方法返回的對象
}
}
//首先 禁止其他程序建立對象
//讓其他程序可以訪問到類對象 ,所以在本類中,自定義一個對象
//爲了方便其他程序對自定義對象的訪問,對外提供一個訪問方式
//代碼體現如下
class Single{
private Single{//構造函數私有化
//在類中創建一個本類對象
static Single s = new Single(); //因爲下面的靜態方法只能訪問靜態成員,所以static修飾
}
//提供一個方法可以獲取該對象
public static getInsatance(){ //因爲無法創建對象,所以使用靜態用類名創建
return s;
}
}
單例設計模式分爲"懶漢式和餓漢式",本演示採用餓漢式,有興趣的話可以在下面評論,我可以補充懶漢式的內容
面對對象三大特性之——繼承
繼承需要注意的地方
繼承在開發中其實用到的地方相對較多,一般在寫代碼的時候發現代碼中存在重複代碼,需要向上抽取的時候,就會考慮到繼承,但稍有不注意也會引起很大的問題,繼承是一把雙刃劍,要慎用,所以繼承需要注意以下事項:
- 單一繼承性(在Java中是不支持多繼承的,通俗的說子類只能有一個父類,而父類可以有很多子類,但接口和接口之間是支持多繼承的,接口概念會在接下來的教程裏面講解)
- 支持多層繼承(繼承可以一直傳下去,子類有父類,父類又有父類)
- 如果父類成員使用private修飾,那麼子類不能被繼承(private修飾符,代表被修飾的內容只在本類有效)
- 如果一個子類繼承了父類的屬性和方法還可以有自己特有的屬性和方法(不光有父類的屬性(可繼承的)和方法(可繼承的),也有自己獨有的屬性和方法)
- 當子類和父類的成員變量重名的時候,子類優先
案例
先看下面定義的兩個類,一個是學生(student),一個是工人(worker),寫完後發現,這兩個類是有一些共性的內容,比如學生和工人都擁有姓名和年齡,是不是有種方法可以讓我們把這些共性內容定義在一個單獨的類裏,然後讓我們的這兩個類和它產生一些關係,然後讓學生和工人類都具備這些特徵,這就是向上抽取的過程
這就是我們的第二個概念了,繼承(extends),這個就能夠讓學生和工人對 人(Person)這個類產生關係,學生和工人就是人這個 類的子類,人就是這個學生類的父類/超類/基類,子繼承父,所以他們就可以共用人的特徵,姓名和年齡
很顯而易見了,繼承:
- 提高了代碼的複用性
- 提高了擴展性
- 提高了可維護性
- 讓類和類之間產生了關係(纔有了多態的概念,多態會在下文講到)
注意事項:千萬不要爲了獲取其他類的功能簡化代碼亂使用繼承功能,必須是類和類之間有所屬關係(is a)纔可以繼承
面對對象三大特性之——多態
概念
多態是指某一種事物存在多種形態,可以理解爲事物存在的多種體現形態,比如用第一個案例的“人”來表示,人就可以分爲,男人和女人,這其實就是人這類事物的不同體現形態,動物同樣也有多種形態,例如貓和狗
貓 m = new 貓();
//貓也是動物
動物 m = new 貓();
//m這個實體既具備貓的類型,也具備動物的類型
//前提是貓必須是動物的一種(繼承關係)
除了這種體現,其實在方法的重載和覆蓋上也體現了多態性(重載和覆蓋講在未來的教程裏講解,這裏僅作了解),這裏我主要講解的是對象的多態性
這裏會圍繞着多態的表現形式,多態的前提,多態的好處,多態如何應用講解
案例
首先看下面代碼:
按照之前邏輯肯定是這樣做
Cat c = new Cat();
c.eat()
Dog d = new Dog();
d.eat();
這樣做的話,如果我想讓另一隻貓吃東西,就要多創建另外一個對象,然後再調用eat()方法,這句話就在重複使用了,我們就可以封裝方法
public static void main(String[] args){
Cat c = new Cat();
func (c) ;
}
//定義一個方法,提高代碼複用性
//注意:這裏還沒有涉及到多態
public void func(Cat c){
c.eat();
}
現在又出現一個問題,狗該怎麼使用這個方法呢,有沒有一種辦法讓參數傳進去之後就可以顯示出當前傳遞的對象呢?
這種重載方式當然也可以實現,但是如果後面的類型較多的時候,例如出現了豬、牛、羊等等等等,那時候該怎麼辦呢?
這就涉及到轉化了,貓除了具備貓類型以外,他還繼承了動物,他也是動物的一種,如果用Animal來做這個事情,也是可以的
public static void main(String[] args){
Cat c = new Cat();
func( c);
}
//定義一個方法,提高代碼複用性
public void func(Animal a){
a.eat();//打印 -> 小魚乾
}
這就是一個對象具備的多種形態造成的
多態的體現:
父類的引用指向了自己的子類對象/父類的引用也可以接收自己的子類對象
多態的好處:
多態大大提高了程序的擴展性
前提:
必須是類和類之間存在關係(繼承或實現)
轉型
向上轉型和向下轉型
從基本數據類型我們能夠看出:向上轉型是可以自動完成的;向下轉型是需要強制轉換的,引用數據類型也是如此
還用剛剛的例子:
//因爲貓是動物,所以這裏自動轉型(向上轉型是可以自動完成的)
Animal a = new Cat();//類型提升(向上轉型) 把貓提升成了動物
剛剛演示了向上轉型,下面演示一下向上轉型
Animal a = new Cat();
//強制將父類的引用轉成子類類型
Cat c = (Cat) a;
c.shout();// 輸出 -> 喵喵
這裏有種轉換方式需要注意一下:
不要出現下面的這種操作,將父類對象轉換成子類類型
我們能轉換的是父類應用指向了自己的子類對象時,該應用可以被提升/轉換
多態自始至終都是子類對象在變化
Animal a = new Animal();
Cat c = new (Cat)a;
案例代碼附上:
public class Study{
public static void main(String[] args){
//因爲貓是動物,所以這裏自動轉型(向上轉型是可以自動完成的)
Animal a = new Cat();//類型提升(向上轉型) 把貓提升成了動物
Cat c = (Cat)a;
c.eat();
}
public void func(Animal a){
a.eat();
}
}
class Animal{//這裏可以修改成抽象類abstract更加形象
public void eat(){
}
}
class Cat extends Animal{
public void eat(){
System.out.print("小魚乾");
}
public void shout(){
System.out.print("喵喵");
}
}
class Dog extends Animal{
public void eat(){
System.out.print("骨頭/屎");
}
public void shout(){
System.out.print("汪汪");
}
}
總結:
Java裏面的每定義一個類實際上就相當於一種新的數據類型,就跟int ,float, String等一樣,不過是一種新定義的類型而已
文章內如有講解不清晰不理解或者不對的地方可以在評論區指出,看到一定改正,最後,感謝閱讀!