Java 教程
- Java - 首页
- Java - 概述
- Java - 历史
- Java - 特性
- Java 与 C++
- JVM - Java 虚拟机
- Java - JDK 与 JRE 与 JVM
- Java - Hello World 程序
- Java - 环境搭建
- Java - 基本语法
- Java - 变量类型
- Java - 数据类型
- Java - 类型转换
- Java - Unicode 系统
- Java - 基本运算符
- Java - 注释
- Java - 用户输入
- Java - 日期与时间
Java 控制语句
- Java - 循环控制
- Java - 决策制定
- Java - If-else
- Java - Switch
- Java - For 循环
- Java - For-Each 循环
- Java - While 循环
- Java - do-while 循环
- Java - Break
- Java - Continue
面向对象编程
- Java - OOPs 概念
- Java - 对象与类
- Java - 类属性
- Java - 类方法
- Java - 方法
- Java - 变量作用域
- Java - 构造函数
- Java - 访问修饰符
- Java - 继承
- Java - 聚合
- Java - 多态
- Java - 重写
- Java - 方法重载
- Java - 动态绑定
- Java - 静态绑定
- Java - 实例初始化块
- Java - 抽象
- Java - 封装
- Java - 接口
- Java - 包
- Java - 内部类
- Java - 静态类
- Java - 匿名类
- Java - 单例类
- Java - 包装类
- Java - 枚举
- Java - 枚举构造函数
- Java - 枚举字符串
Java 内置类
Java 文件处理
Java 错误与异常
- Java - 异常
- Java - try-catch 块
- Java - try-with-resources
- Java - 多重 catch 块
- Java - 嵌套 try 块
- Java - Finally 块
- Java - throw 异常
- Java - 异常传播
- Java - 内置异常
- Java - 自定义异常
Java 多线程
- Java - 多线程
- Java - 线程生命周期
- Java - 创建线程
- Java - 启动线程
- Java - 线程连接
- Java - 线程命名
- Java - 线程调度器
- Java - 线程池
- Java - 主线程
- Java - 线程优先级
- Java - 守护线程
- Java - 线程组
- Java - 关闭钩子
Java 同步
Java 网络
- Java - 网络
- Java - Socket 编程
- Java - URL 处理
- Java - URL 类
- Java - URLConnection 类
- Java - HttpURLConnection 类
- Java - Socket 类
- Java - 泛型
Java 集合
Java 接口
Java 数据结构
Java 集合算法
高级 Java
- Java - 命令行参数
- Java - Lambda 表达式
- Java - 发送邮件
- Java - Applet 基础
- Java - Javadoc 注释
- Java - 自动装箱和拆箱
- Java - 文件不匹配方法
- Java - REPL (JShell)
- Java - 多版本 Jar 文件
- Java - 私有接口方法
- Java - 内部类菱形运算符
- Java - 多分辨率图像 API
- Java - 集合工厂方法
- Java - 模块系统
- Java - Nashorn JavaScript
- Java - Optional 类
- Java - 方法引用
- Java - 函数式接口
- Java - 默认方法
- Java - Base64 编码解码
- Java - Switch 表达式
- Java - Teeing 收集器
- Java - 微基准测试
- Java - 文本块
- Java - 动态 CDS 档案
- Java - Z 垃圾收集器 (ZGC)
- Java - 空指针异常
- Java - 打包工具
- Java - 密封类
- Java - 记录类
- Java - 隐藏类
- Java - 模式匹配
- Java - 紧凑数字格式化
- Java - 垃圾回收
- Java - JIT 编译器
Java 杂项
- Java - 递归
- Java - 正则表达式
- Java - 序列化
- Java - 字符串
- Java - 进程 API 改进
- Java - 流 API 改进
- Java - 增强的 @Deprecated 注解
- Java - CompletableFuture API 改进
- Java - 流
- Java - 日期时间 Api
- Java 8 - 新特性
- Java 9 - 新特性
- Java 10 - 新特性
- Java 11 - 新特性
- Java 12 - 新特性
- Java 13 - 新特性
- Java 14 - 新特性
- Java 15 - 新特性
- Java 16 - 新特性
Java API 和框架
Java 类参考
- Java - Scanner
- Java - 数组
- Java - 字符串
- Java - Date
- Java - ArrayList
- Java - Vector
- Java - Stack
- Java - PriorityQueue
- Java - LinkedList
- Java - ArrayDeque
- Java - HashMap
- Java - LinkedHashMap
- Java - WeakHashMap
- Java - EnumMap
- Java - TreeMap
- Java - IdentityHashMap
- Java - HashSet
- Java - EnumSet
- Java - LinkedHashSet
- Java - TreeSet
- Java - BitSet
- Java - Dictionary
- Java - Hashtable
- Java - Properties
- Java - Collection
- Java - Array
Java 有用资源
Java - 多态
Java 中的多态
多态是指一个对象具有多种形态的能力。多态是 Java OOPs 概念 的重要特性,它允许我们使用单个方法名称(接口)执行多个操作。任何可以传递多个 IS-A 测试的 Java 对象都被认为是多态的。在 Java 中,所有 Java 对象 都是多态的,因为任何对象都会通过其自身类型和类 Object 的 IS-A 测试。
Java 中多态的用途
OOP 中多态最常见的用途是当父类引用用于引用子类对象时。
重要的是要知道,访问对象的唯一可能方法是通过 引用变量。引用变量只能有一种类型。一旦声明,引用变量的类型就不能更改。
只要引用变量没有声明为 final,它就可以重新分配给其他对象。引用变量的类型将决定它可以在对象上调用的方法。
引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型。引用变量可以声明为类或接口类型。
Java 多态示例
让我们来看一个例子。
public interface Vegetarian{} public class Animal{} public class Deer extends Animal implements Vegetarian{}
现在,Deer 类被认为是多态的,因为它具有多重继承。以下对上述示例成立:-
- 一只鹿 IS-A 动物
- 一只鹿 IS-A 素食动物
- 一只鹿 IS-A 鹿
- 一只鹿 IS-A 对象
当我们将引用变量事实应用于 Deer 对象引用时,以下声明是合法的:-
Deer d = new Deer(); Animal a = d; Vegetarian v = d; Object o = d;
所有引用变量 d、a、v、o 都引用堆中的同一个 Deer 对象。
Java 多态实现
在此示例中,我们通过创建 Deer 的对象并将同一个对象分配给超类或已实现接口的引用来展示上述概念。
interface Vegetarian{} class Animal{} public class Deer extends Animal implements Vegetarian{ public static void main(String[] args) { Deer d = new Deer(); Animal a = d; Vegetarian v = d; Object o = d; System.out.println(d instanceof Deer); System.out.println(a instanceof Deer); System.out.println(v instanceof Deer); System.out.println(o instanceof Deer); } }
输出
true true true true
Java 多态的类型
Java 中有两种类型的多态
- 编译时多态
- 运行时多态
Java 中的编译时多态
编译时多态也称为静态多态,它是通过 方法重载 实现的。
示例:编译时多态
此示例具有多个具有相同名称的方法来实现 Java 中编译时多态的概念。
// Java Example: Compile Time Polymorphism public class Main { // method to add two integers public int addition(int x, int y) { return x + y; } // method to add three integers public int addition(int x, int y, int z) { return x + y + z; } // method to add two doubles public double addition(double x, double y) { return x + y; } // Main method public static void main(String[] args) { // Creating an object of the Main method Main number = new Main(); // calling the overloaded methods int res1 = number.addition(444, 555); System.out.println("Addition of two integers: " + res1); int res2 = number.addition(333, 444, 555); System.out.println("Addition of three integers: " + res2); double res3 = number.addition(10.15, 20.22); System.out.println("Addition of two doubles: " + res3); } }
输出
Addition of two integers: 999 Addition of three integers: 1332 Addition of two doubles: 30.369999999999997
Java 中的运行时多态
运行时多态也称为动态方法调度,它是通过 方法覆盖 实现的。
示例:运行时多态
// Java Example: Run Time Polymorphism class Vehicle { public void displayInfo() { System.out.println("Some vehicles are there."); } } class Car extends Vehicle { // Method overriding @Override public void displayInfo() { System.out.println("I have a Car."); } } class Bike extends Vehicle { // Method overriding @Override public void displayInfo() { System.out.println("I have a Bike."); } } public class Main { public static void main(String[] args) { Vehicle v1 = new Car(); // Upcasting Vehicle v2 = new Bike(); // Upcasting // Calling the overridden displayInfo() method of Car class v1.displayInfo(); // Calling the overridden displayInfo() method of Bike class v2.displayInfo(); } }
输出
I have a Car. I have a Bike.
Java 中的虚拟方法和运行时多态
在本节中,我将向您展示 Java 中覆盖方法的行为如何让您在设计类时利用多态。
我们已经讨论了方法覆盖,其中子类可以覆盖其父类中的方法。覆盖的方法本质上隐藏在父类中,除非子类在覆盖方法中使用 super 关键字,否则不会调用该方法。
示例:使用虚拟方法实现运行时多态
/* File name : Employee.java */ public class Employee { private String name; private String address; private int number; public Employee(String name, String address, int number) { System.out.println("Constructing an Employee"); this.name = name; this.address = address; this.number = number; } public void mailCheck() { System.out.println("Mailing a check to " + this.name + " " + this.address); } public String toString() { return name + " " + address + " " + number; } public String getName() { return name; } public String getAddress() { return address; } public void setAddress(String newAddress) { address = newAddress; } public int getNumber() { return number; } }
现在假设我们扩展 Employee 类如下:-
/* File name : Salary.java */ public class Salary extends Employee { private double salary; // Annual salary public Salary(String name, String address, int number, double salary) { super(name, address, number); setSalary(salary); } public void mailCheck() { System.out.println("Within mailCheck of Salary class "); System.out.println("Mailing check to " + getName() + " with salary " + salary); } public double getSalary() { return salary; } public void setSalary(double newSalary) { if(newSalary >= 0.0) { salary = newSalary; } } public double computePay() { System.out.println("Computing salary pay for " + getName()); return salary/52; } }
现在,仔细研究以下程序并尝试确定其输出:-
/* File name : VirtualDemo.java */ public class VirtualDemo { public static void main(String [] args) { Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00); Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00); System.out.println("Call mailCheck using Salary reference --"); s.mailCheck(); System.out.println("\n Call mailCheck using Employee reference--"); e.mailCheck(); } } class Employee { private String name; private String address; private int number; public Employee(String name, String address, int number) { System.out.println("Constructing an Employee"); this.name = name; this.address = address; this.number = number; } public void mailCheck() { System.out.println("Mailing a check to " + this.name + " " + this.address); } public String toString() { return name + " " + address + " " + number; } public String getName() { return name; } public String getAddress() { return address; } public void setAddress(String newAddress) { address = newAddress; } public int getNumber() { return number; } } class Salary extends Employee { private double salary; // Annual salary public Salary(String name, String address, int number, double salary) { super(name, address, number); setSalary(salary); } public void mailCheck() { System.out.println("Within mailCheck of Salary class "); System.out.println("Mailing check to " + getName() + " with salary " + salary); } public double getSalary() { return salary; } public void setSalary(double newSalary) { if(newSalary >= 0.0) { salary = newSalary; } } public double computePay() { System.out.println("Computing salary pay for " + getName()); return salary/52; } }
输出
Constructing an Employee Constructing an Employee Call mailCheck using Salary reference -- Within mailCheck of Salary class Mailing check to Mohd Mohtashim with salary 3600.0 Call mailCheck using Employee reference-- Within mailCheck of Salary class Mailing check to John Adams with salary 2400.0
在这里,我们实例化两个 Salary 对象。一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
在调用 s.mailCheck() 时,编译器在编译时看到 Salary 类中的 mailCheck(),而 JVM 在运行时调用 Salary 类中的 mailCheck()。
e 上的 mailCheck() 非常不同,因为 e 是 Employee 引用。当编译器看到 e.mailCheck() 时,编译器会看到 Employee 类中的 mailCheck() 方法。
在这里,在编译时,编译器使用 Employee 中的 mailCheck() 来验证此语句。然而,在运行时,JVM 调用 Salary 类中的 mailCheck()。
此行为称为虚拟方法调用,这些方法称为虚拟方法。覆盖的方法在运行时被调用,无论在编译时的源代码中使用了什么数据类型的引用。