当前位置: 首页 > news >正文

JavaSE-8-多态

一、多态的概念

不同的对象对同一消息做出不同的响应。

多态指的是父类的引用变量可以指向其子类的对象,并且在运行时根据实际所指向的对象类型调用相应的方法,而不是在编译时就确定调用哪个方法。

关键点:为减少复杂代码的复杂圈度(if-else),使用多态

 多种类的直接做成列表遍历列表(向上转型+调用他们共同的父类的方法)

 这个打印结果是什么?func()0

构造父类前先要把父类构造完 父类的fun()会去执行字类的重写同名方法 此时num 还没初始化 所以num为0

 

多态主要通过以下两种方式实现:

1. 方法重写(Override)

子类可以重写父类中具有相同方法签名(方法名、参数列表和返回类型)的方法,从而在运行时根据实际对象的类型调用相应的方法。

2. 方法重载(Overload)

在同一个类中,可以定义多个具有相同方法名但参数列表不同的方法,编译器会根据调用时传递的参数类型和数量来决定调用哪个方法。不过,严格意义上方法重载是编译时多态,而方法重写是运行时多态。

1. 方法重写实现多态
// 父类
class Animal {public void makeSound() {System.out.println("Animal makes a sound.");}
}// 子类 Dog
class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks.");}
}// 子类 Cat
class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("Cat meows.");}
}public class PolymorphismExample {public static void main(String[] args) {// 父类引用指向子类对象Animal dog = new Dog();Animal cat = new Cat();// 调用相同的方法,根据实际对象类型执行不同的实现dog.makeSound(); cat.makeSound(); }
}

代码解释

  • Animal 是父类,定义了 makeSound 方法。
  • Dog 和 Cat 是 Animal 的子类,它们重写了 makeSound 方法。
  • 在 main 方法中,使用 Animal 类型的引用变量分别指向 Dog 和 Cat 对象,调用 makeSound 方法时,会根据实际对象的类型执行相应的方法。(重写实现的是 动态绑定在运行时根据实际所指向的对象类型调用相应的方法

2. 方法重载实现多态
class Calculator {public int add(int a, int b) {return a + b;}public double add(double a, double b) {return a + b;}public int add(int a, int b, int c) {return a + b + c;}
}public class OverloadExample {public static void main(String[] args) {Calculator calculator = new Calculator();System.out.println(calculator.add(1, 2)); System.out.println(calculator.add(1.5, 2.5)); System.out.println(calculator.add(1, 2, 3)); }
}

代码解释

  • Calculator 类中定义了多个 add 方法,它们具有相同的方法名但参数列表不同。
  • 在 main 方法中,根据传递的参数类型和数量,编译器会自动选择调用合适的 add 方法。(重载实现的是 静态绑定

二、动态绑定vs静态绑定

在面向对象编程中,重载实现的多态是静态绑定,重写实现的多态是动态绑定

重载与静态绑定

  • 重载的概念:在同一个类中,允许存在多个同名方法,但这些方法的参数列表(参数个数、参数类型或参数顺序)不同。当调用重载的方法时,编译器会根据调用时传入的实际参数来确定具体调用哪个方法。
  • 静态绑定的原理:静态绑定也称为早期绑定,在编译阶段,编译器就能够根据调用方法时提供的参数类型、个数等信息,确定要调用的具体是哪个重载方法。编译器会在编译期间生成相应的调用代码,将方法调用与具体的方法实现绑定起来,这种绑定是在程序运行前就确定好的,所以重载实现的多态是静态绑定。

重写与动态绑定

  • 重写的概念:子类中定义了与父类中方法签名(方法名、参数列表、返回类型)完全相同的方法,在子类对象调用该方法时,会执行子类中重写后的方法,而不是父类中的原始方法。
  • 动态绑定的原理:动态绑定也叫晚期绑定,在运行时,根据对象的实际类型来确定调用哪个类的重写方法。在编译阶段,编译器只知道对象的静态类型,但无法确定对象在运行时的实际类型,只有在程序运行到方法调用语句时,才会根据对象的实际类型来决定调用哪个类的重写方法,所以重写实现的多态是动态绑定。

静态类型是放在栈上的引用, 实际类型是放在堆上的对象

在许多面向对象编程语言(如 Java、C# 等)里,静态类型和实际类型是两个不同的概念,并且对象引用和对象实例在内存中的存储位置也有特定规律,下面为你详细解释静态类型、实际类型以及它们和内存存储位置(栈和堆)的关系。

静态类型和实际类型的概念

  • 静态类型:指的是在编译阶段就已经确定的变量类型。当你声明一个变量时,需要指定它的类型,这个类型就是静态类型。编译器依据静态类型来检查变量的使用是否合法。例如在 Java 里:
Animal animal = new Dog();

这里 animal 变量的静态类型是 Animal,这在编译阶段就已经明确了

  • 实际类型:指的是变量在运行时实际引用的对象类型。在上述例子中,animal 变量实际引用的是 Dog 对象,所以它的实际类型是 Dog。实际类型要在程序运行时才能确定。

静态类型、实际类型与栈、堆的关系

  • 静态类型变量的引用存储在栈上:当你声明一个变量时,它的静态类型决定了编译器如何处理这个变量。变量的引用(指向堆中对象的地址)会被存储在栈上。例如:
Animal animal = new Dog();

这里 animal 变量的引用存储在栈上,它指向堆中 Dog 对象的地址

  • 实际类型的对象存储在堆上:当使用 new 关键字创建对象时,无论静态类型是什么,实际创建的对象实例都会被分配到堆上。在上述例子中,Dog 对象的实际数据存储在堆上。

三、向上转型

向上转型是一种在面向对象编程中的概念,以下是关于它的详细介绍:

定义

向上转型是指将一个子类对象转换为父类类型的过程。在这个过程中,子类对象被当作父类对象来使用,它可以调用父类中定义的方法和属性,但对于子类特有的方法和属性,如果没有在父类中声明,就不能通过转型后的父类引用直接访问。

实现方式

在大多数面向对象编程语言中,向上转型通常是隐式进行的,不需要显式的类型转换操作。例如在 Java 中:

class Animal {public void eat() {System.out.println("Animal is eating");}
}class Dog extends Animal {public void bark() {System.out.println("Dog is barking");}
}public class Main {public static void main(String[] args) {Dog dog = new Dog();Animal animal = dog; // 向上转型,将Dog类型的对象赋值给Animal类型的变量animal.eat(); // 可以调用父类中的方法// animal.bark(); // 不能直接调用子类特有的方法}
}

作用

  • 提高代码的灵活性和可扩展性:向上转型使得代码可以以更通用的方式处理不同类型的对象,只需要关注对象的父类类型,而不需要为每个子类都编写重复的代码。比如在一个处理动物行为的系统中,无论具体是哪种动物(狗、猫等),都可以将它们向上转型为动物类型,统一进行一些基本的操作,如进食等。
  • 多态性的基础:向上转型是实现多态的重要基础。通过向上转型,不同子类的对象可以以父类的形式传递和使用,在调用方法时会根据对象的实际类型来决定执行哪个子类的重写方法,从而实现多态行为。

注意事项

  • 向上转型后,虽然可以调用父类的方法,但如果子类重写了父类的方法,那么实际执行的是子类重写后的方法,这是由多态性决定的。
  • 由于向上转型会丢失子类特有的信息,所以在需要使用子类特有的方法和属性时,可能需要进行向下转型,但向下转型需要谨慎使用,因为如果转型不当可能会导致运行时错误。

 

四、向上转型的三种情况

1. 赋值转型

赋值转型是将子类对象直接赋值给父类类型的变量,这是最常见的向上转型方式。通过这种方式,子类对象会被当作父类对象使用,调用方法时,如果子类重写了父类的方法,则会执行子类重写后的方法。

// 定义父类 Animal
class Animal {public void makeSound() {System.out.println("Animal makes a sound");}
}// 定义子类 Dog 继承自 Animal
class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks");}public void wagTail() {System.out.println("Dog wags its tail");}
}public class AssignmentUpcasting {public static void main(String[] args) {// 创建 Dog 类的对象Dog dog = new Dog();// 赋值转型:将 Dog 对象赋值给 Animal 类型的变量Animal animal = dog;// 调用 makeSound 方法,由于子类重写了该方法,所以执行子类的实现animal.makeSound(); // 以下代码会编译错误,因为 animal 是 Animal 类型,无法直接调用 Dog 类特有的方法// animal.wagTail(); }
}

2. 方法参数转型

方法参数转型是指在调用方法时,将子类对象作为参数传递给接收父类类型参数的方法。这样,方法可以处理多种子类对象,提高了代码的通用性。

// 定义父类 Shape
class Shape {public void draw() {System.out.println("Drawing a shape");}
}// 定义子类 Circle 继承自 Shape
class Circle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a circle");}
}// 定义子类 Rectangle 继承自 Shape
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a rectangle");}
}public class MethodParameterUpcasting {// 定义一个方法,接收 Shape 类型的参数public static void drawShape(Shape shape) {shape.draw();}public static void main(String[] args) {// 创建 Circle 类的对象Circle circle = new Circle();// 创建 Rectangle 类的对象Rectangle rectangle = new Rectangle();// 方法参数转型:将 Circle 对象作为 Shape 类型传递给 drawShape 方法drawShape(circle); // 方法参数转型:将 Rectangle 对象作为 Shape 类型传递给 drawShape 方法drawShape(rectangle); }
}

3. 函数返回值转型

函数返回值转型是指方法返回一个子类对象,但将其赋值给父类类型的变量或者作为返回值返回给期望接收父类类型的地方。

// 定义父类 Fruit
class Fruit {public void taste() {System.out.println("Fruit has a taste");}
}// 定义子类 Apple 继承自 Fruit
class Apple extends Fruit {@Overridepublic void taste() {System.out.println("Apple tastes sweet");}public void peel() {System.out.println("Peeling the apple");}
}public class ReturnValueUpcasting {// 定义一个方法,返回 Fruit 类型,实际返回的是 Apple 对象public static Fruit getFruit() {return new Apple();}public static void main(String[] args) {// 函数返回值转型:将返回的 Apple 对象当作 Fruit 类型接收Fruit fruit = getFruit();// 调用 taste 方法,执行 Apple 类重写后的方法fruit.taste(); // 以下代码会编译错误,因为 fruit 是 Fruit 类型,无法直接调用 Apple 类特有的方法// fruit.peel(); }
}

五、抽象类 

存在的意义是为了让你记得字类要重写父类的方法 相当于多了一遍的编译器校验,大大提高了开发的效率 。如果继承抽象类的字类依然是抽象类 那么继承该子类的类出来混迟早要还 它肩负着重写上面所有的方法。

http://www.dtcms.com/a/279136.html

相关文章:

  • python 双下划线开头函数
  • 【字节跳动】数据挖掘面试题0017:推荐算法:双塔模型,怎么把内容精准地推送给用户
  • ATE - Force模式和Meas模式
  • AI Agent vs SaaS:企业服务产品正迈向“智能中枢”阶段
  • Linux中使用云仓库上传镜像和私库制作Registry
  • 算法-练习题
  • 【牛客刷题】小红的数字删除
  • 可达性统计(拓扑排序模板,bitset)
  • 【算法】贪心算法:最大数C++
  • Linux 音频的基石: ALSA
  • 【云服务器安全相关】如何使用 `ping` 命令排查云服务器网络连接问题
  • javascript自执行函数
  • Retinex视网膜算法(SSR、MSR、MSRCR)
  • DNS重绑定
  • 纯净系统清理工具,告别卡顿烦恼
  • iOS高级开发工程师面试——RunTime
  • 构建完整工具链:GCC/G++ + Makefile + Git 自动化开发流程
  • 数字影像产业新机遇 入驻国际数字影像产业园享151项服务
  • 【人工智能】通过 Dify 构建智能助手
  • Day34 Java方法05 可变参数
  • JavaScript中Object.defineProperty的作用和用法以及和proxy的区别
  • 优化菜品呈现:让菜品自己 “开口” 求传播
  • 【ASP.NET Core】内存缓存(MemoryCache)原理、应用及常见问题解析
  • 我的Qt八股文笔记2:Qt并发编程方案对比与QPointer,智能指针方案
  • 电气安全监测系统:筑牢电气安全防线
  • DAOS系统架构-Container
  • 壹[1],异步与多线程
  • 美联储降息趋缓叠加能源需求下调,泰国证券交易所新一代交易系统架构方案——高合规、强韧性、本地化的跨境金融基础设施解决方案
  • 【Linux】Ubuntu22.04安装zabbix
  • 固态金属储氢实用化提速:新氢动力 20 公斤级系统重磅发布