3. 繼承
3.1 Java中的繼承
繼承是類與類的一種關系相恃,是一種“is a”的關系辜纲。
注意:Java中的繼承是單繼承,也就是說拦耐,一個類只有一個父類耕腾。
繼承的好處有哪些?
- 子類擁有父類的所有屬性和方法(關鍵字private修飾的屬性和方法除外)
- 實現(xiàn)代碼復用
繼承的語法規(guī)則是什么杀糯?
class 子類 extends 父類
例如:
class Dog extends Animal{
......
}
下面來實踐一下扫俺,實現(xiàn)一個簡單的繼承。
- 第一步:創(chuàng)建一個類 Animal
package com.example;
public class Animal {
public int age;
public String name;
public void eat() {
System.out.println("動物具有吃東西的能力固翰!");
}
}
- 第二步:創(chuàng)建另一個類 Dog狼纬,讓其繼承自類Animal
package com.example;
public class Dog extends Animal {
}
這一步,在我們的子類中沒有定義任何的變量和方法
- 第三步:main方法中實例化Dog類
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Dog dog = new Dog(); // 實例化一個Dog對象
dog.age = 10;
dog.name = "huahua";
dog.eat();
}
}
運行一下看看骂际,結果如下:
動物具有吃東西的能力疗琉!
我們看到,在這里我們實例化了一個子類對象歉铝,然而它也擁有了屬性 age 和 name盈简,以及方法eat(),這些屬性和方法完全來自于其父類。
比如弃衍,我們將父類中的屬性age改成private修飾后,測試類中的子類對象再次調用age屬性時坚俗,編譯器提示錯誤如下:
3.2 Java中的方法重寫
如果子類對繼承父類的方法不滿意,是可以重寫父類繼承的方法的猖败,當調用方法時會優(yōu)先調用子類的方法速缆。
方法重寫的語法規(guī)則是什么?
- 返回值類型
- 方法名
- 參數(shù)類型及個數(shù)
以上這幾點都要與繼承父類的方法相同恩闻。同樣艺糜,我們也來代碼實踐一下。
// 父類:Animal
package com.example;
public class Animal {
public int age;
public String name;
public void eat() {
System.out.println("動物具有吃東西的能力幢尚!");
}
}
// 子類:Dog
package com.example;
public class Dog extends Animal {
// 重寫父類的eat()方法 [注意:返回類型/方法名/參數(shù)個數(shù) 都要與父類方法相同]
public void eat() {
System.out.println("狗具有吃東西的能力破停!");
}
}
// main方法
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Dog dog = new Dog(); // 實例化一個子類Dog對象
dog.eat();
}
}
我們來看看程序運行結果,如下:
狗具有吃東西的能力尉剩!
從結果來看真慢,方法重寫后,優(yōu)先調用的是子類本身重寫后的方法理茎。
3.3 Java中的繼承初始化順序
繼承的初始化順序是什么黑界?
- 初始化父類,再初始化子類
- 先執(zhí)行初始化對象中的屬性皂林,再執(zhí)行構造方法中的初始化
說白了朗鸠,初始化過程其實就是相當于執(zhí)行類的構造方法。同樣础倍,我們也來代碼實踐一下烛占。
首先在父類Animal中的構造方法中打印一句話,如下:
// 父類:Animal
package com.example;
public class Animal {
public int age;
public String name;
public void eat() {
System.out.println("動物具有吃東西的能力沟启!");
}
// Animal類的無參構造方法
public Animal(){
System.out.println("Animal類執(zhí)行了扰楼!");
}
}
然后,在子類Dog中的構造方法中同樣也打印一句話美浦,如下:
// 子類:Dog
package com.example;
public class Dog extends Animal {
// 重寫父類的eat()方法 [注意:返回類型/方法名/參數(shù)個數(shù) 都要與父類方法相同]
public void eat() {
System.out.println("狗具有吃東西的能力弦赖!");
}
public Dog(){
System.out.println("Dog類執(zhí)行了!");
}
}
然后浦辨,在main方法中只創(chuàng)建一個子類對象蹬竖,如下:
// main方法
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Dog dog = new Dog(); // 實例化一個子類Dog對象
}
}
接著沼沈,我們來執(zhí)行一下程序,看看結果如下:
Animal類執(zhí)行了币厕!
Dog類執(zhí)行了列另!
從上面的結果我們可以看到,。
3.4 Java中final的使用
final可以修飾 類/屬性/方法/變量
- final 修飾類的時候阴绢,該類不允許被繼承
- final 修飾方法的時候店乐,該方法不允許被覆蓋(重寫)
- final 修飾屬性的時候,該類的屬性不會進行隱式的初始化(類的初始化屬性必須要有值)或者在構造方法中賦值
- final 修飾變量的時候呻袭,該變量的值只能賦一次值眨八,即變?yōu)槌A?/li>
3.5 Java中super的使用
在對象內部使用左电,可以代表父類對象
既然super可以代表父類對象廉侧,就可以訪問父類的屬性,以及調用父類的方法篓足。同樣段誊,我們也來代碼實踐一下。
// 父類:Animal
package com.example;
public class Animal {
public int age = 10;
public String name;
public void eat() {
System.out.println("動物具有吃東西的能力栈拖!");
}
// 父類的構造函數(shù)
public Animal(){
System.out.println("Animal類執(zhí)行了连舍!");
}
}
// 子類:Dog
package com.example;
public class Dog extends Animal {
public int age = 20;
//重寫父類eat()方法
public void eat() {
System.out.println("狗具有吃東西的能力!");
}
// 子類的構造函數(shù)
public Dog() {
System.out.println("Dog類執(zhí)行了辱魁!");
}
public void show() {
System.out.println("super關鍵字獲取父類屬性age:"+ super.age); //使用super關鍵字獲得父類的屬性age
System.out.println("子類本身的屬性age:"+ age); // 獲取子類本身的屬性age
super.eat(); // 使用super關鍵字獲取父類的方法
eat(); // 獲取子類本身的方法
}
}
// main方法
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Dog dog = new Dog(); // 實例化一個子類Dog對象
dog.show();
}
}
接著烟瞧,我們來執(zhí)行一下程序诗鸭,看看結果如下:
Animal類執(zhí)行了染簇!
Dog類執(zhí)行了!
super關鍵字獲取父類屬性age:10
子類本身的屬性age:20
動物具有吃東西的能力强岸!
狗具有吃東西的能力锻弓!
3.6 Java中object類
青灼。Object類中的方法,適合所有子類妓盲。
3.6.1 toString()方法
在Object類里面定義toString()方法的時候返回的是 對象的哈希code碼(對象地址字符串)杂拨。
可以通過重寫 toString()方法 表示出對象的屬性。
比如:在上面的代碼中悯衬,如果我們在main方法中直接輸出Dog對象弹沽,如下:
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Dog dog = new Dog(); // 實例化一個子類Dog對象
// dog.show();
System.out.println(dog);
}
}
我們看到結果如下:
Animal類執(zhí)行了!
Dog類執(zhí)行了!
com.example.Dog@1b6d3586
顯然策橘,上面的輸出不是我們滿意的輸出(因為不知道是啥意思炸渡,嗚嗚~)。如果我們想知道對象的屬性值丽已,這個時候就需要重寫從父類繼承過來的 **toString() **方法蚌堵,如下:
// 子類:Dog
package com.example;
public class Dog extends Animal {
public int age = 20;
//重寫父類eat()方法
public void eat() {
System.out.println("狗具有吃東西的能力!");
}
// 子類的構造函數(shù)
public Dog() {
System.out.println("Dog類執(zhí)行了沛婴!");
}
public void show() {
System.out.println("super關鍵字獲取父類屬性age:"+ super.age); //使用super關鍵字獲得父類的屬性age
System.out.println("子類本身的屬性age:"+ age); // 獲取子類本身的屬性age
super.eat(); // 使用super關鍵字獲取父類的方法
eat(); // 獲取子類本身的方法
}
@Override
public String toString() {
return "Dog [age=" + age + "]";
}
}
然后直接運行main()方法吼畏,結果如下:
Animal類執(zhí)行了!
Dog類執(zhí)行了瘸味!
Dog [age=20]
3.6.2 equals()方法
equals()方法:比較的是對象的引用是否指向同一塊內存地址宫仗。
一般情況下,比較兩個對象時旁仿,是比較的兩個對象的值是否相同藕夫,所以要進行重寫對象方法。
同樣枯冈,我們還是來代碼實踐一下毅贮。父類和子類還是之前的Animal和Dog,我們只改變一下main方法尘奏,如下:
// main方法
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Dog dog1 = new Dog(); // 實例化一個Dog對象
dog1.age = 15;
Dog dog2 = new Dog();
dog2.age = 15;
if(dog1.equals(dog2)) {
System.out.println("兩個對象是相同的滩褥!");
}else {
System.out.println("兩個對象是不相同的!");
}
}
}
我們來看看運行結果炫加,如下:
Animal類執(zhí)行了瑰煎!
Dog類執(zhí)行了!
Animal類執(zhí)行了俗孝!
Dog類執(zhí)行了酒甸!
兩個對象是不相同的!
從上面的運行結果赋铝,我們看到插勤,雖然兩個對象的屬性age值時一樣的,但通過equals方法比較的結果是 “兩個對象是不相同的革骨!” 這是因為實例化兩個對象的過程农尖,實際上是在內存中開辟兩個內存地址,分別存放兩個實例化對象良哲,而equals()方法比較的是對象的引用盛卡,也就是對象的地址,所以兩個對象是不同的筑凫!