回顾一些之前学过的知识:Java面向对象的特征是什么?
——封装、继承、多态
封装:set、get方法
继承:extends、implements关键字
在上一章内容中我们提到了继承,但当子类从父类中继承了各种属性和方法后。是否就一定要像父类一样表现为同一种状态呢?对于同一种方法,父类和子类一定会是同一种执行结果吗?
多态,从名字中我们可以获取到什么信息?多种状态、不一样的状态
目录
一、从继承到多态
1.现象引入
假设:K是一名大学生
什么是多态:K作为大学生中一个具体的对象,K可以被称为大学生,也可以同时被称为学生,两种身份。
即一个对象拥有两重身份,使用这两个身份有什么区别吗?
大学生作为K的身份明显更为具体,而学生则更为笼统(抽象)。
思考:物理中的物态变化是否和Java中的多态有相似之处?(“态”之间的变化)
2.什么是多态
用一句话来概述:同一方法在不同情况下具有不同的行为。
多态是面向对象编程中一个重要的概念,它允许不同类的对象对同一个消息作出不同的响应。
在Java中,多态性通过方法重写(override)和方法重载(overload)实现。
方法重写是运行时多态的体现,它发生在子类重写父类方法的情况下。
由于Java的动态绑定机制,在运行时会根据对象的实际类型调用相应的重写方法,这就是多态的表现。即使是通过父类引用指向子类对象,调用的方法仍然是子类实现的方法。
通过多态性,我们可以编写更灵活的代码,减少重复的代码量,提高代码的可扩展性和可维护性。多态性使得代码更具有通用性,能够适应不同类型的对象。
3.多态的作用
多态的作用之一:可以避免if else的判断(减少if else判断条件的使用)
多态可以实现匹配机制(相同的方法,不同的结果)
二、转型
转型是实现多态性的手段之一。
1.数据类型
数据类型 = 基本数据类型+引用数据类型(如类、接口、数组等等)
2.基本数据类型的转型
1)强制转型
例:一个浮点数 → 一个整数(有缺点:损失精度) |
Double d = 1.22; Int i = (int)d; |
2)自动转型
例:一个整数 → 一个浮点数 |
int i = 10; double j = i; |
注意:强制转型前需要(),而自动转型则不需要。
3)转型的推广
A a = new A; |
B b = a ; (什么情况下该式成立?) |
—— 假设1.0:A为学生类,B为大学生类
现在A创建了一个新的对象学生a,那么要将学生a的值赋给B的一个对象大学生b,是否成立且合理呢?
大学生b = 学生a;
理解:即学生是否一定是一个大学生?(按照顺序从右往左理解,赋值的规则)
—— 假设2.0:B为学生类,A为大学生类
学生a =大学生b ;
理解:即大学生是否一定是一个学生?
显然是假设2.0成立(引用数据类型中的自动转型),从而B为A的父类
可以类比数学中放缩的思想:大学生对象可以是大学生,也可以是学生
3.创建对象与内存地址
在转型的推广中,我们需要用到创建对象的相关知识,在这里我们进行一些回顾。
1)对象是如何被创建的
子类创建对象时,先在内存中开辟一片空间,存入,然后再将内存的地址返回给对象变量名去存储。
所以,对象的地址一般会存在子类类型对应的对象变量名中
A a = new A( );
a为对象变量名,对象又是存储在内存中具体的数据体
2)什么是内存地址
示例1:
package lzc20240415.多态;
public class Test {
public static void main(String[] args){
Test test = new Test();
System.out.println(test);
}
}
输出:
lzc20240415.多态.Test@7ba4f24f
@xxxxxx后面那一串可以暂且认为是地址
示例2:
package lzc20240415.多态;
public class Student {
}
class uniStudent extends Student{
}
class Main{
public static void main(String[] args){
uniStudent uniStu = new uniStudent();
System.out.println(uniStu);
Student stu = uniStu;
System.out.println(stu);
}
}
输出:
lzc20240415.多态.uniStudent@7699a589
lzc20240415.多态.uniStudent@7699a589
子类对象的引用地址也可以存储在父类的对象变量名中
3)代码阶段
编译期 |
1.0判断哪些变量可以调用哪些属性和方法(不会运行) 2.0查找类的代码结构,若类中有定义则可以通过编译 若无则无法通过(会变红报错) |
运行期 |
未转型前应是:Student stu = new Student( ); 只有一个uniStu对象(子类对象可以通过赋值给父类引用) 其中重写的方法会覆盖父类中同名的方法 ——K对外声称他是一名学生,但并不妨碍他是一名大学生,他可以具有阅读高等数学表现行为(可以调用方法,合理) |
三、代码示例
public class Student { //主类
String name;
String ID;
int age;
public void setName(String name){
this.name=name;
}
public void showInfo(){
System.out.println("name:"+name+"\nage:"+age+"\nID:"+ID);
}
public void Maths(){
System.out.println("学生————学习数学");
}
}
class uniStudent extends Student{
String habit;
@Override
public void showInfo(){
super.showInfo();//很奇妙的super关键字
System.out.println("habit:"+habit);
}
@Override
public void Maths(){
System.out.println("大学生————学习高等数学");
}
}
class midStudent extends Student{
@Override
public void Maths(){
System.out.println("中学生————学习初等数学");
}
}
class Learn{
public static void exam(Student stu){
stu.Maths();
}
}
class Main{
public static void main(String[] args){
uniStudent uniStu = new uniStudent();
uniStu.age=20;
uniStu.setName("三水点一灯");
uniStu.habit="金铲铲";
uniStu.ID="大学生";
uniStu.showInfo();
uniStu.Maths();
Learn.exam(uniStu);//思考该格式
System.out.println("——————————————————————");
Student stu = uniStu;//转型
stu.age=9;
stu.ID="学生";
stu.setName("三水气象台");
stu.showInfo();//转型后还是调用重写的方法
stu.Maths();
Learn.exam(stu);
System.out.println("——————————————————————");
midStudent midStu = new midStudent();
midStu.age=16;
midStu.setName("紫川秀");
midStu.ID="中学生";
midStu.showInfo();
midStu.Maths();
Learn.exam(midStu);
System.out.println("——————————————————————");
Student stu1 = midStu;
stu1.age=11;
stu1.ID="另一个学生";
stu1.setName("荒");
stu1.showInfo();
stu1.Maths();
Learn.exam(stu1);
}
}
思考:一般方法和静态方法的区别是什么?