JavaRush /Java 博客 /Random-ZH /Java 中的多态性

Java 中的多态性

已在 Random-ZH 群组中发布
关于 OOP 的问题是 IT 公司 Java 开发人员技术面试中不可或缺的一部分。在这篇文章中我们将讨论OOP的原则之一——多态性。我们将重点关注面试中经常被问到的方面,并提供一些小例子以方便理解。

什么是多态性?

多态性是程序能够以相同的接口相同地使用对象,而无需了解该对象的特定类型。如果您以这种方式回答什么是多态性这个问题,您很可能会被要求解释您的意思。再次,不要问一堆额外的问题,为面试官安排好一切。

Java 多态性面试 - 1
我们可以从以下事实开始:OOP 方法涉及基于类对象的交互来构建 Java 程序。是预先编写的图形(模板),程序中的对象将根据这些图形创建。此外,一个类总是有一个特定的类型,通过良好的编程风格,它可以通过名称“讲述”其用途。另外,需要注意的是,由于Java是强类型语言,程序代码在声明变量时总是需要指明对象的类型。除此之外,严格类型化可以提高代码安全性和程序可靠性,并允许您在编译阶段防止类型不兼容错误(例如,尝试将字符串除以数字)。当然,编译器必须“知道”声明的类型 - 这可以是 JDK 中的类,也可以是我们自己创建的类。请面试官注意,在使用程序代码时,我们不仅可以使用声明时分配的类型的对象,还可以使用其后代。这是很重要的一点:我们可以将多种类型视为一种类型(只要这些类型是从基类型派生的)。这也意味着,在声明了超类类型的变量后,我们可以将其后代之一的值赋给它。如果你举个例子,面试官会喜欢的。选择一组对象可以通用(基础)的某个对象,并从中继承几个类。基类:
public class Dancer {
    private String name;
    private int age;

    public Dancer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void dance() {
        System.out.println(toString() + "I dance like everyone else.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. " ;
    }
}
在后代中,重写基类方法:
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance() {
        System.out.println( toString() + "I dance electric boogie!");
    }
}

public class BreakDankDancer extends Dancer{

    public BreakDankDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance(){
        System.out.println(toString() + "I'm breakdancing!");
    }
}
Java 中的多态性和程序中对象的使用的一个示例:
public class Main {

    public static void main(String[] args) {
        Dancer dancer = new Dancer("Anton", 18);

        Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);// upcast to base type
        Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20); // upcast to base type

        List<Dancer> discotheque = Arrays.asList(dancer, breakDanceDancer, electricBoogieDancer);
        for (Dancer d : discotheque) {
            d.dance();// polymorphic method call
        }
    }
}
在方法代码中main显示 以下行中的内容:
Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20);
我们声明了一个超类类型变量,并将其后代之一的值赋给它。最有可能的是,您会被问到为什么编译器不会抱怨赋值符号左侧和右侧声明的类型之间的不匹配,因为 Java 具有严格的类型。解释一下向上类型转换在这里起作用——对对象的引用被解释为对基类的引用。此外,编译器在代码中遇到这种构造时,会自动且隐式地执行此操作。根据示例代码可以看出,赋值符号左侧声明的类类型具有Dancer在右侧声明的多种形式(类型)BreakDankDancerElectricBoogieDancer对于超类方法中定义的通用功能,每种形式都可以有自己独特的行为dance。也就是说,在超类中声明的方法可以在其后代中以不同的方式实现。在这种情况下,我们正在处理方法重写,这正是创建各种形式(行为)的原因。你可以通过运行main方法代码来执行: 程序输出 我是Anton,我今年18岁。我和其他人一样跳舞。我是阿列克谢,今年 19 岁。我跳霹雳舞!我是伊戈尔,今年 20 岁。我跳电动布吉舞! 如果我们不在后代中使用重写,那么我们将不会得到不同的行为。BreakDankDancer例如,如果我们ElectricBoogieDancer注释掉我们的类的方法dance,程序的输出将是这样的: 我是 Anton,我 18 岁。我和其他人一样跳舞。我是阿列克谢,今年 19 岁。我和其他人一样跳舞。我是伊戈尔,今年 20 岁。我和其他人一样跳舞。 这意味着创建新类根本没有意义 BreakDankDancerJava多态性的原理到底是什么?在程序中使用一个对象而不知道它的具体类型,它隐藏在哪里?在我们的示例中,这是对类型的对象的方法调用。Java多态性意味着程序不需要知道对象或对象将是什么类型。最主要的是,它是阶级的后裔。而如果我们谈论后代,需要注意的是,Java 中的继承不仅是,而且是。现在是时候记住 Java 不支持多重继承 - 每种类型都可以有一个父类(超类)和无限数量的后代(子类)。因此,接口用于向类添加多种功能。与继承相比,接口减少了对象与父对象的耦合,并且使用非常广泛。在Java中,接口是引用类型,因此程序可以将该类型声明为接口类型的变量。现在是举个例子的好时机。让我们创建界面: ElectricBoogieDancerd.dance()dDancerBreakDankDancerElectricBoogieDancerDancerextendsimplements
public interface Swim {
    void swim();
}
为了清楚起见,让我们采用不同且不相关的对象并在其中实现一个接口:
public class Human implements Swim {
    private String name;
    private int age;

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void swim() {
        System.out.println(toString()+"I swim with an inflatable ring.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. ";
    }

}

public class Fish implements Swim{
    private String name;

