Java 多态

作者: Arvin Chen 分类: Java 来源: Break易站(www.breakyizhan.com)

多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:

Java多态的作用

消除类型直接的耦合关系,使代码更加清晰明了。

  • 1. 消除类型之间的耦合关系
  • 2. 可替换性
  • 3. 可扩充性
  • 4. 接口性
  • 5. 灵活性
  • 6. 简化性

Java多态的实现条件

要想实现Java的多态,必须要有下面三种条件:

1. 继承 (Java中的继承)

2. 重写 (在Java中的重写)

3. 向上转型 (Java的向上转型和向下转型)

Java多态的具体理解

简单的理解,就是用一个父类就能够引用不同的子类,这就是多态。 比如说我的一个Shape(父类),既可以是Triangle(子类),也可以是Square(子类),这样子的话,Shape呈现出了不同的形态,也是多态。

Java的多态理解

下面我们来看一个简单的例子:

Shape s = new Triangle();
s.draw();

上面的例子创建了一个指向子类的父类引用类型,调用的也是方法,会先调用父类Shape的方法,然后再是Triangle里面的方法。这是Java的一个“后期绑定”机制,只有在运行的时候,判断对象的类型,然后调用对应的方法。

创建一个指向子类的父类类型引用,该引用既可以处理父类Father对象,也可以处理子类Child对象,当相同的消息发送给子类或者父类对象时,该对象就会根据自己所属的引用而执行不同的行为,这就是多态。

Java多态的实现方式

多态的实现方式也有两种:基于继承实现和基于接口实现

基于继承实现的多态

基于继承的实现的多态主要表现在,父类可以对多个子类重写的方法,表现出来。我们来看一个例子:

public class Shape {
private String name;
 
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
 
public Shape(){
}
 
public String draw(){
return "画的是 " + getName();
}
 
/**
* 重写toString()
*/
public String toString(){
return null;
}
}
 
public class Circle extends Shape{
public Circle(){
setName("Circle");
}
 
/**
* 重写父类方法,实现多态
*/
public String draw(){
return "画的是 " + getName();
}
 
/**
* 重写toString()
*/
public String toString(){
return "Shape : " + getName();
}
}
 
public class Triangle extends Shape{
public Triangle(){
setName("Triangle");
}
 
/**
* 重写父类方法,实现多态
*/
public String draw(){
return "画的是 " + getName();
}
 
/**
* 重写toString()
*/
public String toString(){
return "Shape : " + getName();
}
}
 
public class Test {
public static void main(String[] args) {
//定义父类数组
Shape[] Shapes = new Shape[2];
//定义两个子类
Circle Circle = new Circle();
Triangle Triangle = new Triangle();
 
//父类引用子类对象
Shapes[0] = Circle;
Shapes[1] = Triangle;
 
for(int i = 0 ; i < 2 ; i++){
System.out.println(Shapes[i].toString() + "--" + Shapes[i].draw());
}
System.out.println("-------------------------------");
 
}
}
/*****www.breakyizhan.com******OUTPUT:
Shape : Circle--画的是 Circle
Shape : Triangle--画的是 Triangle
***/

基于接口实现的多态

基于接口实现的多态是通过实现接口并覆盖接口中同一方法的几不同的类体现的。我们可以来看一个例子:

interface Animal{ //接口的方法全部都是非静态的方法。
 
public void eat();
 
public void walk();
}
 
//接口的实现类
class cat implements Animal{
 
public void eat(){
System.out.println("猫在吃!!");
}
 
public void walk(){
System.out.println("猫在走!!");
}
 
}
 
//接口的实现类
class dog implements Animal{
 
public void eat(){
System.out.println("狗在吃!!");
}
 
public void walk(){
System.out.println("狗在走!!");
}
 
}
 
class Demo3
{
public static void main(String[] args)
{
//实现关系下的多态
Animal d = new dog(); //接口的引用类型变量指向了接口实现类的对象。
d.eat();
d.walk();
}
}
/*****www.breakyizhan.com******OUTPUT:
狗在吃!!
狗在走!!
***/

Java的多态:重写

在 Java 中,当设计类时,被重写的方法的行为怎样影响多态性。

我们已经讨论了方法的重写,也就是子类能够重写父类的方法。

当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。

要想调用父类中被重写的方法,则必须使用关键字 super

/* 文件名 : 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("Employee 构造函数");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public void mailCheck() {
      System.out.println("邮寄支票给: " + 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类:

/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; // 全年工资
   public Salary(String name, String address, int number, double salary) {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck() {
       System.out.println("Salary 类的 mailCheck 方法 ");
       System.out.println("邮寄支票给:" + getName()
       + " ,工资为:" + salary);
   }
   public double getSalary() {
       return salary;
   }
   public void setSalary(double newSalary) {
       if(newSalary >= 0.0) {
          salary = newSalary;
       }
   }
   public double computePay() {
      System.out.println("计算工资,付给:" + getName());
      return salary/52;
   }
}

现在我们仔细阅读下面的代码,尝试给出它的输出结果:

/* 文件名 : VirtualDemo.java */
public class VirtualDemo {
   public static void main(String [] args) {
      Salary s = new Salary("员工 A", "北京", 3, 3600.00);
      Employee e = new Salary("员工 B", "上海", 2, 2400.00);
      System.out.println("使用 Salary 的引用调用 mailCheck -- ");
      s.mailCheck();
      System.out.println("\n使用 Employee 的引用调用 mailCheck--");
      e.mailCheck();
    }
}

以上实例编译运行结果如下:

Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck -- 
Salary 类的 mailCheck 方法 
邮寄支票给:员工 A ,工资为:3600.0

使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法 
邮寄支票给:员工 B ,工资为:2400.0

例子解析

  • 实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
  • 当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。
  • 因为 e 是 Employee 的引用,所以调用 e 的 mailCheck() 方法时,编译器会去 Employee 类查找 mailCheck() 方法 。
  • 在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。

以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。

Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。

  •   本文标题:Java 多态 - Break易站
    转载请保留页面地址:https://www.breakyizhan.com/java/14761.html

    发表笔记

    电子邮件地址不会被公开。 必填项已用*标注

    更多阅读