概述
- 多個(gè)類中存在相同屬性和行為時(shí)宪卿,將這些內(nèi)容抽取到單獨(dú)一個(gè)類中,那么多個(gè)類無需再定義這些屬性和行為望众,只要繼承單獨(dú)的類即可匪补。
- 多個(gè)類可以稱為子類,單獨(dú)的類稱為父類或者超類烂翰。
- 子類可以訪問父類中的<font color="#FF0000">非私有的</font>屬性和行為夯缺。
- 通過extends關(guān)鍵字讓類與類之間產(chǎn)生繼承關(guān)系。
class SubDemo extends Demo{}
- 繼承的好處:
繼承的出現(xiàn)提高了代碼的復(fù)用性刽酱。
繼承的出現(xiàn)讓類與類之間產(chǎn)生了關(guān)系喳逛,提供了多態(tài)的前提。
特點(diǎn)
-
Java中支持單繼承棵里,不支持多繼承
一個(gè)子類只能有一個(gè)直接父類润文,否則若多個(gè)父類中有相同成員,會產(chǎn)生調(diào)用的不確定性殿怜。class SubDemo extends Demo{} //ok class SubDemo extends Demo1,Demo2...{} //error
-
Java支持多層繼承(繼承體系)
class A{} class B extends A{} class C extends B{}
當(dāng)要使用一個(gè)繼承體系時(shí):
1)查看該體系中的頂層類典蝌,了解該體系的基本功能
2)創(chuàng)建體系中的最子類對象,完成功能的使用 什么時(shí)候定義繼承头谜?
當(dāng)類與類之間存在著所屬("is a")關(guān)系的時(shí)候骏掀,就定義繼承。如xxx是yyy的一種柱告,則xxx extends yyy截驮。或者判斷父類中的功能是否子類都應(yīng)該具備际度,若有些不具備葵袭,則不存在繼承。不要僅為了獲取其他類中某個(gè)功能而去繼承 乖菱。
super關(guān)鍵字
this和super的用法很相似坡锡。
this代表一個(gè)本類對象的引用蓬网;super代表一個(gè)<font color="#FF0000">父類空間</font>(注意并不是代表父類對象,如下代碼鹉勒,從未定義過父類對象帆锋,子類之所以可以獲取父類的內(nèi)容,是因?yàn)槠涑钟衧uper引用)禽额。
-
當(dāng)本類中的成員和局部變量同名用this區(qū)分锯厢;當(dāng)子父類中的成員變量同名用super區(qū)分父類(如果不使用super,則默認(rèn)調(diào)用子類自己的成員變量)脯倒,子類要調(diào)用父類構(gòu)造函數(shù)時(shí)哲鸳,可以使用super關(guān)鍵字。
Example1
public class Father { int num = 4; } class Son extends Father{ int num = 5; void show() { System.out.println(this.num+"..."+super.num); } } class Demo { public static void main(String[] args) { Son s = new Son(); s.show(); } }
父類比子類先加載盔憨。
注意:
問題:父類中的私有內(nèi)容徙菠,子類是否具備——實(shí)際上子類對于父類中私有的成員變量是繼承了的,即定義子類對象時(shí)郁岩,在堆中存有父類的私有成員變量(上圖堆中fu的方框內(nèi))婿奔,只是不能直接訪問,所以確切地說问慎,應(yīng)該是子類中不能直接地訪問父類中的私有內(nèi)容萍摊。
Example2
class Person
{
private String name;
private int age;
Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
class Student extends Person
{
Student(String name, int age)
{
super(name, age);//調(diào)用父類的構(gòu)造函數(shù)
}
}
class Worker extends Person
{
Worker(String name, int age)
{
super(name, age);//調(diào)用父類的構(gòu)造函數(shù)
}
}
函數(shù)覆蓋(Override)
- 函數(shù)覆蓋發(fā)生在父類與子類之間,其函數(shù)名如叼、參數(shù)類型冰木、返回值類型必須同父類中的相對應(yīng)被覆蓋的函數(shù)嚴(yán)格一致,覆蓋函數(shù)和被覆蓋函數(shù)只有函數(shù)體不同笼恰,當(dāng)派生類對象調(diào)用子類中該同名函數(shù)時(shí)會自動調(diào)用子類中的覆蓋版本踊沸,而不是父類中的被覆蓋函數(shù)版本,這種機(jī)制就叫做函數(shù)覆蓋社证。
- 父類中的私有方法不可以被覆蓋逼龟。
- 在子類覆蓋方法中,繼續(xù)使用被覆蓋的方法可以通過super.函數(shù)名獲取追葡。
- 覆蓋注意事項(xiàng):
覆蓋時(shí)腺律,子類方法權(quán)限一定要大于等于父類方法權(quán)限。
靜態(tài)只能覆蓋靜態(tài)宜肉,或被靜態(tài)覆蓋(非靜態(tài)不能覆蓋靜態(tài)匀钧,靜態(tài)不能覆蓋非靜態(tài),否則會報(bào)編譯錯(cuò))谬返。 - 覆蓋的應(yīng)用:
當(dāng)子類需要父類的功能之斯,而功能主體子類有自己特有內(nèi)容時(shí),可以復(fù)寫父類中的方法朱浴,這樣即沿襲了父類的功能吊圾,又定義了子類特有的內(nèi)容。
子類的實(shí)例化過程
注意構(gòu)造函數(shù)不會繼承更不會覆蓋(首先函數(shù)名都不一樣...
子類的實(shí)例化過程:子類中所有的構(gòu)造函數(shù)默認(rèn)都會訪問父類中空參數(shù)的構(gòu)造函數(shù)(因?yàn)槊恳粋€(gè)構(gòu)造函數(shù)的第一行都有一條默認(rèn)的語句super()
翰蠢;之所以會有這樣一條默認(rèn)語句项乒,是因?yàn)樽宇惱^承了父類,獲取到了父類中的內(nèi)容(屬性)梁沧,所以在使用父類內(nèi)容之前檀何,要先明確父類是如何對這些數(shù)據(jù)初始化的)。
public class Father
{
public Father()
{
System.out.println("Father run");
}
}
class Son extends Father
{
public Son()
{
//super(); //默認(rèn)會調(diào)用父類的空參構(gòu)造函數(shù)
System.out.println("Son run");
}
}
當(dāng)父類中沒有空參數(shù)的構(gòu)造函數(shù)時(shí)廷支,子類的構(gòu)造函數(shù)必須通過this或者super語句指定要訪問的構(gòu)造函數(shù)频鉴。
注意:super語句必須要定義在子類構(gòu)造函數(shù)的第一行,因?yàn)楦割惖某跏蓟瘎幼饕韧瓿伞?br>
如果子類構(gòu)造函數(shù)中使用this調(diào)用了本類構(gòu)造函數(shù)恋拍,那么就沒有super了垛孔,因?yàn)閟uper和this都只能定義在第一行。但是可以保證的是施敢,子類中肯定會有其他的構(gòu)造函數(shù)訪問父類的構(gòu)造函數(shù)周荐,即子類構(gòu)造函數(shù)中至少有一個(gè)訪問父類構(gòu)造函數(shù)。
public class Father
{
public Father(int num)
{
System.out.println("Father run");
}
}
class Son extends Father
{
public Son()
{
this(4);
}
public Son(int num)
{
super(num);
System.out.println("Son run");
}
}
注意僵娃,類默認(rèn)的空參構(gòu)造函數(shù)的修飾符與類保持一致概作。
class Demo
{
/*
Demo()
{
super(); //父類為Object
return;
}
*/
}
Example1:子類的實(shí)例化過程
public class Father {
Father()
{
show();
}
public void show()
{
System.out.println("father show");
}
}
class Son extends Father
{
int num = 8;
Son()
{
super();
//分水嶺
System.out.println("Son run..." + num);
}
public void show()
{
System.out.println("Son show..." + num);
}
}
class Demo
{
public static void main(String[] args)
{
Son s = new Son();
s.show();
}
}
輸出結(jié)果為:
Son show...0
Son run...8
Son show...8
內(nèi)存圖解:
默認(rèn)初始化,成員變量num=0默怨;顯示初始化讯榕,成員變量num=8。
默認(rèn)初始化后匙睹,執(zhí)行super()愚屁,經(jīng)過父類構(gòu)造函數(shù)初始化,再進(jìn)行自己的顯示初始化痕檬,最后是自己的構(gòu)造函數(shù)初始化集绰。
注意,若子類當(dāng)中有構(gòu)造代碼塊谆棺,則其初始化的過程為:
一個(gè)對象的實(shí)例化過程:
Person p = new Person();
1栽燕、JVM讀取指定路徑下的Person.class文件,如果該類有直接父類改淑,先加載Person的父類碍岔,再加載Person進(jìn)內(nèi)存;
2朵夏、在堆內(nèi)存中開辟空間蔼啦,分配地址;
3仰猖、在堆的對象空間中捏肢,對對象中的屬性進(jìn)行默認(rèn)初始化奈籽;
4、調(diào)用對應(yīng)的構(gòu)造函數(shù)進(jìn)行初始化鸵赫;
5衣屏、在構(gòu)造函數(shù)中,先調(diào)用父類的構(gòu)造函數(shù)進(jìn)行初始化辩棒;
6狼忱、父類初始化完畢后,再對子類的屬性進(jìn)行顯示初始化一睁;
7钻弄、構(gòu)造代碼塊初始化;
8者吁、進(jìn)行子類構(gòu)造函數(shù)的特定初始化窘俺;
9、初始化完畢后复凳,將地址值賦值給引用變量(不一定是在棧中批销,如下)。
class Demo
{
Person p = new Person(); //在堆中
}
final關(guān)鍵字
- final可以修飾類染坯,方法均芽,變量。
- final修飾的類不可以被繼承单鹿。(繼承弊端:打破了封裝性掀宋。)
- final修飾的方法不可以被覆蓋。(可以用于在父類中仲锄,若有幾個(gè)方法不希望被復(fù)寫時(shí))
- final修飾的變量是一個(gè)常量劲妙,只能被賦值一次。(用于在類中值不會變化的儒喊,命名方式為所有字母大寫镣奋,單詞之間用下劃線連接;為什么要用final修飾變量怀愧,其實(shí)在程序中若一個(gè)數(shù)據(jù)是固定的侨颈,那么直接使用這個(gè)數(shù)據(jù)就可以了,但是這樣閱讀性差芯义,所以加上final)
- 內(nèi)部類只能訪問被final修飾的局部變量哈垢。
Example1
class Person
{
final int x;
}
class FinalDemo
{
public static void main(String[] args){
new Person();
}
}
編譯錯(cuò)誤:可能尚未初始化變量x(指的顯示初始化)。
注意:變量若是有final修飾符扛拨,則一般都為靜態(tài):static final int x = 7
耘分,每個(gè)對象的值都一樣。