Java中的重要核心知識(shí)點(diǎn)之繼承詳解
一、繼承
1、概念
繼承(inheritance)機(jī)制:是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特 性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加新功能,這樣產(chǎn)生新的類,稱派生類。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu), 體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過(guò)程。
繼承主要解決的問(wèn)題是:共性的抽取,實(shí)現(xiàn)代碼復(fù)用。
例如:狗和貓都是動(dòng)物,分別定義一個(gè)貓類和狗類,他們都是動(dòng)物,讓他們來(lái)繼承動(dòng)物類,那么我們就可以將共性的內(nèi)容進(jìn)行抽取,然后采用繼承的思想來(lái)達(dá)到共用。
// Dog.java
public class Dog{
string name;
int age;
float weight;
public void eat(){
System.out.println(name + "正在吃飯");
}
public void sleep(){
System.out.println(name + "正在睡覺(jué)");
}
void Bark(){
System.out.println(name + "汪汪汪~(yú)~~");
}
}
// Cat.Java
public class Cat{
string name;
int age;
float weight;
public void eat(){
System.out.println(name + "正在吃飯");
}
public void sleep()
{
System.out.println(name + "正在睡覺(jué)");
}
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}

2、語(yǔ)法
在Java中如果要表示類之間的繼承關(guān)系,需要借助extends關(guān)鍵字,具體如下:
修飾符 class 子類 extends 父類 {
// ...
}
那么對(duì)于貓和狗的場(chǎng)景,我們讓貓類和狗類均繼承自動(dòng)物類:
public class Animal{
String name;
int age;
public void eat(){
System.out.println(name + "正在吃飯");
}
public void sleep(){
System.out.println(name + "正在睡覺(jué)");
}
}
// Dog.java
public class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~(yú)~~");
}
}
// Cat.Java
public class Cat extends Animal{
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
// TestExtend.java
public class TestExtend {
public static void main(String[] args) {
Dog dog = new Dog();
// dog類中并沒(méi)有定義任何成員變量,name和age屬性肯定是從父類Animal中繼承下來(lái)的
System.out.println(dog.name);
System.out.println(dog.age);
// dog訪問(wèn)的eat()和sleep()方法也是從Animal中繼承下來(lái)的
dog.eat();
dog.sleep();
dog.bark();
}
}
注意事項(xiàng):
1、子類會(huì)將父類中的成員變量或成員方法繼承到子類中去。
2、子類中一定要含有與父類不同的成員方法或者成員變量,體現(xiàn)出與父類的不同,否則繼承就失去了意義。
3、父類成員的訪問(wèn)
(1)子類中訪問(wèn)父類成員變量
第一種情況:子類和父類不存在同名成員變量
public class Base {
int a;
int b;
}
public class Derived extends Base {
int c;
int d;
public void method(){
a=10;//訪問(wèn)的是從父類繼承下來(lái)的成員變量a
b=20;//訪問(wèn)的是從父類繼承下來(lái)的成員變量b
c=30;//訪問(wèn)的是子類自己的成員變量c
d=40;//訪問(wèn)的是子類自己的成員變量d
}
}
第二種情況:子類和父類成員變量有同名
public class Base {
int a;
int b;
}
public class Derived extends Base {
int b;
int c;
public void method(){
a=10;
b=20;//此時(shí)訪問(wèn)的是父類中的b還是子類中的b?
c=30;
//d=40; 編譯報(bào)錯(cuò),因?yàn)樵谧宇惡透割愔卸疾淮嬖诔蓡T變量d
}
}
注意事項(xiàng):
在子類方法中或者通過(guò)子類對(duì)象訪問(wèn)成員變量時(shí):
1、如果訪問(wèn)的成員變量子類中有,優(yōu)先訪問(wèn)自己的成員變量。
2、如果訪問(wèn)的成員變量子類中無(wú),則訪問(wèn)父類繼承下來(lái)的,如果父類也沒(méi)有定義,則編譯報(bào)錯(cuò)。
3、如果訪問(wèn)的成員變量與父類中成員變量同名,則優(yōu)先訪問(wèn)自己的,即:子類將父類同名成員隱藏了。
(2)子類中訪問(wèn)父類成員方法
第一種情況:子類和父類中不存在同名成員方法
public class Base {
public void methodA(){
System.out.println("我是父類成員方法");
}
}
public class Derived extends Base {
public void methodB(){
System.out.println("我是子類成員方法");
}
public void methodC(){
methodA();//訪問(wèn)繼承自父類成員方法
methodB();//訪問(wèn)子類自己的成員方法
//methodD(); 編譯報(bào)錯(cuò),在繼承體系中沒(méi)有methodD方法
}
}
第二種情況:子類和父類中存在同名方法
public class Base {
public void methodA(){
System.out.println("我是父類成員方法methodA");
}
public void methodB(){
System.out.println("我是父類成員方法methodB");
}
}
public class Derived extends Base {
public void methodA(int a){
System.out.println("我是子類成員方法methodA");
}
public void methodB(){
System.out.println("我是子類成員方法methodB");
}
public void methodC(){
methodA(1);//帶參的methodA方法,訪問(wèn)的是子類的方法
methodA();//無(wú)參的methodA方法,訪問(wèn)的是父類的方法
methodB();//直接訪問(wèn)methodB,訪問(wèn)到的是子類的方法
}
}
注意事項(xiàng):
1、通過(guò)子類對(duì)象或方法訪問(wèn)父類與子類中不同名方法時(shí),優(yōu)先在子類中找,找到則訪問(wèn),否則在父類中找,找到則訪問(wèn),否則編譯報(bào)錯(cuò)。
2、通過(guò)子類對(duì)象訪問(wèn)父類與子類同名方法時(shí),如果父類和子類同名方法的參數(shù)列表不同,根據(jù)調(diào)用方法時(shí)傳遞的參數(shù)選擇合適的方法訪問(wèn),如果沒(méi)有則報(bào)錯(cuò);如果父類和子類同名方法的原型一致,則只能訪問(wèn)到子類的,父類的無(wú)法通過(guò)派生類對(duì)象直接訪問(wèn)到。
咦????那么問(wèn)題來(lái)了,如果想要訪問(wèn)與子類方法同名且原型一致的父類方法時(shí)該怎么訪問(wèn)呢?——super關(guān)鍵字
4、super關(guān)鍵字
當(dāng)子類和父類中存在同名且原型一致的方法時(shí),我們想要調(diào)用父類的該方法時(shí)發(fā)現(xiàn)不能直接訪問(wèn),而在Java中提供了super關(guān)鍵字,該關(guān)鍵字的主要作用就是:在子類方法中訪問(wèn)父類成員。
public class Base {
int a;
int b;
public void methodA(){
System.out.println("我是父類成員方法methodA");
}
public void methodB(){
System.out.println("我是父類成員方法methodB");
}
}
public class Derived extends Base {
int a;//與父類同名成員
String b;//與父類同名成員的類型不同
// 與父類中methodA()構(gòu)成重載
public void methodA(int a){
System.out.println("我是子類成員方法methodA");
}
//重寫(xiě)父類中的methodB方法
public void methodB(){
System.out.println("我是子類成員方法methodB");
}
public void methodC(){
a=100;
super.a=110;
b="abc";
super.b=100;
methodA(1);//帶參的methodA方法,訪問(wèn)的是子類的方法
methodA();//無(wú)參的methodA方法,訪問(wèn)的是父類的方法
methodB();//直接訪問(wèn)methodB,訪問(wèn)到的是子類的方法
super.methodB();//通過(guò)super關(guān)鍵字調(diào)用的是父類的methodB
}
}
注意事項(xiàng):
1、只能在非靜態(tài)方法中使用。

2、用于在子類方法中,訪問(wèn)父類的成員變量和方法。
5、子類構(gòu)造方法
子類對(duì)象構(gòu)造時(shí),需要先調(diào)用基類構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。
public class Base {
public Base(){
System.out.println("我是父類構(gòu)造方法");
}
}
public class Derived extends Base {
public Derived(){
// super();
// 注意子類構(gòu)造方法中默認(rèn)會(huì)調(diào)用基類的無(wú)參構(gòu)造方法:super()
//當(dāng)用戶沒(méi)有寫(xiě)時(shí)編譯器自動(dòng)添加
//且super()必須是第一條語(yǔ)句,且只能出現(xiàn)一次
System.out.println("我是子類構(gòu)造方法");
}
public static void main(String[] args) {
Derived a=new Derived();
}
}
/*
執(zhí)行結(jié)果
我是父類構(gòu)造方法
我是子類構(gòu)造方法
*/
說(shuō)明:在子類構(gòu)造方法中,并沒(méi)有寫(xiě)任何關(guān)于基類構(gòu)造的代碼,但是在構(gòu)造子類對(duì)象時(shí),先執(zhí)行基類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法,因?yàn)椋鹤宇悓?duì)象是一個(gè)父類對(duì)象,在構(gòu)造子類對(duì)象時(shí),先要將從父類繼承下來(lái)的成員初始化完整,然后再初始化子類自己新增加的成員。
從對(duì)象模型的角度來(lái)看:

