精选Java中的多态和继承

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精选Java中的多态和继承相关的知识,希望对你有一定的参考价值。

在优锐课架构学习中,了解了关于用多态方法调用将你的大脑包围在Java方法调用周围

根据传说中的Venkat Subramaniam,多态是面向对象编程中最重要的概念。 多态性(或对象根据其类型执行特殊操作的能力)使Java代码具有灵活性。 诸如 四个人帮 之类的设计模式(例如Command,Observer,Decorator,Strategy和许多其他模式)都使用某种形式的多态性。 精通此概念可极大地提高你思考解决编程难题的能力。

Juggy:
public abstract class JavaMascot {
    public abstract void executeAction();}public class Duke extends JavaMascot {
    @Override
    public void executeAction() {
        System.out.println("Punch!");
    }}public class Juggy extends JavaMascot {
    @Override
    public void executeAction() {
        System.out.println("Fly!");
    }}public class JavaMascotTest {
    public static void main(String... args) {
        JavaMascot dukeMascot = new Duke();
        JavaMascot juggyMascot = new Juggy();
        dukeMascot.executeAction();
        juggyMascot.executeAction();
    }}
 Punch!Fly!

多态性的接口和继承
借助此Java Challenger,我们将致力于多态性与继承之间的关系。 要记住的主要事情是多态性需要继承或接口实现。 你可以在下面的示例中看到这一点,其中包括Duke和
由于其特定的实现方式,Duke和Juggy的动作都将被执行。

方法是否重载了多态性?
许多程序员对多态与方法重写和方法重载之间的关系感到困惑。 实际上,只有方法重载才是真正的多态性。 重载使用相同的方法名称,但参数不同。 多态性是一个广义术语,因此始终会有关于该主题的讨论。

多态性的目的是什么?
使用多态的最大优点和目的是将客户端类与实现代码分离。 客户端类无需进行硬编码,而是接收实现以执行必要的操作。 这样,客户端类就足够了解执行其动作的知识,这是松耦合的示例。

为了更好地了解多态的目的,请看一下

public abstract class SweetProducer {
    public abstract void produceSweet();}public class CakeProducer extends SweetProducer {
    @Override
    public void produceSweet() {
        System.out.println("Cake produced");
    }}public class ChocolateProducer extends SweetProducer {
    @Override
    public void produceSweet() {
        System.out.println("Chocolate produced");
    }}public class CookieProducer extends SweetProducer {
    @Override
    public void produceSweet() {
        System.out.println("Cookie produced");
    }}public class SweetCreator {
    private List<SweetProducer> sweetProducer;
    public SweetCreator(List<SweetProducer> sweetProducer) {
        this.sweetProducer = sweetProducer;
    }
    public void createSweets() {
        sweetProducer.forEach(sweet -> sweet.produceSweet());
    }}public class SweetCreatorTest {
    public static void main(String... args) {
        SweetCreator sweetCreator = new SweetCreator(Arrays.asList(new CakeProducer(),
                new ChocolateProducer(), new CookieProducer()));
        sweetCreator.createSweets();
    }}

在此示例中,你可以看到SweetCreator类仅知道SweetProducer类。 它不知道每个甜食的实现。 这种分离使我们可以灵活地更新和重用我们的类,并使代码易于维护。 设计代码时,请始终寻找使代码尽可能灵活和可维护的方法。 多态性是用于这些目的的一种非常强大的技术。

提示:@Override注释使程序员有义务使用必须重写的相同方法签名。 如果未重写该方法,则将出现编译错误。
方法覆盖中的协变返回类型

如果它是协变类型,则可以更改覆盖方法的返回类型。 协变量类型基本上是返回类型的子类。 考虑一个例子:

public abstract class JavaMascot {
    abstract JavaMascot getMascot();}public class Duke extends JavaMascot {
    @Override
    Duke getMascot() {
        return new Duke();
    }}

由于Duke是JavaMascot,因此我们可以在覆盖时更改返回类型。

Java核心类的多态性
我们一直在核心Java类中使用多态。 一个非常简单的示例是当我们实例化ArrayList类时,将List接口声明为一种类型:

