Java——面向对象三大特性3(多态)

简介: Java——面向对象三大特性3(多态)

多态是面向对象三大特性中,最为重要也是最为灵活的一个特性。


class Animal{
    public void eat(){
        System.out.println("Animal eat");
    }
    public void sleep(){
        System.out.println("sleep 8 hours");
    }
}
class Dog extends Animal{
    public void sleep(){
        System.out.println("sleep 6 hours");
    }
    public void lookAfterHouse(){
        System.out.println("Dog can look after house");
    }
}
class Cat extends Animal{
    public void catchMouse(){
        System.out.println("Cat can catch mouse");
    }
}


3.1 引用类型和对象类型

01 : Animal a ;
02: a = new Animal();



       第一行代码中,我们定义了一个引用 a,而约束这个引用的类型为 Animal。我们知道,Java 语言是一个强类型的语言,在定义变量的时候,必须指定变量的类型。变量类型和变量中存放的数据类型必须匹配。这样,a 引用中只能存放 Animal 类型的对象。在这里,我们称“a 引用的引用类型为 Animal”。


        第二行代码中,我们创建了一个 Animal 类型的对象,将这个对象的地址赋给 a 引用。每当我们创建对象时,总要指定这个对象的类型。对象的类型我们会写在“new”关键字的后面。在这里,我们称“a 引用所指向的对象类型为 Animal”。



        也就是说,我们在定义引用的时候,为引用指定“引用类型”。而将对象放入引用的时候,引用中的对象还有一个“对象类型”。



以我们目前的知识程度,我们可以认为:引用类型和对象类型必须是一致的


Animal d = new Dog();

因此我们可以得出结论:子类的对象可以放入父类的引用中!也就是说,一个引用的引用类型和对象类型未必完全一致,对象类型可以是引用类型的子类。当我们看到一个引用时,一方面要考察这个引用的引用类型,另一方面还要考察这个引用中所存储的对象的类型,这两个类型可能是不同的。


3.2 多态的语法特性


1. 对象类型永远不变


一个对象在创建的时候,其对象类型就已经决定了。直到这个对象消亡,它的对象类型永远不会改变。在多态的语法中,一个对象可能存放在不同类型的引用中,但是请注意,对象自身的类型从来也不会发生变化。例如在小强的眼中,这个狗对象被当作了一个动物对象,但这个对象实际上还是一只狗,这是不会变化的。这一点很好理解,一个对象,它是狗就是狗,是猫就是猫,如果你认为它是狗它就是狗,你认为它是猫它就是猫,这显然是不合理,不“唯物”的。

public class TestPolymorphism {
    public static void main(String[] args) {
        Animal a = new Dog(); //将一个狗对象看作是一个动物对象
        a.eat(); // 正确
        a.sleep(); // 正确
        a.lookAfterHouse(); // 编译错误
    }
}

      当小强把 Dog 对象放入 Animal 类型的引用时,只能对这个引用调用 eat 方法,sleep 方法,而当他试图调用 lookAfterHouse 方


法时,将会得到一个编译错误。因为他把狗对象看作是一个动物对象,他并不了解这个对象还具有 lookAfterHouse 方法。也就是说,当我们对一个引用调用方法时,只能调用这个引用的引用类型中定义的方法。例如在上述代码中,a 引用的引用类型为Animal,而 Animal 类中定义了 eat 方法和 sleep 方法,因此我们可以对 a 引用调用这两个方法。而由于 Animal 类中没有定义 lookAfterHouse方法,我们就无法对 a 引用调用这个方法。


      通过代码,我们看到:Animal 类中定义了 sleep 方法,打印“sleep 8 hours”。而 Dog 类作为 Animal 的子类,在 sleep 方法的实现方式上有自己独特的实现,因此,Dog 类覆盖了Animal 类中的 sleep 方法,打印“sleep 6 hours”。


从中我们可以得到结论:引用类型和对象类型之间如果存在方法的覆盖,那么在程序运行的时候,JVM 会根据引用中所存储的对象类型,去调用对象类型中覆盖之后的方法。例如:Animal 类型的 a 引用中存放的是 Dog 对象,而 Dog 类覆盖了 Animal 类中的 sleep 方法,则 JVM 会调用 Dog 类中覆盖之后的 sleep 方法,而不是 Animal 类中的 sleep 方法。


3.3 强制类型转换和 instanceof

Animal a = new Dog();
Dog d = (Dog)a ;
d.lookAfterHouse();

强类型转换:


a 引用中存放的就是一个 Dog 类的对象,我们“强制性的要求”系统把 a 引用中的对象,作为 Dog 对象存放在 d 引用中。


    由于我们表明了这样一种“强硬”的态度,编译器在编译的时候,就不会对这句话报出任何错误了。但是请注意,这并不意味着这句话一定是对的。因为 a 引用中依然可能存放Cat 对象 ,一旦 这种 情况发 生,虽 然编译 通过, 但是运 行时,JVM 会抛 出一个ClassCastException,类型转换异常。

public class TestPolymorphism {
    public static void main(String[] args) {
        Animal a = new Cat();
        Dog d = (Dog) a;
        d.lookAfterHouse();
    }
}

因此我们可以得出结论:


1. 子类的引用可以直接赋值给父类引用。(这是多态的基本用法)


2. 父类的引用赋值给子类引用,必须强制类型转换,并有可能在运行时得到一个类型转换异常。



那么,如何避免类型转换异常的发生呢?下面我们介绍 Java 中的一个关键字:instanceof。


instanceof 的基本语法如下:


引用 instanceof 类名

public class TestInstanceof {
    public static void main(String[] args) {
        Animal a =new Cat();
        System.out.println(a instanceof Cat);//true
        System.out.println(a instanceof Dog);//false
        System.out.println(a instanceof Animal);//true
    }
}

3.4 多态的作用


多态最主要的作用在于:我们可以将若干不同子类的对象都当做统一的父类对象来使用,这样就会提高程序的通用性,屏蔽不同子类之间的差异。


相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
78 2
|
17天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
26天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
44 6
|
23天前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
22 1
|
1月前
|
JavaScript 前端开发 Java
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。如果你从我的文章中受益,欢迎关注我,我将持续更新更多优质内容。你的支持是我前进的动力!🎉🎉🎉
24 0
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
|
1月前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
35 4
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
38 2
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
101 3
|
2月前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
31 2
|
6月前
|
Java 开发者
Java 面向对象新视界:揭秘子类如何“继承”父类精华,再添“创新”之笔
【6月更文挑战第16天】在Java的面向对象世界,子类继承父类的特性,如`Circle`继承`Shape`,展示“is-a”关系。子类不仅保留父类的`color`和`display`方法,还添加了`radius`属性及定制的显示逻辑。这种继承与创新允许代码复用,增强灵活性和可扩展性,使得构建复杂系统变得更加高效和模块化。通过持续的继承与定制,开发者能构建出一系列独具特色的类,充分展现面向对象编程的力量。
157 57