面向物件概述以及三大特徵
一、類與物件的概述
現實世界中是如何描述一個事物的呢?
舉例:學生
姓名,年齡,性別…
學習,吃飯,睡覺
屬性:該事物的描述資訊
行為:該事物能夠做什麼
我們學習程式語言,是為了模擬現實世界的事物的。而我們學習的程式語言Java中最基本的單位是:類。所以,我們就應該把事物通過類來體現出來:
由此,我們就得到了現實世界事物和類的對應關係:
事物: 類:
屬性 成員變數
行為 成員方法
類:是一組相關的屬性和行為的集合。是一個抽象的概念。
格式:
修飾符 class 類名 {
構造器……
方法……
欄位……
}
物件:是該類事物的具體表現形式。具體存在的個體。
格式:類名 物件名 = new 類名();
Person p = new Person();
舉例:
學生:類
班長:物件
類:對現實生活中事物的描述。
物件:實實在在存在的類的個體。
二、什麼是面向物件
1:面向物件思想
面向物件是基於面向過程的程式設計思想。
面向過程:強調的是每一個功能的步驟
面向物件:強調的是物件,然後由物件去呼叫功能
2:面向物件的思想特點
A:是一種更符合我們思想習慣的思想 B:可以將複雜的事情簡單化 C:將我們從執行者變成了指揮者 舉例: 買電腦: 面向過程:我的瞭解電腦--瞭解我自己的需求--找對應的引數資訊--去中關村買電腦--討價還價--買回電腦 面向物件:我知道我要買電腦-- 班長去給我買 -- 班長就買回來了 洗衣服: 面向過程:把衣服脫下--找一個盆--放點洗衣粉--加點水--把衣服扔進去--搓一搓--清洗衣服--擰乾--晾起來 面向物件:把衣服脫下--開啟全自動洗衣機--扔進去--一鍵即可--晾起來 吃飯: 面向過程:去超市買菜--摘菜--洗菜--切菜--炒菜--盛起來--吃 面向物件:上飯店吃飯,你--服務員(點菜)--廚師(做菜)--服務員(端菜)--吃
3.:開發,設計,特徵
面向物件開發
就是不斷的建立物件,使用物件,指揮物件做事情。
面向物件設計
其實就是在管理和維護物件之間的關係。
面向物件特徵
封裝(encapsulation)
繼承(inheritance)
多型(polymorphism)
三、自定義類
事物:
屬性 事物的資訊描述
行為 事物的功能
類:
成員變數 事物的屬性
成員方法 事物的行為
定義一個類,其實就是定義該類的成員變數和成員方法。
實際上,類的一般組成:
成員變數
構造方法
成員方法
根據返回值:
void型別
非void型別
形式引數:
空參方法
非空參方法
一個標準程式碼的最終版。
學生類:
成員變數:
name,age
構造方法:
無參,帶兩個參
成員方法:
getXxx()/setXxx()
show():輸出該類的所有成員變數值
給成員變數賦值:
A:setXxx()方法
B:構造方法
輸出成員變數值的方式:
A:通過getXxx()分別獲取然後拼接
B:通過呼叫show()方法搞定
1、成員變數和區域性變數的區別?
A:在類中的位置不同
成員變數:在類中方法外
區域性變數:在方法定義中或者方法宣告上
B:在記憶體中的位置不同
成員變數:在堆記憶體
區域性變數:在棧記憶體
C:生命週期不同
成員變數:隨著物件的建立而存在,隨著物件的消失而消失
區域性變數:隨著方法的呼叫而存在,隨著方法的呼叫完畢而消失
D:初始化值不同
成員變數:有預設初始化值
區域性變數:沒有預設初始化值,必須定義,賦值,然後才能使用。
注意事項:
區域性變數名稱可以和成員變數名稱一樣,在方法中使用的時候,採用的是就近原則。
2、形式引數的問題:
基本型別:形式引數的改變不影響實際引數
引用型別:形式引數的改變直接影響實際引數
如果你看到了一個方法的形式引數是一個類型別(引用型別),這裡其實需要的是該類的物件。
3、匿名物件:
就是沒有名字的物件。
A:呼叫方法,僅僅只呼叫一次的時候。
注意:呼叫多次的時候,不適合。
匿名呼叫的好處
匿名物件呼叫完畢就是垃圾。可以被垃圾回收器回收。
B:匿名物件可以作為實際引數傳遞
4、建立物件做了哪些事情:
Student s = new Student();
A:把Student.class檔案載入到記憶體
B:在棧記憶體給s變數開闢了一個空間
C:在堆記憶體為學生物件申請了一個空間
D:給成員變數進行了預設初始化。null、0
E:給成員變數進行顯示初始化。張三,23
F:通過構造方法給成員變數進行初始化。李四,24
G:資料初始化完畢,然後把堆記憶體的地址值賦值給棧記憶體的s變數。
四、面向物件三大特徵
1.封裝(encapsulation)
封裝:是指隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。
->好處:將變化隔離
便於使用
提高複用性
提高安全性
->封裝原則:
將不需要對外提供的內容都隱藏起來
把屬性都隱藏,提供公共方法對其訪問
private許可權修飾符
可以修飾成員變數和成員方法
被其修飾的成員只能在本類中被訪問
this關鍵字
this: 是當前類的物件引用。簡單的記,它就代表當前類的一個物件。
當定義類中功能時,該函式內部要用到呼叫該函式的物件時,這時用this來表示這個物件。但凡本類功能內部使用到了本類物件,都用this表示。
注意:(1)誰呼叫這個方法,在該方法內部的this就代表誰。
(2)用this呼叫建構函式,必須定義在建構函式的第一行。因為建構函式是用於初始化的,所以初始化動作一定要執行。否則編譯失敗。
this的場景:
解決區域性變數隱藏成員變數
static關鍵字:
static是一個修飾符,用於修飾成員變數和成員函式上。可以直接類名呼叫。
特點:
①隨著類的載入而載入,先有靜態,後有物件產生
②被所有物件所共享。
③可以直接被類名所呼叫。
弊端:
①有些資料是物件特有的資料,是不可以被靜態修飾的。因為那樣的話,特有資料會變成物件的共享資料。這樣對事物的描述就出了問題。所以,在定義靜態時,必須要明確,這個資料是否是被物件所共享的。
②靜態方法只能訪問靜態成員,不可以訪問非靜態成員。
因為靜態方法載入時,優先於物件存在,所以沒有辦法訪問物件中的成員。
③靜態方法中不能使用this,super關鍵字。
因為this代表物件,而靜態在時,有可能沒有物件,所以this無法使用。
④主函式是靜態的。
靜態成員變數又稱為類變數,而非靜態成員變數成為例項變數。
成員變數和靜態變數的區別:
①成員變數所屬於物件。所以也稱為例項變數。
靜態變數所屬於類。所以也稱為類變數。
②成員變數存在於堆記憶體中。
靜態變數存在於方法區中。
③例項變數生命週期短,隨著物件建立而存在,隨著物件被回收而消失。
靜態變數生命週期長,隨著類的載入而存在,隨著類的消失而消失。
④成員變數只能被物件所呼叫 。
靜態變數可以被物件呼叫,也可以被類名呼叫。
所以,成員變數可以稱為物件的特有資料,靜態變數稱為物件的共享資料。
static關鍵字注意事項
A:在靜態方法中是沒有this關鍵字的
如何理解呢?
靜態是隨著類的載入而載入,this是隨著物件的建立而存在。
靜態比物件先存在。
B:靜態方法只能訪問靜態的成員變數和靜態的成員方法
靜態方法:
成員變數:只能訪問靜態變數
成員方法:只能訪問靜態成員方法
非靜態方法:
成員變數:可以是靜態的,也可以是非靜態的
成員方法:可是是靜態的成員方法,也可以是非靜態的成員方法。
簡單記:
靜態只能訪問靜態。
構造方法:
建立物件時,給物件的資料進行初始化
格式:
A:方法名與類名相同
B:沒有返回值型別,連void都沒有
C:沒有具體的返回值
構造方法的注意事項:
A:如果我們沒有給出構造方法,系統將自動提供一個無參構造方法。
B:如果我們給出了構造方法,系統將不再提供預設的無參構造方法。
注意:這個時候,如果我們還想使用無參構造方法,就必須自己給出。建議永遠自己給出無參構造方法
給成員變數賦值有兩種方式:
A:setXxx()
B:構造方法
兩個小問題:
變數什麼時候定義為成員變數:
如果這個變數是用來描述這個類的資訊的,那麼,該變數就應該定義為成員變數。
原因:類是一組相關的屬性和行為的集合,並且類是通過事物轉換過來的,而類中的成員變數就是事物的屬性,是用來描述事物的。同理:成員變數其實是用來描述類的。
變數到底定義在哪裡好呢?
變數的範圍是越小越好。因為能及時的被回收。
main方法的格式講解:
public static void main(String[] args) {…}
public:公共的,訪問許可權是最大的。由於main方法是被jvm呼叫,所以許可權要夠大。
static:靜態的,不需要建立物件,通過類名就可以。方便jvm的呼叫。
void:因為我們曾經說過,方法的返回值是返回給呼叫者,而main方法是被jvm呼叫。你返回內容給jvm沒有意義。
main:是一個常見的方法入口。我見過的語言都是以main作為入口。
String[] args:這是一個字串陣列
程式碼塊
(1)用{}括起來的程式碼。
(2)分類:
A:區域性程式碼塊
區域性位置,用於限定變數的生命週期,及早釋放,提高記憶體利用率。
B:構造程式碼塊
在類中的成員位置,用{}括起來的程式碼。每次呼叫構造方法執行前,都會先執行構造程式碼塊。
把多個構造方法中相同的程式碼可以放到這裡,每個構造方法執行前,首先執行構造程式碼塊。
C:靜態程式碼塊
在類中的成員位置,用{}括起來的程式碼,只不過它用static修飾了。
對類的資料進行初始化,僅僅只執行一次。
(3)靜態程式碼塊,構造程式碼塊,構造方法的順序問題?
靜態程式碼塊 > 構造程式碼塊 > 構造方法
類的初始化順序如下:
父類靜態變數——>父類靜態塊——>子類靜態變數——>子類靜態塊——>父類變數
——>父類普通塊父類建構函式(子類例項化時先要呼叫父類建構函式)
——>子類變數——>子類普通塊——>子類建構函式
如果有多個初始化塊,則按照程式碼先後順序執行。
2.繼承(inheritance)
(1)把多個類中相同的成員給提取出來定義到一個獨立的類中。然後讓這多個類和該獨立的類產生一個關係,
這多個類就具備了這些內容。這個關係叫繼承。
(2)Java中如何表示繼承呢?格式是什麼呢?
A:用關鍵字extends表示
B:格式:
class 子類名 extends 父類名 {}
(3)繼承的好處:
A:提高了程式碼的複用性
B:提高了程式碼的維護性
C:讓類與類產生了一個關係,是多型的前提
(4)繼承的弊端:
A:讓類的耦合性增強。這樣某個類的改變,就會影響其他和該類相關的類。
原則:低耦合,高內聚。
耦合:類與類的關係
內聚:自己完成某件事情的能力
B:打破了封裝性
(5)Java中繼承的特點
A:Java中類只支援單繼承
B:Java中可以多層(重)繼承(繼承體系)
(6)繼承的注意事項:
A:子類不能繼承父類的私有成員
B:子類不能繼承父類的構造方法,但是可以通過super去訪問
C:不要為了部分功能而去繼承
(7)什麼時候使用繼承呢?
A:繼承體現的是:is a的關係。
B:採用假設法
(8)Java繼承中的成員關係
A:成員變數
a:子類的成員變數名稱和父類中的成員變數名稱不一樣,這個太簡單
b:子類的成員變數名稱和父類中的成員變數名稱一樣,這個怎麼訪問呢?
子類的方法訪問變數的查詢順序:
在子類方法的區域性範圍找,有就使用。
在子類的成員範圍找,有就使用。
在父類的成員範圍找,有就使用。
找不到,就報錯。
B:構造方法
a:子類的構造方法預設會去訪問父類的無參構造方法
是為了子類訪問父類資料的初始化
b:父類中如果沒有無參構造方法,怎麼辦?
子類通過super去明確呼叫帶參構造
子類通過this呼叫本身的其他構造,但是一定會有一個去訪問了父類的構造
讓父類提供無參構造
C:成員方法
a:子類的成員方法和父類中的成員方法名稱不一樣,這個太簡單
b:子類的成員方法和父類中的成員方法名稱一樣,這個怎麼訪問呢?
通過子類物件訪問一個方法的查詢順序:
在子類中找,有就使用
在父類中找,有就使用
找不到,就報錯
super關鍵字
1.在Java中,this通常指當前物件,super則指父類的。
2.子類的建構函式如果要引用super的話,必須把super放在函式的首位.
3.在Java中,有時還會遇到子類中的成員變數或方法與超類(有時也稱父類)中的成員變數或方法同名。因為子類中的成員變數或方法名優先順序高,所以子類中的同名成員變數或方法就隱藏了超類的成員變數或方法,但是我們如果想要使用超類中的這個成員變數或方法,就需要用到super.
4.用super直接傳遞引數
this和super的異同
1)super(引數):呼叫基類中的某一個建構函式(應該為建構函式中的第一條語句)
2)this(引數):呼叫本類中另一種形成的建構函式(應該為建構函式中的第一條語句)
3)super: 它引用當前物件的直接父類中的成員(用來訪問直接父類中被隱藏的父類中成員資料或函式,基類與派生類中有相同成員定義時如:super.變數名 super.成員函資料名(實參)
4)this:它代表當前物件名(在程式中易產生二義性之處,應使用this來指明當前物件;如果函式的形參與類中的成員資料同名,這時需用this來指明成員變數名)
5)呼叫super()必須寫在子類構造方法的第一行,否則編譯不通過。每個子類構造方法的第一條語句,都是隱含地呼叫super(),如果父類沒有這種形式的建構函式,那麼在編譯的時候就會報錯。
6)super()和this()類似,區別是,super()從子類中呼叫父類的構造方法,this()在同一類內呼叫其它方法。
7)super()和this()均需放在構造方法內第一行。
8)儘管可以用this呼叫一個構造器,但卻不能呼叫兩個。
9)this和super不能同時出現在一個建構函式裡面,因為this必然會呼叫其它的建構函式,其它的建構函式必然也會有super語句的存在,所以在同一個建構函式裡面有相同的語句,就失去了語句的意義,編譯器也不會通過。
10)this()和super()都指的是物件,所以,均不可以在static環境中使用。包括:static變數,static方法,static語句塊。
11)從本質上講,this是一個指向本物件的指標, 然而super是一個Java關鍵字。
方法重寫
方法重寫:子類中出現了和父類中方法宣告一模一樣的方法。
子類物件呼叫方法的時候:
先找子類本身,再找父類。
方法重寫的應用:
當子類需要父類的功能,而功能主體子類有自己特有內容時,可以重寫父類中的方法。
這樣,即沿襲了父類的功能,又定義了子類特有的內容。
方法重寫的注意事項
A:父類中私有方法不能被重寫
因為父類私有方法子類根本就無法繼承
B:子類重寫父類方法時,訪問許可權不能更低
最好就一致
C:父類靜態方法,子類也必須通過靜態方法進行重寫
子類重寫父類方法的時候,最好宣告一模一樣。
final關鍵字
(1)由於繼承中方法有一個現象:方法重寫。所以,父類的功能,就會被子類給覆蓋調。
有些時候,我們不想讓子類去覆蓋掉父類的功能,只能讓他使用。針對這種情況,Java就提供了一個關鍵字:final
final:最終的意思。常見的是它可以修飾類,方法,變數。
(2)特點:
A:它修飾的類,不能被繼承。
B:它修飾的方法,不能被重寫。
C:它修飾的變數,是一個常量。
(3)面試相關:
A:區域性變數
a:基本型別 值不能發生改變
b:引用型別 地址值不能發生改變,但是物件的內容是可以改變的
B:初始化時機
a:只能初始化一次。
b:常見的給值
定義的時候。(推薦)
構造方法中。
兩道面試題:
1:方法重寫和方法過載的區別?方法過載能改變返回值型別嗎?
方法重寫:
在子類中,出現和父類中一模一樣的方法宣告的現象。
方法過載:
同一個類中,出現的方法名相同,引數列表不同的現象。
方法過載能改變返回值型別,因為它和返回值型別無關。
Override:方法重寫
Overload:方法過載
2:this關鍵字和super關鍵字分別代表什麼?以及他們各自的使用場景和作用。
this:代表當前類的物件引用
super:代表父類儲存空間的標識。(可以理解為父類的引用,通過這個東西可以訪問父類的成員)
場景:
成員變數:
this.成員變數
super.成員變數
構造方法:
this(…)
super(…)
成員方法:
this.成員方法
super.成員方法
3.多型(polymorphism)
多型是指同一個物件在不同時刻體現出來的不同狀態。
(1)實現多型的機制:
父類的引用變數可以指向子類的例項物件,而程式呼叫的方法在執行期才動態繫結,就是引用變數所指向的真正例項物件的方法,也就是記憶體里正在執行的那個物件的方法,而不是引用變數的型別中定義的方法。
多型的作用:把不同的子類物件都當作父類來看,可以遮蔽不同子類物件之間的差異,寫出通用的程式碼,做出通用的程式設計,以適應需求的不斷變化。
(2)多型的前提:
A:有繼承或者實現關係。
B:有方法重寫。
C:有父類或者父介面引用指向子類物件。
多型的分類:
a:具體類多型
class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
b:抽象類多型
abstract class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
c:介面多型
interface Fu {}
class Zi implements Fu {}
Fu f = new Zi();
(3)多型中的成員訪問特點
A:成員變數
編譯看左邊,執行看左邊
B:構造方法
子類的構造都會預設訪問父類構造
C:成員方法
編譯看左邊,執行看右邊
D:靜態方法
編譯看左邊,執行看左邊
為什麼?
因為成員方法有重寫。
(4)多型的好處:
A:提高程式碼的維護性(繼承體現)
B:提高程式碼的擴充套件性(多型體現)
(5)多型的弊端:
父不能使用子的特有功能。
現象:
子可以當作父使用,父不能當作子使用。
多型中引用變數型別轉換
向上轉型(子類→父類):(自動完成)
父類名稱 父類物件 = 子類例項 ;
向下轉型(父類→子類):(強制完成)
子類名稱 子類物件 = (子類名稱)父類例項 ;
論壇武俠例子幫助理解:
繼承:你有一個武林高手師父,人稱“玉羅剎”,然後你從你師傅那裡學到了他的全部武功。
擴充套件:後來你又自創了你自己的武功。
多型:你現在可以用你師傅的名號“玉羅剎”去行俠仗義,你師父會的你都會。別人可以認為你就是你師父。但是,你不能使用你自己的武功,因為你還帶著“玉羅剎”的面具。如果你想要使用自己的武功,就必須先拿下面具(使用強制轉換,將父類物件引用轉換成子類物件型別)。
java不允許的:你師父不會使用你的武功,所以你師父不能偽裝成你(父類物件不能強轉成子類物件型別)
然而,你的師傅可以收很多的徒弟,每個徒弟都可以修煉自己的武功,所以到最後,你師父能幹的事,他的徒弟們都可以取代他的位置
class A//玉羅剎
{
public void Jiuyinzhenjing()//九陰真經
{
System.out.println("this is A Say");
}
}
class B extends A//你繼承了你師父的全部武學
{
public void Rulaishenzhang()//如來神掌
{
System.out.println("this is B Sing");
}
}
public class Duotai
{
public static void main(String[] args)
{
A shifu = new B();
shifu.Jiuyinzhenjing();//使用“玉羅剎”的名號使用九陰真經
B tudi= new B();
tudi.Jiuyinzhenjing();//使用師父的九陰真經
tudi.Rulaishenzhang();//自己使用如來神掌
//a.Rulaishenzhang();//不可以,你師父不會如來神掌
B you;
you = (B)shifu;//你拿下面具,露出真面目,可以使用如來神掌了
you.Rulaishenzhang();
Sharen(shifu);//師父用九陰真經去殺了10000人
Sharen(tudi);//徒弟也可以用九陰真經去殺10000人
}
public static void Sharen(A a)//師父的絕學九陰真經殺人
{
a.Jiuyinzhenjing();
System.out.println("使用九陰真經殺了10000人!");
}
}