day5--面向对象(继承、多态)
继承(inheritance):就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要 继承那一个类即可。
其中,多个类可以称为子类(subclass),单独那一个类称为父类、超类(superclass)或者基类。
继承描述的是事物之间的所属关系,这种关系是:is-a
的关系。例如,图中兔子属于食草动物,食草动物属于动 物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
好处
-
继承的出现减少了代码冗余,提高了代码的复用性。
-
继承的出现,更有利于功能的扩展。
-
继承的出现让类与类之间产生了关系,提供了多态的前提。
继承的格式
```java
class superclass { ... }
class subclass extends superclass { ... }
```
继承的特点
-
子类继承了父类,就继承了父类的方法和属性。
-
在子类中,可以使用父类中定义的方法和属性,也可以创建新的属性和方法。
-
在Java 中,继承的关键字用的是“extends”,即子类是对父类的“扩展”。
-
子类不能直接访问父类中私有的(private)的成员变量和方法。
-
Java只支持单继承和多层继承,不允许多重继承
-
一个子类只能有一个父类
-
一个父类可以派生出多个子类
-
方法重写
方法重写(override/overwrite):在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
重写语法规则
-
子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
-
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类
-
子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
-
子类不能重写父类中声明为private权限的方法
-
-
子类方法抛出的异常不能大于父类被重写方法的异常
-
声明为
static
的方法不能被重写,但是能够被再次声明
super关键字
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。如果子类父类中出现重名的成员变量,这时的访问是有影响的。子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super
关键字,修饰父类成员变量,类似于之前学过的this
。
使用规则
super可用于
-
访问父类中定义的属性
-
调用父类中定义的成员方法
-
在子类构造器中调用父类的构造方法
注意
-
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
-
super的追溯不仅限于直接父类
-
super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
调用父类的构造方法
-
子类中所有的构造器默认都会访问父类中空参数的构造方法
-
当父类中没有空参数的构造方法时,子类的构造方法必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造方法。同时,只能”二选一”,且必须放在构造器的首行
-
如果子类构造方法中既未显式调用父类或本类的构造方法,且父类中又没有无参的构造方法,则编译出错
public class Person { private String name; private int age; private Date birthDate; public Person(String name, int age, Date d) { this.name = name; this.age = age; this.birthDate = d; } public Person(String name, int age) { this(name, age, null); } public Person(String name, Date d) { this(name, 30, d); } public Person(String name) { this(name, 30); } } public class Student extends Person { private String school; public Student(String name, int age, String s) { super(name, age); school = s; } public Student(String name, String s) { super(name); school = s; } public Student(String s) { // 编译出错: no super(),系统将调用父类无参数的构造器。 this("ll", "s"); school = s; } }
子类对象的实例化过程
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空 间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。理解图解如下:
-
第1步:加载类,在这个类加载之前要加载所有父类。
-
第2步:在内存堆中分配对象空间。递归分配所有父类和子类属性空间。属性默认自动初始化。
-
第3步:属性的显示初始化赋值。
-
第4步:递归调用父类构造方法。(默认调用父类无参数构造器!)
-
第5步:调用本类构造方法。
抽象类
概述
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
抽象方法 :没有方法体的方法。
抽象类:包含抽象方法的类,不能实例化
abstract关键字
使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
如果一个类包含抽象方法,那么该类必须是抽象类。
示例代码
public abstract class Animal {
public abstract void run();
}
抽象类的使用
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父 类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
示例代码
public class Cat extends Animal {
public void run (){
System.out.println("小猫在墙头走~~~");
}
}
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
注意事项
-
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
-
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
-
-
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
-
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
-
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
-
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。
-
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有 意义。
-
final
关键字
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
-
final标记的类不能被继承。提高安全性,提高程序的可读性。
-
String类、System类、StringBuffer类
-
-
final标记的方法不能被子类重写。比如:Object类中的getClass()。
-
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
-
final标记的成员变量必须在声明时或在每个构造器中或代码块中显式初始化,然后才能使用。
-
final double MY_PI = 3.14;
-
多态
概述
是指同一行为,具有多个不同表现形式。
生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也 是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。
概念及语法
概念
多态性,是面向对象中最重要的概念,在Java中的体现:父类的引用指向子类的对象
-
可以直接应用在抽象类和接口上
-
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
-
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
-
多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
-
语法格式
父类类型 引用 = new 子类对象;
引用.方法名();
示例代码
public abstract class Animal {
public abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Animal a1 = new Cat();
// 调用的是 Cat 的
eat a1.eat();
// 多态形式,创建对象
Animal a2 = new Dog();
// 调用的是 Dog 的
eat a2.eat();
}
}
实现原理
1需要存在继承或者实现关系
2有方法的重写
虚拟方法调用(virtual method invocation)
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
多态情况下
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。这也叫做方法的动态绑定
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端