類的繼承格式
class 父類 {
}
class 子類 extends 父類 {
}
通過extends關(guān)鍵字來實現(xiàn)
一、為什么需要繼承
有一些類有共同的地方技竟,容易出現(xiàn)代碼冗余冰肴,所以我們需要抽取一下,封裝繼承多態(tài)嘛。
子類通過復(fù)寫方法等方式可以對父類做一些拓展熙尉,比如:
筆都有產(chǎn)地和顏色联逻,筆就是父類,鋼筆繼承自筆检痰,鋼筆的產(chǎn)地和顏色是什么包归,蠟筆呢,毛筆也有铅歼,但是毛筆沒有筆帽公壤,特殊一點,這就可以做拓展了椎椰,為什么沒有筆帽厦幅,要注意什么。
二慨飘、繼承到底繼承了什么确憨?有什么特點?
一個類里面有4種東西
- 屬性(包括類屬性和實例屬性)瓤的、
- 方法(包括類方法和實例方法)休弃、
- 構(gòu)造器/構(gòu)造方法
- 初始化塊(包括類的初始化塊和實例的初始化塊)。
子類繼承父類的時候圈膏,到底繼承了什么塔猾?
- 1、子類繼承父類所有的屬性(除了private)
-
2稽坤、子類繼承父類(除private)所有的方法丈甸,(子類方法如果不調(diào)用 super.所復(fù)寫方法名稱 ,那么對應(yīng)父類方法將不會執(zhí)行)
- final方法不可以被繼承
- static方法不可以被繼承尿褪,隨著類的加載而加載老虫,繼承毛線。但是如果權(quán)限允許子類還是可以用茫多。
-
3、子類可以通過super忽刽,表示父類的引用天揖,調(diào)用父類的屬性或者方法。
(構(gòu)造函數(shù)和代碼塊是無法被繼承)
對于private這點非常好理解跪帝,因為private的訪問權(quán)限是本類中嘛今膊,就算通過super也不能訪問private的private屬性。但是可以通過對應(yīng)的get方法獲取伞剑,get是public嘛斑唬。
如果子類非要訪問父類的私有域,那么就反射吧
繼承有什么特點:
Java只可以單繼承,不能多繼承
繼承的好處是是可以提高效率恕刘,抽取封裝缤谎,缺點是提高了代碼耦合。
Object是所有類的直接或者間接父類褐着,Object是萬類之祖坷澡。
-
繼承中的this和super
super關(guān)鍵字:
我們可以通過super關(guān)鍵字來實現(xiàn)對父類成員的訪問,用來引用當(dāng)前對象的父類含蓉。this關(guān)鍵字:
指向自己的引用频敛。
-
繼承與final
-
final 類
final class 類名 {//類體}
不能繼承的,即最終類
被聲明為 final 類的方法自動地聲明為 final馅扣,但是實例變量并不是 final -
final方法
修飾符(public/private/default/protected) final 返回值類型 方法名(){//方法體}
該方法不能被子類重寫
-
-
final實例變量
final修飾的變量稱為常量斟赚,這些變量只能賦值一次,final 的變量不能被修改
final修飾的引用類型變量,表示該變量的引用不能變,而不是該變量的值不能變; 如果子類方法簽名和父類簽名完全一致差油,那就必須寫上@Override的注解拗军,也就是必須復(fù)寫,不然的話厌殉,報錯食绿。
調(diào)用被覆蓋的父類方法:使用super.方法名(實參);
三、子類的初始化
類的初始化分為 類初始化 和 對象初始化公罕。
三器紧、1.類初始化
先執(zhí)行父類的 類成員(static成員) 和 類代碼塊(static代碼塊),后執(zhí)行子類的類成員(static成員) 和 類代碼塊(static代碼塊)楼眷。
static成員和static代碼塊僅會在類加載的時候加載一次铲汪。
三、2.對象初始化
1罐柳、先初始化父類的屬性和代碼塊掌腰,后初始化子類的屬性和代碼塊。
2张吉、接著初始化父類的構(gòu)造函數(shù)齿梁,然后是子類
關(guān)于子類能從父類繼承到什么和繼承中的類初始化,我們看一下下面的代碼就清楚了:
AClass
public class AClass {
int normalVar = 10;
// 父類私有變量肮蛹,子類無法繼承勺择,但可以get到
private int priVar = 20;
{
System.out.println("AClass 普通代碼塊");
}
static{
System.out.println("AClass static代碼塊");
}
public AClass(){
System.out.println("AClass 無參構(gòu)造方法");
}
public AClass(String str){
System.out.println("AClass 帶參構(gòu)造方法!");
}
public void aMethodOne(){
normalVar = 55;
System.out.println("aMethodOne");
}
public void aMethodTwo(){
normalVar = 66;
System.out.println("aMethodTwo");
}
public int getPriVar() {
return priVar;
}
public void setNormalVar(int normalVar) {
this.normalVar = normalVar;
}
}
.
.
BClass
public class BClass extends AClass{
{
System.out.println("BClass 普通代碼塊");
}
static{
System.out.println("BClass static代碼塊");
}
public BClass(){
System.out.println("BClass 無參構(gòu)造方法");
}
public BClass(String str){
System.out.println("BClass 帶參構(gòu)造方法伦忠!");
}
@Override
public void aMethodOne() {
super.aMethodOne();
System.out.println("子類Override的aMethodOne方法 并且調(diào)用super.aMethodOne();");
}
@Override
public void aMethodTwo() {
//super.aMethodTwo();
System.out.println("子類Override的aMethodTwo方法 不調(diào)用super.aMethodTwo();");
}
public void getFatherVar(){
System.out.println("調(diào)用父類的非私有變量:"+normalVar);
//int a = priVar; // 父類的私有變量子類無法繼承省核,本句會報錯
}
}
.
.
TrtExt
public class TrtExt{
public static void main(String[] args) {
BClass bClass1 = new BClass();
System.out.println("=========");
BClass bClassZs = new BClass("張三");
bClassZs.getFatherVar();
bClassZs.setNormalVar(88); // 子類bClass2對象改變父類的變量,不會影響其他子類
System.out.println("====================");
BClass bClassLs = new BClass("李四");
bClassLs.getFatherVar();//
System.out.println("====================");
BClass bClassWw = new BClass("王五");
bClassWw.getFatherVar();// 父類的原始值
bClassWw.aMethodOne(); // BClass的aMethodOne方法有super.aMethodOne();所以會調(diào)父類的aMethodOne()
bClassWw.getFatherVar();
System.out.println("====================");
BClass bClassAl = new BClass("阿六");
bClassAl.getFatherVar();// 父類的原始值
bClassAl.aMethodTwo();// BClass的aMethodTwo方法 沒有super.aMethodOne();所以不調(diào)父類aMethodTwo
bClassAl.getFatherVar();
}
}
.
.
輸出:
AClass static代碼塊
BClass static代碼塊
AClass 普通代碼塊
AClass 無參構(gòu)造方法
BClass 普通代碼塊
BClass 無參構(gòu)造方法
=========
AClass 普通代碼塊
AClass 無參構(gòu)造方法
BClass 普通代碼塊
BClass 帶參構(gòu)造方法昆码!
調(diào)用父類的非私有變量:10
====================
AClass 普通代碼塊
AClass 無參構(gòu)造方法
BClass 普通代碼塊
BClass 帶參構(gòu)造方法气忠!
調(diào)用父類的非私有變量:10
====================
AClass 普通代碼塊
AClass 無參構(gòu)造方法
BClass 普通代碼塊
BClass 帶參構(gòu)造方法邻储!
調(diào)用父類的非私有變量:10
aMethodOne
子類Override的aMethodOne方法 并且調(diào)用super.aMethodOne();
調(diào)用父類的非私有變量:55
====================
AClass 普通代碼塊
AClass 無參構(gòu)造方法
BClass 普通代碼塊
BClass 帶參構(gòu)造方法!
調(diào)用父類的非私有變量:10
子類Override的aMethodTwo方法 不調(diào)用super.aMethodTwo();
調(diào)用父類的非私有變量:10
通過這個例子旧噪,我們可以清楚地看到
1吨娜、靜態(tài)代碼塊只會隨著類的加載加載一次,優(yōu)先于對象
2舌菜、子類只可以繼承父類的非private的
變量
和方法
萌壳,無法繼承代碼塊
和構(gòu)造方法
3、當(dāng)子類被實例化時日月,會先調(diào)用父類的無參構(gòu)造方法袱瓮。
子類從父類繼承的變量和方法是獨立的,不會受到其他子類的影響爱咬。
(bClass2改變父類的變量不會影響bClass3繼承到的變量)4尺借、子類復(fù)寫父類的方法,如果不調(diào)用 super.所復(fù)寫方法名稱 那么對應(yīng)父類方法將不會執(zhí)行精拟,也就是說燎斩,這個方法就相當(dāng)一個普通的方法,沒有從父類繼承到什么蜂绎。
(絕大多數(shù)情況下是需要調(diào)用 super.所復(fù)寫方法名稱 的栅表,但是有時候我們個別子類不想要父類方法的初始化邏輯,我們可以靈活選擇是否保留 )
四师枣、overload和override
方法重載(overload)
1.必須是同一個類
2方法名一樣
3參數(shù)類型不一樣或參數(shù)數(shù)量不一樣
方法的重寫/復(fù)寫(override)
子類對父類方法的復(fù)寫
兩同兩小一大原則:
兩同:
方法名相同怪瓶,參數(shù)類型相同
兩小:
子類返回類型小于等于父類方法返回類型践美,若為基本類型或者void則必須相同
子類拋出異常小于等于父類方法拋出異常洗贰,
一大:
子類訪問權(quán)限大于等于父類方法訪問權(quán)限。
實例一下:
AClass
public class AClass {
public int number = 10;
protected void changeNumber(int i){
setNumber(100);
System.out.println("父類 changeNumber: "+number);
}
public int setNumber(int i) {
number = i;
System.out.println("父類 number: "+number);
return number;
}
}
.
.
BClass
public class BClass extends AClass{
// 這個復(fù)寫方法的訪問權(quán)限比父類大陨倡,也就是兩者訪問權(quán)限不一致敛滋,但是也是對那個方法的復(fù)寫
@Override
public void changeNumber(int i) {
super.changeNumber(i);
setNumber(i);
}
}
.
.
TrtExt
public class TrtExt{
public static void main(String[] args) {
BClass bClass2 = new BClass();
bClass2.changeNumber(70);
}
}
輸出:
父類 number: 100
父類 changeNumber: 100
父類 number: 70
我們看到,其實并不是復(fù)寫的方法就需要和父類方法完全一致的兴革。