    public Fish(String name) {
        this.name = name;
    }

    @Override
    public void swim() {
        System.out.println("I'm a fish " + name + ". I swim by moving my fins.");

    }

public class UBoat implements Swim {

    private int speed;

    public UBoat(int speed) {
        this.speed = speed;
    }

    @Override
    public void swim() {
        System.out.println("The submarine is sailing, rotating the propellers, at a speed" + speed + " knots.");
    }
}
方法main
public class Main {

    public static void main(String[] args) {
        Swim human = new Human("Anton", 6);
        Swim fish = new Fish("whale");
        Swim boat = new UBoat(25);

        List<Swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
执行接口中定义的多态方法的结果使我们能够看到实现该接口的类型的行为差异。它们存在于方法执行的不同结果中swim。研究完我们的例子后,面试官可能会问为什么,当执行代码时main
for (Swim s : swimmers) {
            s.swim();
}
这些类中定义的方法是否为我们的对象调用?执行程序时如何选择所需的方法实现?为了回答这些问题,我们需要谈谈后期(动态)绑定。通过绑定,我们的意思是在方法调用与其在类中的特定实现之间建立连接。本质上,代码决定将执行类中定义的三个方法中的哪一个。Java 默认使用后期绑定(在运行时而不是在编译时,就像早期绑定的情况一样)。这意味着编译代码时
for (Swim s : swimmers) {
            s.swim();
}
编译器还不知道代码来自哪个类HumanFish也不知道Uboat它是否会在swim. 这只有在程序执行时才能确定,这要归功于动态分派机制——在程序执行期间检查对象的类型并为该类型选择所需的方法实现。如果你被问到这是如何实现的,你可以回答说,当加载和初始化对象时,JVM 在内存中构建表,并在表中将变量与其值关联起来,将对象与其方法关联起来。此外,如果一个对象被继承或实现一个接口,则首先检查其类中是否存在重写方法。如果有,则它们与此类型相关联,如果没有,则搜索在上一级类(在父级中)中定义的方法,依此类推,直至多级层次结构中的根。谈到 OOP 中的多态性及其在程序代码中的实现,我们注意到使用抽象描述和接口来定义基类是一种很好的做法。这种实践基于抽象的使用 - 隔离常见的行为和属性并将它们封装在抽象类中,或者仅隔离常见的行为 - 在这种情况下我们创建一个接口。构建和设计基于接口和类继承的对象层次结构是实现 OOP 多态性原则的先决条件。关于Java中的多态性和创新问题,我们可以提到,在创建抽象类和接口时,从Java 8开始,可以使用关键字在基类中编写抽象方法的默认实现default。例如:
public interface Swim {
    default void swim() {
        System.out.println("Just floating");
    }
}
有时他们可能会询问在基类中声明方法的要求,以便不违反多态性原则。这里一切都很简单:这些方法不应该是staticprivateFinalPrivate使该方法仅在类中可用,并且不能在后代中重写它。静态使方法成为类的属性,而不是对象的属性,因此超类方法将始终被调用。Final将使该方法不可变并且对其继承者隐藏。

多态性在Java中给我们带来了什么?

使用多态性给我们带来什么的问题很可能也会出现。在这里你可以简单地回答,而不需要太深入:
  1. 允许您替换对象实现。这就是测试的基础。
  2. 提供程序可扩展性 - 为未来创建基础变得更加容易。基于现有类型添加新类型是扩展 OOP 风格编写的程序功能的最常见方法。
  3. 允许您将具有常见类型或行为的对象组合到一个集合或数组中,并统一操作它们(如我们的示例中,让每个人跳舞 - 一种方法dance或游泳 - 一种方法swim)。
  4. 创建新类型时的灵活性:您可以选择从父级实现方法或在子级中覆盖它。

旅途的临别赠言

多态性原理是一个非常重要且广泛的话题。它涵盖了Java 的 OOP的近一半以及该语言基础知识的很大一部分。你不可能在面试中定义这个原则。对此的无知或误解很可能会导致面试结束。因此,在考试前不要偷懒检查自己的知识,必要时刷新一下。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION