⭐⭐⭐Java基础篇目录⭐⭐⭐
🍁 第一章 Java开发环境搭建
🍁 第二章 标识符与关键字
🍁 第三章 变量
🍁 第四章 数据类型
🍁 第五章 运算符
🍁 第六章 控制语句
🍁 第七章 方法
🍁 第八章 认识面向对象
🍁 第九章 对象的创建和使用
🍁 第十章 封装
🍁 第十一章 this和static
🍁 第十二章 继承
🍁 第十三章 方法的覆盖和多态
🍁 第十四章 super

子类继承父类后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行“方法的覆盖”
😉
方法的覆盖又称方法的重写。
注意区分方法的重载:
方法的重载是一个类中。方法的功能相似的话,建议将方法名定义成一样的。
重载的构成条件是:

当子类对从父类继承过来的方法进行“方法覆盖”以后,子类对象调用该方法的时候,一定是执行覆盖之后的方法。
方法覆盖的构成条件:


public class OverrideTest {
public static void main(String[] args){
Cats c = new Cats();
Birds b = new Birds();
c.move();
b.move();
b.move(1);
}
}
class Animals{
public void move(){
System.out.println("动物在移动");
}
}
class Cats extends Animals{
public void move(){
System.out.println("走猫步");
}
}
class Birds extends Animals{
//形参列表不同,这里不构成覆盖
//相比继承的无参的方法,这里构成重载
public void move(int i){
System.out.println("鸟在飞");
}
}
运行结果:



toString()方法的输出是一个Java对象的内存地址,所以大多数时候可能需要覆盖,从而在System.out.println(引用);时,可以输出对象直观的信息,而非内存地址。
public class TestOverride {
public static void main(String[] args) {
MyDate date = new MyDate();
System.out.println(date);
}
}
class MyDate{
private int year;
private int month;
private int day;
public int getYear(){
return this.year;
}
public void setTear(int year){
this.year = year;
}
public int getMonth(){
return this.month;
}
public void setMonth(int month){
this.month = month;
}
public int getDay(){
return this.day;
}
public void setDay(int day){
this.day = day;
}
//this在构造方法中的用法
public MyDate(){
this(2022,10,27);
}
public MyDate(int year,int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
//覆盖默认继承类Object中的toString()方法
public String toString(){
//同一个类中,this省了
return year+"年"+month+"月"+day+"日";
}
}
toString()方法覆盖以后,不再输出对象的内存地址,输出了清晰了对象的信息

😉
父类型的引用指向子类型的对象,在编译阶段绑定的是父类型的方法, 在运行阶段绑定的是子类型对象的方法,即多种形态。


public class Test {
public static void main(String[] args){
Animals a = new Animals();
Birds b = new Birds();
Cats c = new Cats();
a.move();
c.move();
b.move();
System.out.println("多态运行结果:");
//多态
//父类型的引用指向子类型的对象
Animals a2 = new Birds();
Animals a3 = new Cats();
//无继承关系,error
//Animals a4 = new Dog();
a2.move();
a3.move();
}
}
class Animals{
public void move(){
System.out.println("动物在移动");
}
}
class Cats extends Animals{
public void move(){
System.out.println("走猫步");
}
}
class Birds extends Animals{
public void move(){
System.out.println("鸟在飞");
}
}
class Dog{
}

运行结果:

😉
程序分析:
1 )编译阶段:
编译器只知道a2是Animals类型,所以编译器检查语法的时候,去Animals.class字节码文件中找move()方法,找到了,绑定上move()方法,编译就通过了,静态绑定成功。(编译阶段属于静态绑定)
2)运行阶段:
实际上堆内存中创建的对象是Cat对象,调用move()方法的对象是new Cat()出来的,所以运行阶段执行Cat对象的move方法,此过程为运行阶段绑定。(运行阶段属于动态绑定)

运行阶段和底层堆内存中的实际对象相关,执行时会自动调用实际堆内存中实际对象的相关方法。

当调用的方法是父类有的时候,如:a2.move(),静态绑定和动态绑定阶段都没问题,但当父类型的引用调用的是子类型对象中特有的方法时,静态绑定的编译阶段就会出错,此时就需要向下转型。
//向下转型的前提是有继承关系
Animals a5 = new Cats();
//用一个Cats类型的变量去装转型的Animals类型的a5
Cats x = (Cats)a5;
x.catchMouse();
向下转型成功是因为,编译器看a5是Animals类型,而要转的类Cats和Animals有继承关系,所以成功。而当一个父类有多个子类的时候,即使要转的类和实际对象的类不一样,只要有继承关系,也能转成功。
//正常new对象加一个简单的多态
Animals a6 = new Birds();
//Cats类和a6类型的Animals类有继承关系,能转型成功
Cats y = (Cats)a6;
//y是Cats类型,有catchMouse()方法,编译通过
y.catchMouse();
运行的时候,那Birds类的对象转成Cat类的对象,出错了。

经典错误—java.lang.ClassCastException,类型转换异常

instanceof的整理:
(引用 instanceof 类型)
由此之前的ClassCastException:
Animal a6 = new Bird();
Cat y = (Cat)a6;
y.catchMouse();
可修改为:
if (a6 instanceof Cat){
Cat y = (Cat)a6;
y.catchMouse();
}

为避免ClassCastException,Java规范中要求向下转型必须使用instanceof.



思考,main方法中,我以前看到引用x指向Birds()对象,干嘛还if…else if
大项目中合作分工,若有个方法的形参是Animals a,如public void test (Aniamls a){ },调用者传入的可能是Animals类型,也可能是Birds类型、Cats类型,这就需要if+instanceof

作用:
软件在扩展新需求的过程中,修改的越少越好,修改的越多,系统的稳定性就越差,未知风险就越多,还可能得重新测试。
软件开发的原则之一就是OCP原则,即开闭原则,对扩展开放,对修改关闭,直白的说就是软件扩展的过程中,对之前的东西修改的越少越好。

面向抽象编程,而不是面向具体。



😉
封装、继承、多态三个面向对象的特征环环相扣。有了封装这一整体概念之后,对象和对象之间产生了继承,有了继承之后,才有了方法的覆盖和多态。
上面多态用于程序扩展的例题中,核心是用父类型的引用做形参,调用的时候传入子类型的对象。

**静态方法不谈覆盖。**因为哪怕你覆盖了,并强行用”引用.“,编译时也会当类名.来处理,进而没有多态这一说,覆盖也就变的没意义了。

public class Override {
//定义私有方法
private void test(){
System.out.println("私有的");
}
public static void main(String[] args) {
Override override2 = new Override2();
override2.test();
}
}
class Override2 extends Override{
//尝试给更高的权限来覆盖
public void test(){
System.out.println("尝试覆盖");
}
}
运行结果不是覆盖后的test()方法;

私有的方法不能被覆盖,静态的方法不谈覆盖!!

关于方法覆盖时返回值类型,若原先是基本数据类型,则重写前后必须一致,若是引用数据类型,重写时可以变的更小,不能变的更大。(把Animals返回值类型的变成它子类型Cat的可以,变成Object类型的不行)

重写方法时,直接粘贴一份父类的代码过来,再修改,这样最干脆。