【JavaSE】继承的详解
创始人
2025-05-29 07:13:56
0

前言

大家好,我是那个不会打拳的程序猿。今天我给大家带来的是面向对象之封装继承多态中的继承,文章通过继承的语法、父类成员的访问、super关键字、子类的构造方法、protected关键字等方面来详细讲解继承的用法。

目录

1.继承的概念

1.1继承的语法

1.2父类成员访问

1.2.1子类中访问父类的成员变量

1.2.2子类中访问父类的成员方法

1.3super关键字

1.4子类构造方法 

1.5super和this关键字

1.6protected关键字

1.7继承方式

1.8final关键字 


1.继承的概念

继承机制:是面向对象编程的一个重要体现,它主要是用来使一些重复的代码只用编写一份达到共用的效果。它主要解决:共性的抽取,实现代码的复用。

例如:狗和猫都是动物,他们共有的特性就是吃、睡、跑等,因此我们可以把这些特性作为一份代码,使得狗和猫这两类能共用这份代码,避免写两份代码甚至更多代码。

 通过Java就能写出这样一些代码:

class Dog {String run;String eat;public void sleep() {System.out.println("睡觉");}public void wangWang() {System.out.println("汪汪叫");}
}class Cat {String run;String eat;public void sleep() {System.out.println("睡觉");}public void wangWang() {System.out.println("汪汪叫");}
}

我们发现,狗类和猫类中的这一部分代码是相同的,因此,我们可以把这一部分代码专门用一个类来写出来,从而能人狗和猫类公共使用这份代码,这就是继承的思想。如图:

上图展示了,Dog和Cat类可以继承Animal类中的属性,并且Dog和Cat类也有属于自己的属性。其中Animal为父类(基类/超类),Dog和Cat为子类(派生类)。那么这样的关系怎样用代码去实现呢?请看下方讲解! 


1.1继承的语法

在Java中,我们如果要表示类与类之间的关系,可以通过extends关键字来区分:

修饰符 class 子类 extends 父类 {//....
}

那么对上方场景中Animal、Dog和Cat类使用继承方式设计如下:

class Animal {String run = "跑";String eat = "吃";public void sleep() {System.out.println("睡觉");}
}
class Dog extends Animal {public void wangWang() {System.out.println("汪汪叫");}
}class Cat extends Animal {public void miaoMiao() {System.out.println("喵喵叫");}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();Cat cat = new Cat();//dog类中并没有定义任何成员变量,run和eat是从父类Animal中继承过来的。System.out.println(dog.run);System.out.println(dog.eat);dog.wangWang();//dog类中并没有定义任何成员变量,run和eat是从父类Animal中继承过来的。System.out.println(cat.run);System.out.println(cat.eat);cat.miaoMiao();}
}

上述代码,我们之间在main方法中实例化Dog和Cat这两个类,Dog和Cat这两个类都继承了run和eat这两个成员变量,并且Dog类和Cat类他们都有自己的属性汪汪叫和喵喵叫。因此子类可以继承父类中的属性,而且子类也可以有属于自己的属性。

注意:

  1. 子类会将父类中的成员变量或者成员方法继承到子类中了
  2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

1.2父类成员访问

在继承体系当中,子类将父类的方法和字段可以继承下来,那么在子类中能否不通过实例化对象直接访问父类中的继承下来的成员或方法呢?


1.2.1子类中访问父类的成员变量

(1)子类和父类不存在同名成员变量

class Animal {String run = "跑";String eat = "吃";
}
class Dog extends Animal {String sleep = "睡觉";public void show() {System.out.println(run);//访问从父类中继承下来的runSystem.out.println(eat);//访问从父类中继承下来的eatSystem.out.println(sleep);//访问子类自己的sleep}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.show();}
}

通过以上代码我们可以看到,访问父类继承下来的成员变量是毫无争议的。子类中也有关于子类自己的属性因此访问自己的成员变量也是没有异议的。但是当子类与父类中的成员变量同名的时候,我们该如何去从呢?

 (2)子类和父类成员变量同名

class Animal {String run = "跑";String eat = "吃";
}
class Dog extends Animal {String run = "跑步";String sleep = "睡觉";public void show() {System.out.println(run);//访问子类自己的runSystem.out.println(eat);//访问从父类中继承下来的eatSystem.out.println(sleep);//访问子类自己的sleep}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.show();}
}

 以上代码中,我们发现父类与子类中的成员变量run相同了,因此在子类中我们会优先访问子类自己的run,并不会继承父类中run的属性。

因此,在子类方法中 或者 通过子类对象访问成员时