List<String> list = new ArrayList<>();

为了进一步讲解,请考虑使用Java Collections API且无多态性的以下代码示例:

public class ListActionWithoutPolymorphism {
   // Example without polymorphism
    void executeVectorActions(Vector<Object> vector) {/* Code repetition here*/}
    void executeArrayListActions(ArrayList<Object> arrayList) {/*Code repetition here*/}
    void executeLinkedListActions(LinkedList<Object> linkedList) {/* Code repetition here*/}
    void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList)
        { /* Code repetition here*/}}public class ListActionInvokerWithoutPolymorphism {
        listAction.executeVectorActions(new Vector<>());
        listAction.executeArrayListActions(new ArrayList<>());
        listAction.executeLinkedListActions(new LinkedList<>());
        listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList<>());}

丑陋的代码,不是吗? 想象一下要维护它! 现在来看具有多态性的相同示例

public static void main(String … polymorphism) {ListAction listAction = new ListAction();   
    listAction.executeListActions();}public class ListAction {
    void executeListActions(List<Object> list) {
        // Execute actions with different lists
    }}public class ListActionInvoker {
    public static void main(String... masterPolymorphism) {
        ListAction listAction = new ListAction();
        listAction.executeListActions(new Vector<>());
        listAction.executeListActions(new ArrayList<>());
        listAction.executeListActions(new LinkedList<>());
        listAction.executeListActions(new CopyOnWriteArrayList<>());
    }}

多态性的好处是灵活性和可扩展性。 除了创建几种不同的方法外,我们可以只声明一个接收通用List类型的方法。

在多态方法调用中调用特定方法
可以在多态调用中调用特定的方法,但是这样做会牺牲灵活性。 这是一个例子:

public abstract class MetalGearCharacter {
    abstract void useWeapon(String weapon);}public class BigBoss extends MetalGearCharacter {
    @Override
    void useWeapon(String weapon) {
        System.out.println("Big Boss is using a " + weapon);
    }
   void giveOrderToTheArmy(String orderMessage) {
        System.out.println(orderMessage);
    }}public class SolidSnake extends MetalGearCharacter {
    void useWeapon(String weapon) {
        System.out.println("Solid Snake is using a " + weapon);
    }}public class UseSpecificMethod {
    public static void executeActionWith(MetalGearCharacter metalGearCharacter) {
        metalGearCharacter.useWeapon("SOCOM");
    // The below line wouldn‘t work
        // metalGearCharacter.giveOrderToTheArmy("Attack!");
        if (metalGearCharacter instanceof BigBoss) {
            ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!");
        }
    }public static void main(String... specificPolymorphismInvocation) {
        executeActionWith(new SolidSnake());
        executeActionWith(new BigBoss());
    }}

我们在这里使用的技术是广播或在运行时故意更改对象类型。
请注意,只有在将通用类型转换为特定类型时才可以调用特定方法。 一个很好的类比是对编译器明确地说:“嘿,我知道我在这里做什么,所以我将对象转换为特定类型并使用特定方法。”

参考上面的示例,一个重要原因是编译器拒绝接受特定的方法调用:正在传递的类可能是SolidSnake。 在这种情况下,编译器无法确保MetalGearCharacter的每个子类都声明了giveOrderToTheArmy方法。

保留关键字的实例
请注意实例化的保留字。 在调用特定方法之前,我们询问了MetalGearCharacter是否为BigBoss的“实例”。 如果不是BigBoss实例,我们将收到以下异常消息:

线程“主”中的异常java.lang.ClassCastException:com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake无法转换为com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss

超级保留关键字
如果我们想引用Java超类的属性或方法怎么办? 在这种情况下,我们可以使用超级保留字。 例如:

public class JavaMascot {
  void executeAction() {
    System.out.println("The Java Mascot is about to execute an action!");
  }}public class Duke extends JavaMascot {
  @Override
  void executeAction() {
    super.executeAction();
    System.out.println("Duke is going to punch!");
  }
  public static void main(String... superReservedWord) {
    new Duke().executeAction();
  }}