注意事項(xiàng):
1、若父類顯式定義無(wú)參或者默認(rèn)的構(gòu)造方法,編譯器會(huì)給子類生成一個(gè)默認(rèn)的構(gòu)造方法,且在子類構(gòu)造方法第一行默認(rèn)有隱含的super()調(diào)用,即調(diào)用基類構(gòu)造方法。
2、如果父類構(gòu)造方法是帶有參數(shù)的,此時(shí)編譯器不會(huì)再給子類生成默認(rèn)的構(gòu)造方法,此時(shí)需要用戶為子類顯式定義構(gòu)造方法,并在子類構(gòu)造方法中選擇合適的父類構(gòu)造方法調(diào)用,否則編譯失敗。
具體看如下代碼:
public class Base {
int a;
int b;
public Base(int a){ //父類中帶參數(shù)的構(gòu)造方法
this.a=a;
}
}
public class Derived extends Base {
public Derived(int a,int b){ //此時(shí)需要在子類中自定義構(gòu)造方法
super(a);//選擇合適的父類構(gòu)造方法調(diào)用
this.b=b;
}
public static void main(String[] args) {
Derived a=new Derived(10,20);
System.out.println(a.a);
System.out.println(a.b);
}
}
/*
執(zhí)行結(jié)果:
10
20
*/
3、在子類構(gòu)造方法中,super(…)調(diào)用父類構(gòu)造時(shí),必須是子類構(gòu)造函數(shù)中第一條語(yǔ)句。
4、super(…)只能在子類構(gòu)造方法中出現(xiàn)一次,并且不能和this同時(shí)出現(xiàn)。
6、super和this
相同點(diǎn):
1、都是Java中的關(guān)鍵字
2、只能在類的非靜態(tài)方法中使用,用來(lái)訪問(wèn)非靜態(tài)成員方法和字段
3、在構(gòu)造方法中調(diào)用時(shí),必須是構(gòu)造方法中的第一條語(yǔ)句,并且不能同時(shí)存在,如下圖所示:

