轉載請保留原文鏈接: http://dashidan.com/article/java/basic/13.html
13.Java對象
Java是`OOP`(Object Oriented Programming)面向對象編程語言. java面向對象的三大特點: 封裝, 繼承和多態.
① Java中萬事萬物皆對象?
Java中萬事萬物皆對象?
這個說法的由來, 是因爲java中所有的類默認都繼承自`Object`類. 但這個說法是`不準確`的. 很多資料中這樣說, 曾經給我帶來過很大的困擾.
Java屬於`強類型語言`, `方法`作爲屬性存在於對象中, 不作爲獨立的對象存在. 所以說在Java中萬事萬物皆對象的說法是不準確的, 方法和基本類型變量都不是對象的概念.
在一些弱類型語言比如`javascript`, 方法也可以作爲`對象`的概念來使用, 作爲參數傳遞, 這一點和java是有區別的.
提到面向對象, 不得不提到面向過程編程. 以C語言爲例, 每個方法作爲一個執行過程
存在, 面向過程編程是一種面向計算機思維, 從計算機執行的角度思考編寫代碼的編程方式.
從面向過程編程的基礎上, 發展出來了一種新的編程思想, 面向對象編程, 來應對更復雜的應用場景.面向對象編程是一種面向人
的邏輯思維的編程方式, 將代碼中的通用性抽象出來, 模擬現實世界的歸類, 建立程序模型, 編寫代碼的過程. 對於程序員更加友好, 對於複雜度更高的程序更易於控制.
在生物分類學中, 分爲界門綱目科屬種
, 按照生物的種羣和等級劃分.
貓:
動物界->脊索動物門->哺乳綱->哺乳綱->食肉目->貓科->貓屬->貓種
虎:
動物界->脊索動物門->哺乳綱->哺乳綱->食肉目->貓科->豹屬->虎種
貓
和虎
同處於貓科
, 是貓科子類中的一種, 具有貓科的全部屬性. 用Java面向對象的思想來編程可以這樣表達:貓科
作爲父類
, 貓
和虎
作爲子類
, 繼承自貓科
擁有父類貓科
的全部屬性, 獨有的屬性寫在子類
(貓類或虎類)中.
面向對象編程思想與人類的邏輯思維很接近.
③ 構造函數
構造函數是一種特殊的函數. 構造函數與類名相同, 可以有參數也可以無參數, 在對象被創建時自動執行.
1.構造函數的作用:
在創建對象時完成完成對象數據成員的初始化
爲對象數據成員開闢內存空間
爲對象創建標識符
2.默認構造函數
當用戶沒有顯式的定義構造函數時, 編譯器會爲類生成一個默認的構造函數, 稱爲默認構造函數
. 默認構造函數名與類名相同, 沒有參數.例:
public class Demo1 { // 默認構造函數 public Demo1(){ }}
默認構造函數
默認構造函數在代碼中看不到, 在編譯時自動生成.
3.構造函數的特點
無論是用戶自定義的構造函數還是默認構造函數都主要有以下特點:
在對象被創建時自動執行.
構造函數的函數名與類名相同.
沒有返回值類型、也沒有返回值.
構造函數不能被顯式調用.
4.構造函數的執行
通常是通過new
關鍵字創建對象時執行.
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 構造函數的執行 */public class Demo1 { public static void main(String args[]) { System.out.println("Demo1運行開始"); TestStructure testStructure = new TestStructure(1, "大屎蛋教程網", "dashidan.com"); int id = testStructure.getId(); String url = testStructure.getUrl(); String name = testStructure.getName(); System.out.println("id " + id + " url " + url + " name " + name); System.out.println("運行完畢"); }}/** * 測試構造方法類 */class TestStructure { private int id; private String name; private String url; /** * 構造函數 */ public TestStructure(int id, String name, String url) { /** 構造函數初始化成員變量*/ System.out.println("執行TestStructure構造函數"); this.id = id; this.name = name; this.url = url; System.out.println("id " + id + " name " + name + " url " + url); } public int getId() { return id; } public String getName() { return name; } public String getUrl() { return url; }}
輸出:
Demo1運行開始 執行TestStructure構造函數 id 1 name 大屎蛋教程網 url dashidan.com id 1 url dashidan.com name 大屎蛋教程網 運行完畢
析構函數
c++除了`構造函數`,還有`析構函數`的概念. 析構函數也是一種特殊的成員函數. 它執行與構造函數相反的操作,通常用於撤消對象時的一些清理任務,如釋放分配給對象的內存空間等. Java中只有構造函數,而沒有析構函數.
④ 參數的值傳遞與引用傳遞
Java中方法中給變量賦值有2種方式: 值傳遞和引用傳遞.傳遞基本類型,String參數時是值傳遞. 傳遞引用類型參數時, 是引用傳遞.
1.值傳遞
傳遞的是值的拷貝. 也就是說傳遞參數時, 將值拷貝了一份, 與原有變量不再有關係. 基本類型
和字符串
傳遞採用的是值傳遞.
示例代碼:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 值傳遞 */public class Demo2 { public static void main(String[] args) { int a = 1; String name = "大屎蛋"; changeValue(a, name); /** 原值不變*/ System.out.println("原值 a : " + a); /** 原字符串不變*/ System.out.println("原值 name : " + name); } public static void changeValue(int num, String str) { num++; /** 輸出改變的值, 改變了參數的值, 原值不變*/ System.out.println("changeValue num : " + num); /** 添加字符串, 改變了作爲參數傳進來的字符串的值, 原字符串不變*/ str += "dashidan.com"; System.out.println("changeValue str : " + str); }}
輸出:
changeValue num : 2 changeValue str : 大屎蛋dashidan.com 原值 a : 1 原值 name : 大屎蛋
2.引用傳遞
引用傳遞是指給索引對象賦值時, 是將對象內存的地址作爲參數傳遞. 新的對象與原有指向同一份數據. 當修改對象的變量時, 原有指向改數據的對象都發生改變
.
代碼示例:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 引用傳遞 */public class Demo3 { public static void main(String[] args) { TestObject testObject1 = new TestObject(); testObject1.setName("大屎蛋教程網"); /** 對象賦值,這裏是引用傳遞,testObject1 將對象地址賦值給 testObject2, 他們指向同一個對象*/ TestObject testObject2 = testObject1; System.out.println("修改前 testObject1 name " + testObject1.getName()); System.out.println("修改前 testObject2 name " + testObject2.getName()); /** 改變 testObject2 的name,testObject1的name也發生變化,因爲他們指向同一個數據*/ testObject2.setName("dashidan.com"); System.out.println("修改後 testObject1 name " + testObject1.getName()); System.out.println("修改後 testObject2 name " + testObject2.getName()); }}/** * 引用傳遞測試對象 */class TestObject { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
輸出:
修改前 testObject1 name 大屎蛋教程網 修改前 testObject2 name 大屎蛋教程網 修改後 testObject1 name dashidan.com 修改後 testObject2 name dashidan.com
⑤ 對象的封裝
1.隱藏實現過程
封裝是把過程和數據隱藏起來,對數據的訪問只能通過已定義的接口. 面向對象計算始於這個基本概念. 通過private
關鍵字可以將變量或者方法設置爲只有本類可以訪問, 其他類無法訪問該數據, 這樣便實現了數據的隱藏.
你有一隻寵物, 寵物的名字你不希望別人隨便改, 可以將寵物的名字設置爲私有private
.
package com.dashidan.lesson5;/** * 大屎蛋教程網-dashidan.com * * Java教程進階篇: 5.Java對象(4):封裝 */public class Pet { /** 私有屬性名字*/ private String name;}
2.公開數據訪問接口
你這隻寵物的名字編程私有之後, 你希望給他改個名字, 可以將改名的方法設置爲公開方法. 這樣就能通過統一的開放接口來設置寵物的名字, 並且可以開放一個獲取名字的接口, 來獲得這個私有的屬性值.
示例代碼:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 引用傳遞 */public class Demo3 { public static void main(String[] args) { TestObject testObject1 = new TestObject(); testObject1.setName("大屎蛋教程網"); /** 對象賦值,這裏是引用傳遞,testObject1 將對象地址賦值給 testObject2, 他們指向同一個對象*/ TestObject testObject2 = testObject1; System.out.println("修改前 testObject1 name " + testObject1.getName()); System.out.println("修改前 testObject2 name " + testObject2.getName()); /** 改變 testObject2 的name,testObject1的name也發生變化,因爲他們指向同一個數據*/ testObject2.setName("dashidan.com"); System.out.println("修改後 testObject1 name " + testObject1.getName()); System.out.println("修改後 testObject2 name " + testObject2.getName()); }}/** * 引用傳遞測試對象 */class TestObject { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
輸出:
哈拆遷
⑥ 對象的繼承
Java繼承的本質是子類繼承父類, 擁有父類的屬性和方法, 是代碼複用基礎. 子類可以有自己的方法和屬性.
1.繼承方式
通過關鍵字extends.我們以貓和狗繼承是寵物, 寵物都有走
的行爲. 貓有吃魚
的行爲, 狗有吃骨頭
的行爲爲例實現這個繼承關係.
父類Pet
:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * Pet類 作爲 父類 */public class Pet { public void walk() { System.out.println("Pet walk"); } public void eat() { System.out.println("Pet eat"); }}
子類Cat
:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * Cat 繼承自 Pet類 */public class Cat extends Pet { public void eatFish() { System.out.println("Cat eatFish."); } @Override public void eat() { System.out.println("Cat eat"); }}
子類Dog
:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * Dog 繼承自 Pet類 */public class Dog extends Pet { public void eatBone() { System.out.println("Dog eatBone."); } @Override public void eat() { System.out.println("Dog eat"); }}
執行類
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 */public class Demo5 { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); /** 調用父類的方法*/ cat.walk(); dog.walk(); /** 調用子類的方法*/ cat.eatFish(); dog.eatBone(); }}
輸出:
Pet walk Pet walk Cat eatFish. Dog eatBone.
java單根繼承
java單根繼承是指單個類只能繼承一個父類, 在c++中是多根繼承, 一個類可以同時繼承多個父類.
3.對象靜態綁定與動態綁定
綁定指的是一個方法的調用與方法所在的類(方法主體)關聯起來. java綁定分爲靜態綁定和動態綁定.
靜態綁定
靜態綁定也稱作前期綁定. java中的方法只有final
, static
, private
和構造方法
是靜態綁定
. 靜態綁定發生在編譯
時, 靜態綁定用類
信息來完成.
動態綁定
動態綁定(dynamic binding)也稱作後期綁定. java程序運行時根據具體對象的類型進行綁定. 動態綁定發生在運行
時. 動態綁定則需要用對象
信息來完成.
⑦ 對象的多態
1.多態的前提
當子類和父類存在同一個方法, 子類覆寫了父類的方法, 程序在運行時調用父類的方法還是子類覆寫的方法呢? Java的多態解決了整個問題. 運行時根據對象類型來判斷執行對應對象的方法.
多態的前提條件
1. 有繼承關係.
2. 子類重寫父類的方法.
3. 父類引用指向子類.
貓和狗都繼承自寵物類, 寵物有吃的行爲. 貓吃魚狗吃骨頭, 他們吃的行爲不一樣. 我們可以採用多態的方式來實現.
代碼示例:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程進階篇: 7.Java對象(6):靜態綁定動態綁定與多態 */public class Demo6 { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); /** * cat 和 dog 覆寫了 父類的方法 eat 調用eat方法 時動態判斷對象類型 * cat 對象 執行了 Cat中的 eat方法 * dog 對象 執行了 Dog中的 eat方法 */ cat.eat(); dog.eat(); }}
輸出:
Cat eat Dog eat
⑧ 引用當前對象與父對象
java中使用用this來引用當前對象, super引用當前對象的父對象.
1.this
使用this
獲取當前對象的索引.
省略this
如果省略this. 系統會默認自動添加加上`this.`來指向當前對象.
以下代碼中的setName
方法, 該方法中的傳入的參數name
與成員變量name
重名, 這時需要用this.name
來表明引用成員變量.
代碼示例:
package com.dashidan.lesson8;/** * 大屎蛋教程網-dashidan.com * * Java教程進階篇: 8.Java對象(7):引用當前對象與父對象:this, super * Pet類 */public class Pet { /** * 私有成員變量name */ private String name; public String getName() { /** 默認可以省略this. 系統會自動加上this.*/ return name; } public void setName(String name) { /** this.name 通過this來表明引用當前對象成員變量name*/ this.name = name; }}
2.super
super
引用當前對象的父對象
. 需要訪問父類方法或者變量的時候, 可以使用super.
來訪問.示例代碼:
cat
類:
在調用Cat
類的eat()
方法時, 方法體中通過super.eat()
調用了父類的eat()
方法. 故父類的eat()方法被執行, Pet eat.
輸出到控制檯.
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * Cat 繼承自 Pet類 */public class Cat extends Pet { public void eatFish() { System.out.println("Cat eatFish."); } @Override public void eat() { System.out.println("Cat eat"); } /** *調用父類方法 */ public void petEat() { super.eat(); }}
運行程序:
package com.dashidan.lesson8;/** * 大屎蛋教程網-dashidan.com * * Java教程進階篇: 8.Java對象(7):引用當前對象與父對象:this, super */public class Demo1 { public static void main(String[] args) { Cat cat = new Cat(); cat.eat(); }}
輸出:
Pet eat.
3.this與super在構造函數中的使用
super
和this
均需放在構造方法內第一行. 否則編譯不通過.
每個子類構造方法的第一條語句, 都是隱式調用super()
, 如果父類沒有這種形式的構造函數, 那麼在編譯的時候就會報錯.
this
和super
不能同時出現在一個構造函數裏面.
因爲this
必然會調用其它的構造函數, 其它的構造函數必然也會有super
語句的存在. 所以在同一個構造函數裏面有相同的語句, 就失去了語句的意義, 編譯器也不通過.
`static`環境不能使用`this`, `super`
this和super都指的是對象, 是動態綁定, 所以都不能在`static`環境中使用(靜態綁定)包括: static變量, static方法, static語句塊.
⑨ 抽象方法與抽象類
1.抽象方法
抽象方法是一種特殊的方法, 只有聲明, 而沒有具體的實現. 抽象方法必須用abstract
關鍵字進行修飾. 抽象方法的聲明格式爲:
public abstract void eat();
2.抽象類
如果一個類含有抽象方法,則稱這個類爲抽象類,抽象類必須在類前用abstract
關鍵字修飾.只要包含抽象方法, 必須命名爲抽象類. 抽象類也可以包含普通方法.
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 抽象方法與抽象類 * AbstractPet類 作爲 父類 抽象類 */public abstract class AbstractPet { public abstract void sleep();}
Fish
類作爲子類:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 抽象方法與抽象類 * Fish 類 繼承自 AbstractPet類 */public class Fish extends AbstractPet { /** *實現抽象方法 sleep */ @Override public void sleep() { System.out.println("Never sleep."); }}
運行類
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 抽象方法與抽象類 */public class Demo9 { public static void main(String[] args) { Fish fish = new Fish(); fish.sleep(); }}
輸出:
Never sleep.
抽象類與普通類的區別:
抽象方法必須爲
public
或protected
(private
不能被子類繼承,子類無法實現該方法), 缺省情況下默認爲public
.如果一個類繼承於一個抽象類, 則子類必須實現父類的抽象方法. 如果子類沒有實現父類的抽象方法, 則必須將子類也定義爲爲
abstract
類.
3.抽象類創建對象
抽象類不能用來創建對象嗎?
答案是:抽象類可以被用`new`的方式創建. 通過`匿名內部類`方式.
附帶真相的代碼:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 抽象類創建對象 */public class Demo10 { public static void main(String[] args) { AbstractPet abstractPet = new AbstractPet() { @Override public void sleep() { System.out.println("AbstractPet sleep"); } }; abstractPet.sleep(); }}
輸出:
AbstractPet sleep.
雖然可以用new
的方式創建抽象類, 但不建議這麼用, 這樣做失去了抽象的意義.
示例代碼中出現了`@Override`標籤.
Java中@Override標籤作用
⑩ 接口
接口interface
在軟件工程中, 泛指供其他人調用的方法或者函數. 從這裏可以體會到Java語言設計者的初衷-對行爲的抽象.
1.接口的聲明
用interface
關鍵字聲明接口.接口中可以含有變量和方法. 但是要注意, 接口中的變量會被隱式地指定爲public static final
變量. 接口中的方法必須都是抽象方法
.
public interface Pet{...}
2.接口的實現
一個類實現特定的接口需要使用implements
關鍵字. 需要實現該接口中定義的方法, 如果沒有全部實現, 需要將該類聲明爲abstract
.
public class Bird implements IPet {...}
示例代碼:
IPet
接口:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * 接口 IPet */public interface IPet { void eat();}
Bird
類:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * 接口 */public class Bird implements IPet { @Override public void eat() { System.out.println("Bird eat."); }}
運行類:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象的繼承 * 接口 */public class Demo11 { public static void main(String[] args) { Bird bird = new Bird(); bird.eat(); }}
輸出:
Bird eat.
4.抽象類和接口的區別
抽象類可以提供成員方法的實現細節, 而接口中只能存在
public abstract
方法.抽象類中的成員變量可以是各種類型, 而接口中的成員變量只能是
public static final
類型.接口中不能含有靜態代碼塊以及靜態方法, 而抽象類可以有靜態代碼塊和靜態方法.
一個類只能繼承一個抽象類, 而一個類卻可以實現多個接口.
抽象類作爲很多子類的父類, 它是一種模板式設計, 而接口是一種行爲規範.
抽象類和接口所反映的設計理念是不同的, 抽象類所代表的是
is-a
的關係, 而接口所代表的是like-a
的關係.
對象實例類型判斷
Java中的instanceof運算符是用來在運行時判斷對象是否是指定類的實例.子類實例instanceof
父類, 返回ture. 因爲繼承關係, 子類的實例也是父類的實例.instanceof
是二目運算符, 返回一個布爾值, 表示該對象是否是指定類的實例.用法:
boolean result = object instanceof class
示例代碼:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象實例類型判斷 */public class Demo12 { public static void main(String[] args) { Pet pet = new Pet(); boolean isInstance = pet instanceof Pet; System.out.println("pet instanceof Pet: " + isInstance); Cat cat = new Cat(); /** 子類也屬於父類的一個實例*/ isInstance = cat instanceof Pet; System.out.println("cat instanceof Pet: " + isInstance); }}
輸出:
pet instanceof Pet: true cat instanceof Pet: true
對象向上轉型和向下轉型
java中子類對象轉爲父類對象稱爲向上轉型, 反之稱爲向下轉型.
1.向上轉型
向上轉型時, 子類獨有的方法會遺失, 只保留父類擁有的方法. 如果子類覆寫了父類的方法則調用子類的這個方法. 向上轉型不用強制轉型.
2.向下轉型
父類引用的對象轉換爲子類類型稱爲向下轉型. 向下轉型需要強制轉型。
示例類型與轉型類型需要一致, 不一致會報錯.
示例代碼:
package com.dashidan.lesson12;/** * 大屎蛋教程網-dashidan.com * <p> * Java教程基礎篇: 12.Java對象 * 對象向上轉型和向下轉型 */public class Demo13 { public static void main(String[] args) { Cat cat = new Cat(); /** * testUpcasting 需要傳入父類Pet實例,這裏傳入的是子類cat實例的引用 * 自動向上轉型 * 由於子類Cat覆寫了父類的cat()方法,執行子類的eat()方法 */ testUpCasting(cat); /** 父類對象的引用,指向子類實例*/ Pet pet = new Cat(); /** * testUpcasting 需要傳入父類Pet實例,這裏傳入的是父類Pet的引用 * 強制向下轉型 * 由於子類Cat覆寫了父類的cat()方法,執行子類的eat()方法 */ testDownCasting((Cat) pet); } /** * 測試向上轉型 */ public static void testUpCasting(Pet pet) { pet.eat(); } /** * 測試向下轉型 */ public static void testDownCasting(Cat pet) { pet.eat(); }}
輸出:
Cat eat. Cat eat.
相關文章