概述
- 多個類中存在相同屬性和行為時,將這些內(nèi)容抽取到單獨一個類中,那么多個類無需再定義這些屬性和行為,只要繼承單獨的類即可。
- 多個類可以稱為子類,單獨的類稱為父類或者超類。
- 子類可以訪問父類中的<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)的前提。
特點
-
Java中支持單繼承,不支持多繼承
一個子類只能有一個直接父類,否則若多個父類中有相同成員,會產(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{}當要使用一個繼承體系時:
1)查看該體系中的頂層類,了解該體系的基本功能
2)創(chuàng)建體系中的最子類對象,完成功能的使用 什么時候定義繼承?
當類與類之間存在著所屬("is a")關(guān)系的時候,就定義繼承。如xxx是yyy的一種,則xxx extends yyy?;蛘吲袛喔割愔械墓δ苁欠褡宇惗紤?yīng)該具備,若有些不具備,則不存在繼承。不要僅為了獲取其他類中某個功能而去繼承 。
super關(guān)鍵字
this和super的用法很相似。
this代表一個本類對象的引用;super代表一個<font color="#FF0000">父類空間</font>(注意并不是代表父類對象,如下代碼,從未定義過父類對象,子類之所以可以獲取父類的內(nèi)容,是因為其持有super引用)。
-
當本類中的成員和局部變量同名用this區(qū)分;當子父類中的成員變量同名用super區(qū)分父類(如果不使用super,則默認調(diào)用子類自己的成員變量),子類要調(diào)用父類構(gòu)造函數(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)容,子類是否具備——實際上子類對于父類中私有的成員變量是繼承了的,即定義子類對象時,在堆中存有父類的私有成員變量(上圖堆中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ù)嚴格一致,覆蓋函數(shù)和被覆蓋函數(shù)只有函數(shù)體不同,當派生類對象調(diào)用子類中該同名函數(shù)時會自動調(diào)用子類中的覆蓋版本,而不是父類中的被覆蓋函數(shù)版本,這種機制就叫做函數(shù)覆蓋。
- 父類中的私有方法不可以被覆蓋。
- 在子類覆蓋方法中,繼續(xù)使用被覆蓋的方法可以通過super.函數(shù)名獲取。
- 覆蓋注意事項:
覆蓋時,子類方法權(quán)限一定要大于等于父類方法權(quán)限。
靜態(tài)只能覆蓋靜態(tài),或被靜態(tài)覆蓋(非靜態(tài)不能覆蓋靜態(tài),靜態(tài)不能覆蓋非靜態(tài),否則會報編譯錯)。 - 覆蓋的應(yīng)用:
當子類需要父類的功能,而功能主體子類有自己特有內(nèi)容時,可以復(fù)寫父類中的方法,這樣即沿襲了父類的功能,又定義了子類特有的內(nèi)容。
子類的實例化過程
注意構(gòu)造函數(shù)不會繼承更不會覆蓋(首先函數(shù)名都不一樣...
子類的實例化過程:子類中所有的構(gòu)造函數(shù)默認都會訪問父類中空參數(shù)的構(gòu)造函數(shù)(因為每一個構(gòu)造函數(shù)的第一行都有一條默認的語句super();之所以會有這樣一條默認語句,是因為子類繼承了父類,獲取到了父類中的內(nèi)容(屬性),所以在使用父類內(nèi)容之前,要先明確父類是如何對這些數(shù)據(jù)初始化的)。
public class Father
{
public Father()
{
System.out.println("Father run");
}
}
class Son extends Father
{
public Son()
{
//super(); //默認會調(diào)用父類的空參構(gòu)造函數(shù)
System.out.println("Son run");
}
}
當父類中沒有空參數(shù)的構(gòu)造函數(shù)時,子類的構(gòu)造函數(shù)必須通過this或者super語句指定要訪問的構(gòu)造函數(shù)。
注意:super語句必須要定義在子類構(gòu)造函數(shù)的第一行,因為父類的初始化動作要先完成。
如果子類構(gòu)造函數(shù)中使用this調(diào)用了本類構(gòu)造函數(shù),那么就沒有super了,因為super和this都只能定義在第一行。但是可以保證的是,子類中肯定會有其他的構(gòu)造函數(shù)訪問父類的構(gòu)造函數(shù),即子類構(gòu)造函數(shù)中至少有一個訪問父類構(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");
}
}
注意,類默認的空參構(gòu)造函數(shù)的修飾符與類保持一致。
class Demo
{
/*
Demo()
{
super(); //父類為Object
return;
}
*/
}
Example1:子類的實例化過程
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)存圖解:

默認初始化,成員變量num=0;顯示初始化,成員變量num=8。
默認初始化后,執(zhí)行super(),經(jīng)過父類構(gòu)造函數(shù)初始化,再進行自己的顯示初始化,最后是自己的構(gòu)造函數(shù)初始化。
注意,若子類當中有構(gòu)造代碼塊,則其初始化的過程為:

一個對象的實例化過程:
Person p = new Person();
1、JVM讀取指定路徑下的Person.class文件,如果該類有直接父類,先加載Person的父類,再加載Person進內(nèi)存;
2、在堆內(nèi)存中開辟空間,分配地址;
3、在堆的對象空間中,對對象中的屬性進行默認初始化;
4、調(diào)用對應(yīng)的構(gòu)造函數(shù)進行初始化;
5、在構(gòu)造函數(shù)中,先調(diào)用父類的構(gòu)造函數(shù)進行初始化;
6、父類初始化完畢后,再對子類的屬性進行顯示初始化;
7、構(gòu)造代碼塊初始化;
8、進行子類構(gòu)造函數(shù)的特定初始化;
9、初始化完畢后,將地址值賦值給引用變量(不一定是在棧中,如下)。
class Demo
{
Person p = new Person(); //在堆中
}
final關(guān)鍵字
- final可以修飾類,方法,變量。
- final修飾的類不可以被繼承。(繼承弊端:打破了封裝性。)
- final修飾的方法不可以被覆蓋。(可以用于在父類中,若有幾個方法不希望被復(fù)寫時)
- final修飾的變量是一個常量,只能被賦值一次。(用于在類中值不會變化的,命名方式為所有字母大寫,單詞之間用下劃線連接;為什么要用final修飾變量,其實在程序中若一個數(shù)據(jù)是固定的,那么直接使用這個數(shù)據(jù)就可以了,但是這樣閱讀性差,所以加上final)
- 內(nèi)部類只能訪問被final修飾的局部變量。
Example1
class Person
{
final int x;
}
class FinalDemo
{
public static void main(String[] args){
new Person();
}
}
編譯錯誤:可能尚未初始化變量x(指的顯示初始化)。
注意:變量若是有final修飾符,則一般都為靜態(tài):static final int x = 7,每個對象的值都一樣。