不同點(diǎn):
先給一段代碼
public class Base {
int a;
int b;
}
public class Derived extends Base {
int c;
int d;
public void method(){
super.a=10;
super.b=20;
this.c=30;
this.d=40;
}
public static void main(String[] args) {
Derived d=new Derived();
d.method();
}
}
1、 this是當(dāng)前對(duì)象的引用,當(dāng)前對(duì)象即調(diào)用實(shí)例方法的對(duì)象,super相當(dāng)于是父類對(duì)象的引用。針對(duì)上述代碼,如下圖所示:

2、在非靜態(tài)成員方法中,this用來(lái)訪問(wèn)本類的方法和屬性,super用來(lái)訪問(wèn)父類繼承下來(lái)的方法和屬性。上圖也可以進(jìn)行說(shuō)明。
3、this是非靜態(tài)成員方法的一個(gè)隱藏參數(shù),super不是隱藏的參數(shù)。還是針對(duì)上述代碼,從字節(jié)碼的角度來(lái)看,打開(kāi)method方法中的局部變量表,發(fā)現(xiàn)只有this,而沒(méi)有super。

4、成員方法中直接訪問(wèn)本類成員時(shí),編譯之后會(huì)將this還原,即本類非靜態(tài)成員都是通過(guò)this來(lái)訪問(wèn)的;在子類中如果通過(guò)super訪問(wèn)父類成員,編譯之后在字節(jié)碼層面super實(shí)際是不存在的。

