java多态

2023-05-16

多态

1,定义

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。

它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。

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

2,Java多态的作用

  1. 消除类型直接的耦合关系,使代码更加清晰明了。
  2. 消除类型之间的耦合关系
  3. 可替换性
  4. 可扩充性
  5. 接口性
  6. 灵活性
  7. 简化性

3,Java多态的具体理解

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

[(https://cdn.breakyizhan.com/wp-content/uploads/2019/05/JavaMind8_1-1.png)]

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

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

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

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

4.Java多态的实现条件

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

  1. 继承 (Java中的继承)

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

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

    1. 向上转型:子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。
    2. 向下转型:父类引用的对象转换为子类类型称为向下转型。

    通俗点来说,就是父类类型和子类类型的互相转换,但是子类是继承了父类的所有成员,而父类少了子类的一些成员(新建的一些成员),所以在向上转型的时候还要注意到这一点。

    实例操作:

    向上转型可能会发生的错误是父类用了子类里面有,而父类里面没有的方法

    public class Animal {
    public void eat(){
    System.out.println("animal is eatting...");
    }
    }
    class Fish extends Animal{
    public void eat(){
    System.out.println("Fish is eatting...");
    }
    public void swim(){
    System.out.println("Fish is swimming...");
    }
    }
    class Main{
    public static void main(String[] args) {
    Animal f=new Fish(); //向上转型
    f.eat();
    f.swim(); //此处提示在Animal中没有定义swim方法,所以运行会出错

    向下转型的话,如果父类引用的对象是父类本身,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。

    public class Animal {
    public void eat(){
    System.out.println("animal is eatting...");
    }
    }
    class Fish extends Animal{
    @Overwrite
    public void eat(){
    System.out.println("Fish is eatting...");
    }
    public void swim(){
    System.out.println("Fish is swimming...");
    }
    }
    class main{
    public static void main(String[] args) {
    Animal f=new Fish(); //向上转型
    f.eat();
    Fish f2=(Fish)f; //向下转型,编译和运行皆不会出错
    f2.eat();
    f2.swim();
    Animal a = new Animal();
    //Fish f3=(Fish)a; //这个向下转型,运行会出错的
    //f3.eat();
    //f3.swim();
    if(f3 instanceof Fish){
    Fish f3=(Fish)a; //这个向下转型,运行不会出错
    f3.eat();
    f3.swim();
    }
    }
    

    总结:

    1. 向上转型时,子类中新定义的成员方法会丢失。就像Animal访问不到swim的方法一样
    2. 子类引用不能指向父类对象。Fish c = (Fish)new Animal()这样是不行的。但是父类引用可以指向子类对象
    3. 向上转型的好处是减少重复代码,使代码变得简洁和提高系统扩展性,符合Java的面向对象的过程。
    4. 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)或者用instanceof,这样才会正确
    5. 向下转型只能转型为本类对象(鱼是不能变成鸟的)。

instanceof关键字可以判断对象是否是特定类的一个实例

instanceof是一个关键字,instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。

以下是显示instanceof不同行为的Java程序。

5.Java多态的实现方式

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

1.基于继承实现的多态

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

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
***/

2.基于接口实现的多态

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java多态 的相关文章

  • JPA 中的复合键

    我想创建一个具有自动生成的主键的实体 而且还有一个由其他两个字段组成的唯一复合键 我如何在 JPA 中执行此操作 我想这样做是因为主键应该用作另一个表中的外键 并且使其复合并不好 在下面的代码片段中 我需要命令和模型是唯一的 pk当然是主键
  • 存根方法时出现 InvalidUseOfMatchersException

    我有这个 TestNG 测试方法代码 InjectMocks private FilmeService filmeService new FilmeServiceImpl Mock private FilmeDAO filmeDao Bef
  • Spring Security 自定义过滤器

    我想自定义 Spring security 3 0 5 并将登录 URL 更改为 login 而不是 j spring security check 我需要做的是允许登录 目录并保护 admin report html 页面 首先 我使用教
  • 通过SOCKS代理连接Kafka

    我有一个在 AWS 上运行的 Kafka 集群 我想用标准连接到集群卡夫卡控制台消费者从我的应用程序服务器 应用程序服务器可以通过 SOCKS 代理访问互联网 无需身份验证 如何告诉 Kafka 客户端通过代理进行连接 我尝试了很多事情 包
  • 使用 GWT 读取非常大的本地 XML 文件

    我正在使用 GWT 构建我的第一个 Java 应用程序 它必须从一个非常大的 XML 文件中读取数据 当我尝试发送对文件中信息的请求时遇到问题 并且我不太确定它是否与文件的大小或我的语义有关 在我的程序中 我有以下内容 static fin
  • Java:在 eclipse 中导出到 .jar 文件

    我正在尝试将 Eclipse 中的程序导出到 jar 文件 在我的项目中 我添加了一些图片和 PDF s 当我导出到 jar 文件时 似乎只有main已编译并导出 我的意愿是如果可能的话将所有内容导出到 jar 文件 因为这样我想将其转换为
  • 不同类型的数组

    是否可以有一个包含两种不同类型数据的数组 我想要一个包含双精度型和字符串的数组 我尝试过 ArrayList
  • 通往楼梯顶部的可能路径

    这是一个非常经典的问题 我听说谷歌在他们的面试中使用过这个问题 问题 制定一个递归方法 打印从楼梯底部到楼梯顶部的所有可能的独特路径 有 n 个楼梯 您一次只能走 1 步或 2 步 示例输出 如果它是一个有 3 级楼梯的楼梯 1 1 1 2
  • 什么时候可以在 Java 中使用 Thead.stop() ?

    Thread stop 的 Java 文档听起来好像如果您调用 Thread stop 世界就会终结 已弃用 这种方法本质上是不安全的 停止线程 Thread stop 导致它解锁所有已锁定的监视器 作为未经检查的 ThreadDeath
  • Jackson XML ArrayList 输出具有两个包装器元素

    我在 Jackson 生成的 XML 输出中得到了两个包装器元素 我只想拥有一个 我有一个 Java bean Entity Table name CITIES JacksonXmlRootElement localName City pu
  • 如何使用 Hibernate (EntityManager) 或 JPA 调用 Oracle 函数或过程

    我有一个返回 sys refcursor 的 Oracle 函数 当我使用 Hibernate 调用该函数时 出现以下异常 Hibernate call my function org hibernate exception Generic
  • 如何检测 Java 字符串中的 unicode 字符?

    假设我有一个包含 的字符串 我如何找到所有这些 un icode 字符 我应该测试他们的代码吗 我该怎么做呢 例如 给定字符串 A X 我想将其转换为 AYXY 我想对其他 unicode 字符做同样的事情 并且我不想将它们存储在某种翻译映
  • 如何避免 ArrayIndexOutOfBoundsException 或 IndexOutOfBoundsException? [复制]

    这个问题在这里已经有答案了 如果你的问题是我得到了java lang ArrayIndexOutOfBoundsException在我的代码中 我不明白为什么会发生这种情况 这意味着什么以及如何避免它 这应该是最全面的典范 https me
  • 内部存储的安全性如何?

    我需要的 对于 Android 我需要永久保存数据 但也能够编辑 并且显然是读取 它 用户不应访问此数据 它可以包含诸如高分之类的内容 用户不得对其进行编辑 我的问题 我会 并且已经 使用过Internal Storage 但我不确定它实际
  • 流中的非终结符 forEach() ?

    有时 在处理 Java Stream 时 我发现自己需要一个非终端 forEach 来触发副作用但不终止处理 我怀疑我可以用 map item gt f item 之类的方法来做到这一点 其中方法 f 执行副作用并将项目返回到流中 但这似乎
  • Android:无法发送http post

    我一直在绞尽脑汁试图弄清楚如何在 Android 中发送 post 方法 这就是我的代码的样子 public class HomeActivity extends Activity implements OnClickListener pr
  • 使用按钮作为列表的渲染器

    我想使用一个更复杂的渲染器 其中包含列表的多个组件 更准确地说 类似于this https stackoverflow com questions 10840498 java swing 1 6 textinput like firefox
  • 泛型、数组和 ClassCastException

    我想这里一定发生了一些我不知道的微妙事情 考虑以下 public class Foo
  • Java EE 目录结构

    我对以下教程有疑问 http www mkyong com jsf2 jsf 2 internationalization example http www mkyong com jsf2 jsf 2 internationalizatio
  • Spring表单ModelAttribute字段验证避免400 Bad Request错误

    我有一个ArticleFormModel包含正常发送的数据html form由 Spring 使用注入 ModelAttribute注释 即 RequestMapping value edit method RequestMethod PO

随机推荐