  • 如果访问的成员变量子类中有,优先访问自己的成员变量。
  • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。


1.2.2子类中访问父类的成员方法

(1)成员方法名字不同

class Animal {public void funA() {System.out.println("Animal中的funA()");}
}
class Dog extends Animal {public void funB() {System.out.println("Dog中的funB()");}public void show() {funA();funB();}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.show();}
}

以上代码中,子类与父类中都没有成员方法同名的情况,因此没有报错情况。那么在子类方法中或者子类对象访问方法时,优先访问自己的,没有时再访问父类中的,如果父类中也没有则会报错

(2)成员方法名字相同

class Animal {public void funA() {System.out.println("Animal中的funA()");}public void funB() {System.out.println("Animal中的funB()");}
}
class Dog extends Animal {public void funA(int num) {System.out.println("Dog中的funA()");}public void funB() {System.out.println("Dog中的funB()");}public void show() {funA();//访问了父类中的funA方法funA(6);//因为有了参数,访问了子类中的funA方法funB();//访问的是子类中的funb方法}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.show();}
}

以上代码我们可以看到,访问的优先级也是跟成员方法是一样的。子类里面有的则访问子类里面的,子类里面没有的则访问父类里面的。那如果子类与父类中的成员变量或者成员方法名相同时,我们非要访问父类里面的成员变量或成员方法那怎么办呢?我们可以加上一个super关键字


1.3super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员

class Animal {String name = "赛虎";public void funA() {System.out.println("Animal中的funA()");}public void funB() {System.out.println("Animal中的funB()");}
}
class Dog extends Animal {String name = "小米";public void funA() {System.out.println("Dog中的funA()");}public void funB() {System.out.println("Dog中的funB()");}public void show() {System.out.println(super.name);super.funA();super.funB();}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.show();}
}

 以上代码中,子类与父类中都有相同的成员变量名与方法名,但是在我们的show方法里使用了关键字super使得子类引用了父类的成员变量与方法,这就是super关键字的作用。


1.4子类构造方法 

我们在子类里面执行构造方法时,必须要先调用父类构造方法,然后才能执行子类构造方法。

class Base {public Base() {System.out.println("这是一个无参的父类构造方法");}
}
class Derived extends Base {public Derived() {//编译器会默认增加一个super();语句System.out.println("这是一个子类的构造方法");}
}
public class Test2 {public static void main(String[] args) {Derived der = new Derived();}
}

以上代码,我们可以看到。我们在main方法中创建der对象后,先执行了父类中的构造方法然后再执行了子类中的构造方法。因为编译器会默认在子类的构造方法中第一行添加一条语句:super();使得我们的父类构造方法优先级最高。

class Base {public Base(int a,int b) {System.out.println("这是带两个参数的父类构造方法");}
}
class Derived extends Base {public Derived() {System.out.println("这是一个子类的构造方法");}
}
public class Test2 {public static void main(String[] args) {Derived der = new Derived();}
}

当我们这样去编写代码时,就会报错。那是因为我们子类的构造方法中编译器默认提供的super()语句里面没有参数,因此我们应该这样写代码:

class Base {public Base(int a,int b) {System.out.println("这是带两个参数的父类构造方法");}
}
class Derived extends Base {public Derived() {super(2,3);System.out.println("这是一个子类的构造方法");}
}
public class Test2 {public static void main(String[] args) {Derived der = new Derived();}
}

当我们在子类里面,使用super()语句给父类中的构造方法赋初值后,子类的构造方法才能运行下去。注意,这个super语句如果我们没有人为的在子类构造方法中写入的话,编译器会默认super这个语句的存在。因此我们父类构造方法为无参的也就可以省略不写,如果是有参的就自己写上super语句并赋上值

注意:

  • 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构
  • 造方法
  • 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的
  • 父类构造方法调用,否则编译失败。
  • 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
  • super(...)只能在子类构造方法中出现一次,并且不能和this同时出现

1.5super和this关键字

superthis都可以在成员方法中访问成员变量或调用成员方法,并且都可以放在构造方法的第一行,那他们直接有什么区别呢? 

相同点:

  • 都是Java中的关键字
  • 只能在类中的非静态方法中使用,用来访问非静态成员方法和变量
  •  在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
class Derived extends Base {public Derived() {super(2,3);this();System.out.println("这是一个子类的构造方法");}
}

class Derived extends Base {public Derived() {this();super(2,3);System.out.println("这是一个子类的构造方法");}
}

我们可以看到,当super和this两个关键字同时在子类构造方法中出现时。都出现必须要在第一条语句的说法。

不同点:

  • this是当前对象的引用,而super是对父类中的成员或方法的引用。
  • 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
  • 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有
class Derived extends Base {public Derived(int c,int d) {super(2,3);this(3,4);}
}

当我们同时在一个构造方法中使用super(...)和this(...)就会报错,但如果我们分别使用super和this两个独特的功能时,比如this对当前对象的引用,super对父类构造方法进行传参。就不会造成冲突

class Base {public Base(int a,int b) {System.out.println("a="+a+" b="+b);}
}
class Derived extends Base {int c;int d;public Derived(int c,int d) {super(2,3);this.c = c;this.d = d;System.out.println("c="+c+" d="+d);}
}
public class Test2 {public static void main(String[] args) {Derived der = new Derived(4,5);}
}

 以上代码,我们可以看到,如果super和this各自用各自独特的功能就不会发生报错。


1.6protected关键字

(1)同一个包内同一个类

public class Test {protected int num = 99;public static void main(String[] args) {Test test = new Test();System.out.println(test.num);}
}

 以上代码展示了在同一个包同一个类内里面,被protected修饰的成员变量可以被使用。


(2)同一包中不同类

package Pack1;class Dog {protected int num = 88;
}
public class Test {public static void main(String[] args) {Dog dog = new Dog();System.out.println(dog.num);}
}

  同样在同一包内,我们照样可以很清晰的访问到被protected修饰的变量。


(3)不同包中的子类

//包1中
package Pack1;public class Test1 {protected int num = 99;public static void main(String[] args) {}
}
//包2中
package Pack2;//导入包1中的Test1类
import Pack1.Test1;//继承包1中的Test1父类
public class Test2 extends Test1{public static void main(String[] args) {Test1 test = new Test1();System.out.println(test.num);}
}

当我们在不同包子类访问父类的时候,按我们在同一个包内的情况我们可以直接实例化并继承父类中所有的成员和方法。但是在不同包的时候,被protected修饰的变量不能随意继承,因此我们得使用super关键字来引用被protected修饰的变量或方法

//包1
package Pack1;public class Test1 {protected int num = 99;public static void main(String[] args) {}
}
//包2
package Pack2;//导入包1中的Test1类
import Pack1.Test1;//继承包1中的Test1父类
public class Test2 extends Test1{public void show() {System.out.println(super.num);}public static void main(String[] args) {Test2 test = new Test2();test.show();}
}

 以上代码我们可以看到,通过super关键字就能很好的访问到了protected修饰的关键字了。因此有以下表格:

NO范围privatedefaultprotectedpublic
1同一包中的同一类可以访问可以访问可以访问可以访问
2同一包中不同类不可以访问可以访问可以访问可以访问
3不同包中的子类不可以访问不可以访问可以访问可以访问
4不同包中的非子类不可以访问不可以访问不可以访问可以访问

 以上情况大家可以一一测试一下,体会各个关键字的不同之处。


1.7继承方式

我们的继承方式分为:单继承、多层继承、不同类继承同一类

(1)单继承

//此类为父类
class Animal {
}
//此类为子类
class Dog extends Animal {
}

以上代码展示了子类继承父类的情况,这是一个比较常见的继承方式。

(2)多层继承

//此类为父类
class Animal {
}
//Dog类继承Animal类
class Dog extends Animal {
}
//Cat类继承Dog类
class Cat extends  Dog {
}

以上代码展示了多层继承的情况,当这个层次为三层的时候,它是第一个类作为"爷爷类",第二个类作为"儿子类"继承了它的父类也就是"爷爷类",第三个类作为"孙子类"继承它的父类也就是"儿子类"。这就是一个多层继承的方式。

(3)不同类继承同一个类

//此类为父类
class Animal {
}
//Dog类继承Animal类
class Dog extends Animal {
}
//Cat类继承Animal类
class Cat extends  Animal {
}

以上代码展示了不同类继承同一个类的情况,也就是一个类为父类,其余的类作为子类都继承这个父类。我们称之为不同类继承同一个类。

(4)多继承是不可行的

//不能继承多个父类
class Me extends Animal,Dog {
}

当我们把一个类继承多个类的时候,编译器先是通过不了的,因此我们只能依靠以上三种方式去实现继承。 


1.8final关键字

final关键字会锁定被修饰的变量或者方法,当你试图修改被final修饰的变量或方法时,编译器会报错,令你无法通过编译。

(1)final修饰变量

public class Test3 {public static void main(String[] args) {final int a = 10;a = 20;}
}

当我们在main方法里定义一个用final修饰的变量后,想对它进行修改。此时是不可行的,被final修饰的变量或方法是不可修改的。 

(2)final修饰类

final class Father {
}
class Son extends Father {
}

当我使用final修饰类的时候,此时不能把此类当作父类给其他类继承。


本期博客到这里就结束啦,相信大家对继承有了一定的了解,感谢你的阅读,如有收获还请三连支持。

 下期预告:多态

相关内容

热门资讯

mac 系统安装 安卓系统安装... 亲爱的电脑小白们,是不是最近对电脑系统安装跃跃欲试,但又觉得无从下手?别担心,今天我就要来给你详细讲...
提醒安卓系统升级,体验流畅新篇... 亲爱的安卓用户们,是不是觉得手机越来越卡,应用更新总是跟不上潮流?别急,今天我要给你来点干货,告诉你...
安卓系统outlook会议提醒... 你有没有发现,手机上的安卓系统越来越智能了?这不,最近我发现了一个超实用的功能——Outlook会议...
安卓系统专业软件剪辑,打造高效... 你有没有想过,手机里的视频剪辑功能竟然也能如此专业?没错,就是那个我们每天不离手的安卓系统,它竟然能...
模拟安卓系统软件,软件功能与体... 你有没有想过,手机里的世界可以变得更加丰富多彩?没错,就是那种可以像安卓系统一样自由自在地玩耍的世界...
安卓换系统会卡吗,换系统会卡吗... 你有没有想过,你的安卓手机用久了,是不是也会像人一样,反应变得迟钝了呢?没错,这就是我们今天要探讨的...
平板安卓系统自动重启,安卓平板... 你是不是也遇到过这种情况?平板电脑突然间就自动重启了,是不是瞬间感觉心里一紧,心想这可怎么办呢?别急...
findx3安卓系统,安卓系统... 你有没有发现,最近手机圈里又掀起了一股热潮?没错,就是OPPO Find X3系列的安卓系统。这款系...
安卓系统删除的软件,那些曾陪伴... 手机里的软件越来越多,是不是有时候觉得内存不够用,想清理一下呢?别急,今天就来聊聊安卓系统删除软件的...
白色的手机安卓系统,安卓系统下... 你有没有发现,最近市面上那些白色的手机简直让人眼前一亮呢?尤其是搭载安卓系统的那些,简直就像是一块块...
vico是不是安卓系统,揭秘安... 最近是不是有很多小伙伴在问:“Vico手机,它是不是运行安卓系统呢?”这个问题可真是让人好奇啊!今天...
安卓10系统省电不,安卓10系... 你有没有发现,自从升级到安卓10系统,手机续航能力好像大不如前了?别急,今天就来给你揭秘安卓10系统...
cm14安卓系统,深度定制与极... 你有没有发现,你的安卓手机最近是不是有点不一样了?是不是觉得系统运行得更加流畅,界面也更加美观了呢?...
平板安卓系统咋样升级,轻松实现... 你那平板安卓系统是不是有点儿卡,想给它来个升级大变身?别急,让我来给你详细说说平板安卓系统咋样升级,...
安卓原系统在哪下载,探索纯净体... 你有没有想过,为什么安卓手机那么受欢迎?那是因为它的系统——安卓原系统,它就像是一个充满活力的魔法师...
安卓系统procreate绘图... 你有没有发现,现在手机上画画变得越来越流行了?尤其是用安卓系统的手机,搭配上那个神奇的Procrea...
电视的安卓系统吗,探索安卓电视... 你有没有想过,家里的电视是不是也在悄悄地使用安卓系统呢?没错,就是那个我们手机上常用的安卓系统。今天...
苹果手机系统操作安卓,苹果iO... 你有没有发现,身边的朋友换手机的时候,总是对苹果和安卓两大阵营争论不休?今天,咱们就来聊聊这个话题,...
安卓系统换成苹果键盘,键盘切换... 你知道吗?最近我在想,要是把安卓系统的手机换成苹果的键盘,那会是怎样的体验呢?想象那是不是就像是在安...
小米操作系统跟安卓系统,深度解... 亲爱的读者们,你是否曾在手机上看到过“小米操作系统”和“安卓系统”这两个词,然后好奇它们之间有什么区...