5、在構(gòu)造方法中:this(…)用于調(diào)用本類構(gòu)造方法,super(…)用于調(diào)用父類構(gòu)造方法,兩種調(diào)用不能同時(shí)在構(gòu)造方法中出現(xiàn)。
6、 構(gòu)造方法中一定會(huì)存在super(…)的調(diào)用,用戶沒(méi)有寫(xiě)編譯器也會(huì)增加,但是this(…)用戶不寫(xiě)則沒(méi)有。
7、代碼塊執(zhí)行順序
第一種:在沒(méi)有繼承關(guān)系時(shí)的執(zhí)行順序
public class Person {
int age;
String name;
public Person(int age,String name){
this.age=age;
this.name=name;
System.out.println("執(zhí)行構(gòu)造方法");
}
{
System.out.println("執(zhí)行實(shí)例代碼塊");
}
static {
System.out.println("執(zhí)行靜態(tài)代碼塊");
}
public static void main(String[] args) {
Person a=new Person(20,"luka");
System.out.println("-----------------------");
Person b=new Person(21,"stepth");
}
}
/*
執(zhí)行結(jié)果:
執(zhí)行靜態(tài)代碼塊
執(zhí)行實(shí)例代碼塊
執(zhí)行構(gòu)造方法
-----------------------
執(zhí)行實(shí)例代碼塊
執(zhí)行構(gòu)造方法
*/
說(shuō)明:
1、靜態(tài)代碼塊先執(zhí)行,并且只執(zhí)行一次,在類加載階段執(zhí)行
2、 當(dāng)有對(duì)象創(chuàng)建時(shí),才會(huì)執(zhí)行實(shí)例代碼塊,實(shí)例代碼塊執(zhí)行完成后,最后構(gòu)造方法執(zhí)行
第二種:在存在繼承關(guān)系時(shí)的執(zhí)行順序
public class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
System.out.println("Person:執(zhí)行構(gòu)造方法");
}
{
System.out.println("Person:執(zhí)行實(shí)例代碼塊");
}
static {
System.out.println("Person:執(zhí)行靜態(tài)代碼塊");
}
}
public class Student extends Person{
public Student(int age,String name){
super(age,name);
System.out.println("Student:執(zhí)行構(gòu)造方法");
}
{
System.out.println("Student:執(zhí)行實(shí)例代碼塊");
}
static{
System.out.println("Student:執(zhí)行靜態(tài)代碼塊");
}
public static void main(String[] args) {
Student a=new Student(20,"luka");
System.out.println("------------------------");
Student b=new Student(21,"stepth");
}
}
/*
執(zhí)行結(jié)果:
Person:執(zhí)行靜態(tài)代碼塊
Student:執(zhí)行靜態(tài)代碼塊
Person:執(zhí)行實(shí)例代碼塊
Person:執(zhí)行構(gòu)造方法
Student:執(zhí)行實(shí)例代碼塊
Student:執(zhí)行構(gòu)造方法
------------------------
Person:執(zhí)行實(shí)例代碼塊
Person:執(zhí)行構(gòu)造方法
Student:執(zhí)行實(shí)例代碼塊
Student:執(zhí)行構(gòu)造方法
*/
說(shuō)明:
1、父類靜態(tài)代碼塊優(yōu)先于子類靜態(tài)代碼塊執(zhí)行,靜態(tài)代碼塊相較于其他是最早執(zhí)行。
2、父類實(shí)例代碼塊和父類構(gòu)造方法緊接著執(zhí)行。
3、子類的實(shí)例代碼塊和子類構(gòu)造方法緊接著再執(zhí)行。
4、第二次實(shí)例化子類對(duì)象時(shí),父類和子類的靜態(tài)代碼塊都將不會(huì)再執(zhí)行(靜態(tài)代碼塊只執(zhí)行一次)。
8、父類成員在子類中的可見(jiàn)性

父類中不同訪問(wèn)權(quán)限的成員,在子類中的可見(jiàn)性又是什么樣子的?
// extend01包中
public class B {
private int a;
protected int b;
public int c;
int d;
}
// extend01包中
// 同一個(gè)包中的子類
public class D extends B{
public void method(){
// super.a = 10; // 編譯報(bào)錯(cuò),父類private成員在相同包子類中不可見(jiàn)
super.b = 20; // 父類中protected成員在相同包子類中可以直接訪問(wèn)
super.c = 30; // 父類中public成員在相同包子類中可以直接訪問(wèn)
super.d = 40; // 父類中默認(rèn)訪問(wèn)權(quán)限修飾的成員在相同包子類中可以直接訪問(wèn)
}
}
// extend02包中
// 不同包中的子類
public class C extends B {
public void method(){
// super.a = 10; // 編譯報(bào)錯(cuò),父類中private成員在不同包子類中不可見(jiàn)
super.b = 20; // 父類中protected修飾的成員在不同包子類中可以直接訪問(wèn)
super.c = 30; // 父類中public修飾的成員在不同包子類中可以直接訪問(wèn)
//super.d = 40; // 父類中默認(rèn)訪問(wèn)權(quán)限修飾的成員在不同包子類中不能直接訪問(wèn)
}
}
// extend02包中
// 不同包中的類
public class TestC {
public static void main(String[] args) {
C c = new C();
c.method();
// System.out.println(c.a); // 編譯報(bào)錯(cuò),父類中private成員在不同包其他類中不可見(jiàn)
// System.out.println(c.b); // 父類中protected成員在不同包其他類中不能直接訪問(wèn)
System.out.println(c.c); // 父類中public成員在不同包其他類中可以直接訪問(wèn)
// System.out.println(c.d); // 父類中默認(rèn)訪問(wèn)權(quán)限修飾的成員在不同包其他類中不能直接訪問(wèn)
}
}
**注意:**父類中private成員變量隨時(shí)在子類中不能直接訪問(wèn),但是也繼承到子類中了。
9、繼承方式
Java中支持以下幾種繼承方式:
單繼承:

多層繼承:

不同類繼承同一個(gè)類

不支持多繼承

10、final關(guān)鍵字
final關(guān)鍵可以用來(lái)修飾變量、成員方法以及類。
1、修飾變量或字段,表示常量(即不能修改)。
final int a = 10; a = 20; // 編譯出錯(cuò)
2、修飾類:表示此類不能被繼承。
final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 編譯出錯(cuò)
平時(shí)使用的String字符串類,打開(kāi)其源碼可以看到它是被final修飾的,不可以被繼承。

3.、修飾方法:表示該方法不能被重寫(xiě)。
11、組合
組合也是一種表達(dá)類之間關(guān)系的方式, 也是能夠達(dá)到代碼重用的效果。組合并沒(méi)有涉及到特殊的語(yǔ)法(諸如 extends 這樣的關(guān)鍵字), 僅僅是將一個(gè)類的實(shí)例作為另外一個(gè)類的字段。
這是繼承和組合的區(qū)別。

//車(chē)閘類
class Brake{
}
//車(chē)把手類
class Handle{
}
//輪胎類
class Tire{
}
public class Bicycle {
private Brake brake; //可以復(fù)用車(chē)閘中的屬性和方法
private Handle handle; //可以復(fù)用車(chē)把手中的屬性和方法
private Tire tire; //可以復(fù)用輪胎中的屬性和方法
}
作者:luka.lh
over?。?!
到此這篇關(guān)于Java中的重要核心知識(shí)點(diǎn)之繼承詳解的文章就介紹到這了,更多相關(guān)Java 繼承內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot自定義啟動(dòng)器Starter流程詳解
SpringBoot中的starter是一種非常重要的機(jī)制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進(jìn)starter,應(yīng)用者只需要在maven中引入starter依賴,SpringBoot就能自動(dòng)掃描到要加載的信息并啟動(dòng)相應(yīng)的默認(rèn)配置。starter讓我們擺脫了各種依賴庫(kù)的處理,需要配置各種信息的困擾2022-11-11
Jsoup獲取全國(guó)地區(qū)數(shù)據(jù)屬性值(省市縣鎮(zhèn)村)
這篇文章主要介紹了Jsoup獲取全國(guó)地區(qū)數(shù)據(jù)屬性值(省市縣鎮(zhèn)村)的相關(guān)資料,需要的朋友可以參考下2015-10-10
Springboot與vue實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出方法具體介紹
這篇文章主要介紹了Springboot與vue實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-02-02
詳解Java 反射和反射的應(yīng)用場(chǎng)景
這篇文章主要介紹了Java 反射和反射的應(yīng)用場(chǎng)景的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java反射的相關(guān)知識(shí),感興趣的朋友可以了解下2020-08-08
出現(xiàn)SLF4J:?Failed?to?load?class?“org.slf4j.impl.StaticLog
本文主要介紹了出現(xiàn)SLF4J:?Failed?to?load?class?“org.slf4j.impl.StaticLoggerBinder“.的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
詳解spring cloud整合Swagger2構(gòu)建RESTful服務(wù)的APIs
這篇文章主要介紹了詳解spring cloud整合Swagger2構(gòu)建RESTful服務(wù)的APIs,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Mybatis plus實(shí)現(xiàn)Distinct去重功能
這篇文章主要介紹了Mybatis plus實(shí)現(xiàn)Distinct去重功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

