⭐⭐⭐Java基础篇目录⭐⭐⭐
🍁 第一章 Java开发环境搭建
🍁 第二章 标识符与关键字
🍁 第三章 变量
🍁 第四章 数据类型
🍁 第五章 运算符
🍁 第六章 控制语句
🍁 第七章 方法
🍁 第八章 认识面向对象
🍁 第九章 对象的创建和使用
🍁 第十章 封装
🍁 第十一章 this和static
🍁 第十二章 继承
🍁 第十三章 方法的覆盖和多态
🍁 第十四章 super
文章目录
1、 方法的覆盖(Override)
子类继承父类后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行“方法的覆盖”
😉
方法的覆盖又称方法的重写。
注意区分方法的重载:
方法的重载是一个类中。方法的功能相似的话,建议将方法名定义成一样的。
重载的构成条件是:
- 在同一个类中
- 方法名相同
- 参数列表不同(参数个数、参数类型、个别的参数顺序)
2、方法覆盖的构成条件
当子类对从父类继承过来的方法进行“方法覆盖”以后,子类对象调用该方法的时候,一定是执行覆盖之后的方法。
方法覆盖的构成条件:
- 两个类必须有继承关系
- 重写之后的方法和之前的方法具有相同的返回值类型(后续有变)、相同的方法名、相同的形式参数列表
- 访问权限不能更低,可以更高,如protected到public
- 重写之后的方法不能比之前的方法抛出更多的异常
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("鸟在飞");
}
}
运行结果:
3、覆盖的注意事项
- 覆盖是方法的覆盖,只针对于方法,和属性无关
- 私有方法无法覆盖
- 构造方法不能被继承,所以构造方法也无法被覆盖(所以尽管父类中有有参构造方法,子类对象也调用不了,但大家都有默认的无参构造方法)
- 方法的覆盖只针对“实例方法”,静态方法的覆盖没有意义
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()方法覆盖以后,不再输出对象的内存地址,输出了清晰了对象的信息
😉
4、多态
父类型的引用指向子类型的对象,在编译阶段绑定的是父类型的方法, 在运行阶段绑定的是子类型对象的方法,即多种形态。
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方法,此过程为运行阶段绑定。(运行阶段属于动态绑定)
运行阶段和底层堆内存中的实际对象相关,执行时会自动调用实际堆内存中实际对象的相关方法。
5、多态之向下转型
当调用的方法是父类有的时候,如: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,类型转换异常
6、instanceof–避免ClassCastException
instanceof的整理:
- instanceof可在运行阶段动态判断引用指向的对象的类型
- instanceof的语法:
(引用 instanceof 类型)
- instanceof运算符的结果只能是true/false
- 若(c instanceof Cat)结果为true,则说明引用c指向的堆内存中的对象是Cat类型,反之则说明这个引用指向的对象不是Cat类型
由此之前的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
7、多态在开发中的作用
作用:
- 降低程序的耦合度
- 提高程序的扩展力
软件在扩展新需求的过程中,修改的越少越好,修改的越多,系统的稳定性就越差,未知风险就越多,还可能得重新测试。
软件开发的原则之一就是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类型的不行)
重写方法时,直接粘贴一份父类的代码过来,再修改,这样最干脆。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/146118.html