Java:继承和多态
创始人
2024-01-21 13:38:21
0

文章目录

  • 前言
  • 一、继承
    • 1.继承概念
    • 1.1 继承的语法
    • 1.2 父类成员方法
    • 1.2.1 子类访问父类的成员变量
    • 1.2.2 子类访问父类的成员方法
    • 1.3 super、this 关键字
    • 1.4 子类构造方法
    • 1.5 继承的方式
    • 1.6 final 关键字
    • 1.7 继承与组合
  • 二、多态
    • 2.1 多态的概念
    • 2.2 多态实现的条件
    • 2.3 对重写的认识
      • 2.3.1 重写与重载
    • 2.4 向上转型与向下转型
      • 2.4.1 向上转型
      • 2.4.2 向下转型
    • 2.5 避免在构造方法中调用重写方法
  • 总结

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。


前言

本篇是对继承的介绍,super的使用与this的区别,final关键字,继承方式的讲解和多态的实现,重写,向上/下转型等。如有错误,请在评论区指正,让我们一起交流,共同进步!


本文开始

一、继承

1.继承概念

继承:用于对共性进行抽取,实现代码的复用。
继承机制:允许程序员在保持原有类特性的基础上进行拓展,增加新的功能。这样产生类称为派生、子类。原有的类称为父类,基类,超类。

1.1 继承的语法

使用extends关键字

修饰符 class 子类 extends 父类 {
}

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

1.2 父类成员方法

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

1.子类和父类不存在同名方法

class Base {int a = 1;int b;
}
class Derived extends Base {int c;public void func() {System.out.println(a);//访问继承父类的aSystem.out.println(c);//访问子类自己的c}
}

2.子类和父类成员变量同名

class Base {int a;int b;int c = 9;
}
class Derived extends Base {int c = 10;public void func() {System.out.println(a);System.out.println(b);System.out.println(c);//System.out.println(d);//报错,子类父类都没有定义}
}

子类和父类有同名变量,依照就近原则,先使用子类自己的变量,子类没有在找父类的变量!

在这里插入图片描述

【注】在子类方法(对象)中,访问成员:
1.访问成员变量子类中有,就先访问子类自己的
2.访问成员变量子类中无,就先访问父类的,父类没有,就报错
3.访问成员变量父类子类同名的成员变量,就先访问子类自己的

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

1.子类和父类成员方法名相同

class Base {public void methodBase() {System.out.println("Base里的methodBase()");}
}
class Derived extends Base {public void menthodBase() {System.out.println("Derived里的menthodBase()");}
}

2.子类和父类成员方法名不同
优先访问自己的成员方法,自己没有时再到父类中找,如果父类中也没有则报错。

class Base {int a;int b;int c = 9;public void methodBase() {System.out.println("Base里的methodBase()");}
}
class Derived extends Base {int c = 10;public void methodDerived() {System.out.println("Derived()");}
}

【小结:】
1.子类对象访问父类与子类的 同名方法,根据调用方法传递参数列表选择访问(重载),没有则报错
2.子类对象访问父类与子类的 不同名方法时,优先在子类中找,找不到,在父类找,找到访问,找不到报错

1.3 super、this 关键字

super作用 : 子类和父类中存在相同的成员时,使用super,super可以在子类方法中访问父类的成员。

super调用在子类中调用了父类的成员变量和方法

class Base {int a;int b;int c = 9;public void methodBase() {System.out.println("Base里的methodBase()");}
}
class Derived extends Base {int c = 10;public void methodBase() {System.out.println("Derived里的menthodBase()");super.methodBase();}public void func() {System.out.println("Derived中的 c = " + c);System.out.println("Base中的 c = " + super.c);}
}

代码结果:

在这里插入图片描述

【注】
1.在子类方法中,如果想要明确访问父类中成员时,借助super关键字
2.只能在非静态方法中使用


this,super不同点:
1.this是当前对象的引用,当前对象即调用实例方法的对象;super相当于是子类对象中从父类继承下来部分成
员的引用
2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3. 在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造方法中出现
4.构造方法中一定会存在super()的调用,用户没有写编译器也会增加,但是this()用户不写则没有

super,this相同点:
1.只能在类的非静态方法中使用,用来访问非静态成员方法和字段
2.在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在


1.4 子类构造方法

子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法。(先构造父类,在构造子类)

1.父类定义无参构造方法或默认构造方法,在子类构造方法中第一行默认含有super()调用;(父类或者子类不写构造方法,默认父类子类无参构造)

class A {public A() {System.out.println("A的无参构造");}
}
class B extends A{public B() {super();//调用父类无参构造System.out.println("B的无参构造");}
}
public class Test {public static void main(String[] args) {new B();}
}

代码结果:

在这里插入图片描述

2.如果父类含有带参数的构造方法,此时子类构造需要选择合适的父类构造方法调用;(父类如果写了含参数的构造方法,则就没有无参的构造方法,如果需要用需要自己写无参构造)
3.在子类构造方法中,super()调用父类构造,必须是子类构造函数中的第一条语句
4.super()只能在子类构造方法中出现一次,并且不能和this同时使用;(this(),super()在构造方法中都必须在第一行,所有不能共存)

1.5 继承的方式

1.单继承:
B =》A

class A {
...
}
class B extends A{
}

2.多层继承
C =》B =》A

class A {
....
}
class B extends A{
....
}
class C extends B {....   
}

3.不同类继承同一个类
同一包中不同java文件中

public class A {
}
public class B extends A {
}
public class C extends A {
}

4.多继承(不支持)=》err
JAVA中不支持多继承

public class A {
}
public class B {
}
public class C extends A,B {
}

1.6 final 关键字

1.final 修饰变量或字段,表示常量(不能修改)

final int a = 10;a = 20;

2.final 修饰类:表示类不能被继承

final public class A {
}
//err
public class B extends A {
}

3.final 修饰方法:不能被重写

1.7 继承与组合

组合:仅仅将一个类的示例作为另一个类的字段,为了达到代码的重用

继承:表示对象之间是 is-a 的关系,比如:狗是动物,猫是动物
组合:表示对象之间是 has-a 的关系,比如:学校作为类,学生和老师都是学校里的示例字段

class Stduent {
...
}
class Teacher {
...
}
class School {Stduent[] stduents = new Stduent[3];Teacher[] teachers = new Teacher[3];
}
public class TestDemo {public static void main(String[] args) {School school = new School();}
}

二、多态

2.1 多态的概念

多态 :为了完成某个行为,不同的对象去完成时会产生不同的状态。(同一件事,发生在不同对象上,会产生不同的结果)

例如:动物吃东西
狗吃狗粮,鸟吃鸟粮,鱼吃鱼粮。同样是吃饭的行为,但他们吃的什么却不同。

2.2 多态实现的条件

Java实现多态的条件