在Duke的executeAction方法中使用保留字super调用超类方法。 然后我们从杜克(Duke)执行特定操作。 因此,我们可以在下面的输出中看到两条消息:

Java Mascot即将执行一个动作!Duke要出拳了!

接受多态挑战!
让我们尝试一下你对多态性和继承的了解。首先,请仔细分析以下代码:

public class PolymorphismChallenge {
    static abstract class Simpson {
        void talk() {
            System.out.println("Simpson!");
        }
        protected void prank(String prank) {
            System.out.println(prank);
        }
    }
    static class Bart extends Simpson {
        String prank;
        Bart(String prank) { this.prank = prank; }
        protected void talk() {
            System.out.println("Eat my shorts!");
        }
        protected void prank() {
            super.prank(prank);
            System.out.println("Knock Homer down");
        }
    }
    static class Lisa extends Simpson {
        void talk(String toMe) {
            System.out.println("I love Sax!");
        }
    }
    public static void main(String... doYourBest) {
        new Lisa().talk("Sax :)");
        Simpson simpson = new Bart("D‘oh");
        simpson.talk();
        Lisa lisa = new Lisa();
        lisa.talk();
        ((Bart) simpson).prank();
    }}

你怎么看? 最终输出将是什么? 不要使用IDE来解决这个问题! 关键是要提高代码分析技能,因此请尝试自己确定输出。

了解多态
对于以下方法调用:

new Lisa().talk("Sax :)");

输出为“ I love Sax!”,这是因为我们正在向该方法传递字符串,而Lisa具有该方法。
对于下一个调用:

Simpson simpson = new Bart("D‘oh");
simpson.talk();

输出将是“Eat my shorts!” 这是因为我们正在使用Bart实例化Simpson类型。
现在检查一下,这有点棘手:

Lisa lisa = new Lisa();
lisa.talk();

在这里,我们使用带有继承的方法重载。 我们没有将任何内容传递给talk方法,这就是调用Simpsontalk方法的原因。 在这种情况下,输出将是:

"Simpson!"

这里还有一个:

((Bart) simpson).prank();

在这种情况下,当我们使用new Bart(“ D‘oh”);实例化Bart类时,会传递恶作剧字符串。 在这种情况下,首先将调用super.prank方法,然后是来自Bart的特定恶作剧方法。 输出将是:

"D‘oh""Knock Homer down"

视频挑战! 调试Java多态性和继承

调试是完全吸收编程概念并改善代码的最简单方法之一。 在此视频中,你可以在调试和解释Java多态性挑战时继续学习:

多态性的常见错误
认为无需使用强制转换就可以调用特定方法是一个常见的错误。

另一个错误是不确定多态实例化类时将调用哪种方法。 请记住,要调用的方法是创建的实例的方法。
还要记住,方法重载不是方法重载。

如果参数不同,则无法覆盖方法。 如果返回类型是超类方法的子类,则可以更改重写方法的返回类型。

喜欢这篇文章的可以点个赞,欢迎大家留言评论,记得关注我,每天持续更新技术干货、职场趣事、海量面试资料等等
?> 如果你对java技术很感兴趣也可以交流学习,共同学习进步。?
不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代

文章写道这里,欢迎完善交流。最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。有更多JVM、mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java干货

关于多态性要记住什么
??创建的实例将确定使用多态时将调用哪种方法。
??@ Override注释使程序员有义务使用覆盖的方法; 否则,将出现编译器错误。
?多态可以与普通类,抽象类和接口一起使用。
??大多数设计模式取决于某种形式的多态性。
??在多态子类中使用特定方法的唯一方法是使用强制转换。
??可以使用多态性在代码中设计功能强大的结构。
??运行测试。 这样做,你将能够掌握这个强大的概念!

技术图片

以上是关于精选Java中的多态和继承的主要内容,如果未能解决你的问题,请参考以下文章

Java中的继承和多态

Java面向对象三大特征之继承和多态

Java面试java基础,2021年阿里Java面试题精选

Java中的继承封装多态的理解

java 代码片段

JAVA面对对象三大特征之继承多态