  1. 必须在继承体系下(子类继承父类)
  2. 子类必须要对父类中方法进行重写
  3. 通过父类的引用 , 调用重写的方法

多态示例图示:
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

在这里插入图片描述

在这里插入图片描述
发生重写,子类发生不同的状态,7吃狗粮, qq吃鸟粮
在这里插入图片描述

我们下面讲解为什么重写后会调用子类的重写的eat()方法,为什么不调用父类的?

2.3 对重写的认识

重写(override):也称为覆盖。即外壳不变,核心重写! 也就是说子类能够根据需要实现父类的方法。

重写是子类对 ①父类非静态、②非private修饰,③非final修饰,④非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。
被final修饰的方法称为密封方法。

重写规则:

  1. 子类重写父类,方法的返回值类型、方法名、参数列表相同
  2. 具有父子类关系,被重写的方法返回值类型可以不同
  3. 子类访问权限要 大于 父类的访问权限;父类方法是public修饰的,子类重写不能是protected
  4. 父类中被①static②private③构造方法都不能被重写
  5. 重写的方法,可以使用 @Override 来注释,可以帮助我们进行合法性校验。如果重写的方法名拼写错误,编译器就会发并报错。

发生动态绑定:编译的时候不确定,在从重写的时候,调用子类的重写方法
静态绑定: 重载,编译的时候已经确定调用那个方法

问题1:为什么重写后会调用子类的重写的eat()方法,为什么不调用父类的?
这是因为在重写的时候会发生动态绑定,会在程序运行的时候,帮我们调用了重写的方法。

问题2:为什么不是编译的时候调用子类的重写方法呢?
我们可以通过class字节码文件来看一下
找到字节码文件,通过命令行打开,输入:javap - c 文件名

在这里插入图片描述
通过查看字节码文件可知,编译时调用的还是父类的eat()方法。

2.3.1 重写与重载

在这里插入图片描述

2.4 向上转型与向下转型

2.4.1 向上转型

向上转型:父类对象引用子类的对象
语法格式:父类类型 对象名 = new 子类类型();
向上转型缺点:不能调用子类特有的方法

三种使用:
1.直接赋值
Animal animal = new Dog();

public static void main(String[] args) {Animal animal = new Dog();//animal这个引用只能引用Animal这个类里面的方法//发生向上转型后:父类的引用只能访问父类自己的成员。不能访问到子类特有的成员//相对于子类访问的范围更大一些animal.name = " 7 ";animal.eat();//animal.wang();不能访问子类的方法Animal animal1 = new Bird();//向上转型。前提: Bird继承Animalanimal1.name = "qq";animal1.eat();}

2.方法传参
func(dog)

 public static void func(Animal animal) {//方法传参}public static void main(String[] args) {Dog dog = new Dog();func(dog);}

3.返回值
Animal animal = func2();

public static Animal func2() {//返回值return new Dog();}

2.4.2 向下转型

向下转型:为了调用子类特有的方法,我们可以将父类引用还原为子类对象

public static void main(String[] args) {Animal animal = new Dog();//向下转型:不安全System.out.println("=========");Bird bird = (Bird) animal;bird.fly();}

向下转型不安全,编译没问题,但是运行时会报错

在这里插入图片描述

代码修改:

public static void main(String[] args) {Animal animal = new Dog();//向下转型:不安全,需要判断使用关键字instanceof//父类的类型给到子类Dog dog = (Dog) animal;dog.name = "7";dog.wang();System.out.println("=========");if(animal instanceof Bird) {//判断animal是不是引用了Bird的对象Bird bird = (Bird) animal;bird.fly();}}

判断后结果就不会报错了

在这里插入图片描述

2.5 避免在构造方法中调用重写方法

class B {public B() {
// do nothingfunc();}public void func() {System.out.println("B.func()");}
}
class D extends B {private int num = 1;@Overridepublic void func() {System.out.println("D.func() " + num);}
}
public class TestDemo {public static void main(String[] args) {D d = new D();}
}

代码结果:

在这里插入图片描述

结果可知:
num值为0,并没有赋值为1,这是因为在构造器中调用方法所导致的;
代码执行过程:
1.构造D对象的同时调用B构造方法
2.B 的构造方法调用func()方法,此时会触发动态绑定,会调用D中的func()
3.此时D 对象自身还没有构造,此时num处于为初始化状态,值默认0,如果具备多态性,num的值应该是1

【注】尽量不要在构造器中调用方法!!!
当在父类的构造方法当中,去调用父类和子类重写的方法的时候,会发生动态内存绑定,会调用子类重写的同名方法,而产生问题!

总结

✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...