面向对象 06:三大特性之——多态,多态的基本概念和相关使用,关键字 instanceof,以及对象间的类型转换

一、前言

记录时间 [2024-05-14]

系列文章简摘:
面向对象 02:区分面向过程与面向对象,类和对象的关系
面向对象 03:类与对象的创建、初始化和使用,通过 new 关键字调用构造方法,以及创建对象过程的内存分析
面向对象 04:三大特性之——封装,封装的含义和作用,以及在 Java 中的使用方式,附完整的测试案例代码
面向对象 05:三大特性之——继承,继承在 Java 中的相关使用,区分关键字 super 和 this,方法重写的注意点

更多 Java 相关文章,请参考专栏哦。

本文讲述面向对象编程的三大特性之——多态。通过案例分析,介绍了多态的基本概念、相关使用,以及优缺点等。此外,文章还讲述了关键字 instanceof 的使用方式,以及对象之间的类型转换。


面向对象编程(Object-Oriented Programming, OOP)的三大特性是封装、继承和多态,这三大特性是 OOP 的基础,为设计灵活、可维护和可扩展的软件系统提供了核心机制。

这三个特性共同构成了面向对象编程的基础,使开发者能够设计出高内聚、低耦合的软件系统,促进了软件工程的高效管理和复杂问题的有效解决。

  • 高内聚:一个模块内部各个组成部分(如类的方法、函数、变量等)之间应该紧密关联,共同完成一个具体且单一的功能,不允许外部干涉。
  • 低耦合:各模块之间的依赖关系应该尽可能地减少和简化,一个模块应尽量少地依赖其他模块,模块之间的接口应清晰、简单,且只暴露必要的功能给外部使用。

二、什么是多态

1. 基本概念

多态(Polymorphism)是面向对象编程的一个核心特性,即同一方法可以根据发送对象的不同而采用多种不同的行为方式

多态允许使用父类类型的引用来指向子类的对象,从而使得代码更具通用性、可扩展性和可维护性。

Java 实现多态主要依赖于以下几个关键概念:

  • 继承与多态
    • 多态的实现基于继承,即一个子类可以从父类继承方法,然后可以根据需要重写这些方法来改变其行为。
    • 当一个父类引用指向子类对象时,可以调用子类重写的方法,尽管引用的静态类型是父类,这就是多态的表现。
  • 方法重写(Override)
    • 子类可以重写(Override)父类的方法,提供自己特定的实现。这是实现多态的重要手段。
    • 重写的方法需要有相同的签名(方法名、返回类型以及参数列表)。
  • 动态绑定(Dynamic Binding / Late Binding)
    • 在 Java 中,方法的调用是在运行时绑定的,这意味着实际执行哪个版本的方法(父类的还是子类的)取决于实际对象的类型,而不是引用的类型。这一过程称为动态绑定或晚期绑定。
  • 抽象类与接口
    • 抽象类和接口为多态提供了另一种形式的实现。它们定义了方法的合同,具体的实现由子类完成,这增加了系统的灵活性和可扩展性。

2. 多态的优缺点

多态的优势:

  • 灵活性:可以使用统一的接口来操作各种类型的对象,减少代码耦合度。
  • 可扩展性:增加软件的可扩展性,易于添加新的子类而不影响现有的体系结构。
  • 简化性:提高代码的可读性和可维护性,特别是在处理大量对象时。

多态的限制:

  • 多态通常只适用于方法,不适用于属性。即使子类中重写了父类的属性,父类引用也只能访问到父类定义的属性。
  • 当使用父类引用调用一个方法时,只能访问到在父类中定义过的方法,不能访问子类特有的方法
  • 由于动态绑定,某些情况下可能会牺牲部分运行效率;过多使用向上转型可能使代码的意图不够明确。

3. 案例分析

在 Java 中,一个对象实际类型是确定的,但它可以指向的引用类型是不确定的,比如:父类的引用指向子类。

准备两个父子关系的类

举例说明,准备两个父子关系的类,其中,Animal 是父类,Dog 是子类。

在父类中写了一个方法 sound()

// Animal 父类
public class Animal {
    
    // 在父类中写了一个方法 sound(),然后将该方法在子类中重写
    public void sound() {
        System.out.println("Animal makes a sound");
    }
    
}

在子类中重写了从父类中继承的 sound() 方法,同时,写了一个子类中特有的方法 play()

// Dog 子类
public class Dog extends Animal {
    
    // 在子类中重写了从父类中继承的 sound() 方法
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }

    // 写了一个子类中特有的方法 play()
    public void play() {
        System.out.println("Dog plays");
    }
    
}

引用对象实例化

对于子类 Dog,它能调用的方法是自己的,或者从父类那里继承的。

父类引用可以指向子类,我们可以将父类 Animal 的引用指向子类 Dog,也可以将 Object 的引用指向子类 Dog

public class Application {

    public static void main(String[] args) {
        
        // 一个对象实际类型是确定的
//        new Dog();
//        new Animal();
        
        // 可以指向的引用类型是不确定的,比如:父类的引用指向子类
        
        // 子类 Dog 能调用的方法是自己的,或者从父类那里继承的
        Dog dog = new Dog();
        
        // 父类 Animal 可以指向子类
        Animal animal = new Dog();
        Object obj = new Dog();

    }

}

进行测试

测试一

子类重写了父类的方法,父类对象执行子类的方法。

// 子类重写了父类的方法,父类对象执行子类的方法

animal.sound();		// 调用子类方法 Dog barks
dog.sound();		// 调用子类方法 Dog barks

在这个例子中,Animal 是父类,Dog 是子类。尽管 animal 被声明为 Animal 类型,但它实际上指向的是 Dog 类的一个实例。因此,调用 sound() 方法时,执行的是 Dog 类中重写的方法,这就是多态性的体现。

测试二

父类可以指向子类,但不能调用子类独有的方法。

对象能调用哪些方法,主要看对象左边的类型,和右边关系不大。

// 父类可以指向子类,但不能调用子类独有的方法
// animal.play();
dog.play();

在这个例子中,Animal 是父类,Dog 是子类。尽管 animal 实际上指向的是 Dog 类的一个实例,但不能调用 Dog 类独有的方法 play(),该方法只有 Dog 类型的实例能调用。


4. 注意事项

以下是一些关于多态的注意事项:

注意事项:
    1. 多态是方法的多态,属性没有多态。
    2. 有联系的类才能进行类型转换(比如父类和子类),否则会抛出类型转换异常(ClassCastException)。
    3. 多态存在条件:继承关系,方法需要重写,父类引用指向子类  Father f1 = new Son();

不能被重写的方法:
    1. static 方法,属于类,不属于实例
    2. final 常量类型
    3. private 私有类型

补充:
    1. 子类重写了父类的方法,父类对象执行子类的方法。
    2. 父类可以指向子类,但不能调用子类独有的方法。

三、关键字 instanceof

1. 基本概念

instanceof 是 Java 中的一个关键字,用于判断一个对象是否属于某个类或其子类(或者实现了某个接口)

使用 instanceof 的一个典型场景是在需要确定对象的具体类型,以便进行特定类型的操作时。

但要,频繁使用 instanceof 可能意味着设计不够面向对象,因为良好的面向对象设计倾向于使用多态来处理不同类型的对象,而不是通过类型检查来分支逻辑。

其语法形式如下:

object instanceof ClassName

其中,object 是要检查的对象,ClassName 是类名或接口名。

如果 objectClassName 类的实例,或者是其子类的实例,或者是实现了 ClassName 接口的类的实例,那么 instanceof 表达式的结果就是 true;否则,结果是 false


2. 案例分析

准备父子类和实例对象

例如,有五个类:ObjectAnimalDogCatString

其中,ObjectAnimal 的父类,AnimalDogCat 的父类。StringObject 的子类。

父子关系:

Object > String
Object > Animal > Dog
Object > Animal > Cat

准备一些实例对象:

// 正常的实例化
Object object = new Object();
Animal animal = new Animal();
Dog dog = new Dog();

// 向上转型,父类引用指向子类实例,实际是子类的对象
Object object2 = new Dog();
Animal animal2 = new Dog();

使用 instanceof 判断

判断 object 对象与上述五个类之间的关系。

// Object > String
// Object > Animal > Dog
// Object > Animal > Cat

System.out.println("Object object = new Object();");

System.out.println(object instanceof Object);   // true
System.out.println(object instanceof Animal);   // false
System.out.println(object instanceof Dog);      // false
System.out.println(object instanceof Cat);      // false
System.out.println(object instanceof String);      // false

思考:通过 Object object = new Object(); 实例化 object 对象。

  • objectObject 类的对象,object instanceof Objecttrue
  • object 既不是其他四个类的对象,也不是四个类的子类的对象,故比较为 false

同理,判断 object2 对象与上述五个类之间的关系。

// Object > String
// Object > Animal > Dog
// Object > Animal > Cat

// 向上转型,父类引用指向子类实例,实际是子类的对象
Object object2 = new Dog();

System.out.println("Object object2 = new Dog();");

System.out.println(object2 instanceof Object);  // true
System.out.println(object2 instanceof Animal);  // true
System.out.println(object2 instanceof Dog);     // true
System.out.println(object2 instanceof Cat);     // false
System.out.println(object2 instanceof String);     // false

思考:通过 Object object2 = new Dog(); 实例化 object 对象。

  • object2Dog 类的对象,object2 instanceof Dogtrue
  • object2Object 类的子类的对象,object2 instanceof Objecttrue
  • object2Animal 类的子类的对象,object2 instanceof Animaltrue
  • object2 既不是 CatString 类的对象,也不是它们的子类的对象,故比较为 false

完整的 instanceof 比较

完整的 instanceof 比较如下:

public class Application {

    public static void main(String[] args) {
        
        // instanceof
        Object object = new Object();
        Animal animal = new Animal();
        Dog dog = new Dog();

        Object object2 = new Dog();
        Animal animal2 = new Dog();

        System.out.println("Object object = new Object();");

        System.out.println(object instanceof Object);   // true
        System.out.println(object instanceof Animal);   // false
        System.out.println(object instanceof Dog);      // false
        System.out.println(object instanceof Cat);      // false
        System.out.println(object instanceof String);      // false

        System.out.println("Animal animal = new Animal();");

        System.out.println(animal instanceof Object);   // true
        System.out.println(animal instanceof Animal);   // true
        System.out.println(animal instanceof Dog);      // false
        System.out.println(animal instanceof Cat);      // false
//        System.out.println(animal instanceof String);      // 编译时报错

        System.out.println("Dog dog = new Dog();");

        System.out.println(dog instanceof Object);      // true
        System.out.println(dog instanceof Animal);      // true
        System.out.println(dog instanceof Dog);         // true
//        System.out.println(dog instanceof Cat);     // 编译时报错
//        System.out.println(dog instanceof String);     // 编译时报错

        System.out.println("Object object2 = new Dog();");

        System.out.println(object2 instanceof Object);  // true
        System.out.println(object2 instanceof Animal);  // true
        System.out.println(object2 instanceof Dog);     // true
        System.out.println(object2 instanceof Cat);     // false
        System.out.println(object2 instanceof String);     // false

        System.out.println("Animal animal2 = new Dog();");
        System.out.println(animal2 instanceof Object);  // true
        System.out.println(animal2 instanceof Animal);  // true
        System.out.println(animal2 instanceof Dog);     // true
        System.out.println(animal2 instanceof Cat);     // false
//        System.out.println(animal2 instanceof String);     // 编译时报错

    }

}

四、类型转换

1. 基本概念

在 Java 中,有继承关系的对象之间的转换主要涉及向上转型(Upcasting)和向下转型(Downcasting)。

  • 向上转型(Upcasting):这是 Java 中最常见的对象转换形式,也是自动进行的,无需显式转换。将子类对象赋值给父类引用时,就发生了向上转型。向上转型是安全的,因为子类对象可以视为是父类类型的特殊实例。
  • 向下转型(Downcasting):这是将父类引用转换为子类类型的过程,必须显式进行。因为向下转型可能导致类型不匹配的问题(比如父类引用实际上并未指向子类对象),所以需要使用 (子类类型) 父类引用 的语法,并且最好在转换前使用 instanceof 检查,以避免 ClassCastException

尝试将一个对象转换为与之无继承关系的类型时,Java 会在编译时报错。如果使用强制类型转换,运行时则会抛出 ClassCastException 异常。


2. 案例分析

向上转型,例如,将子类对象赋值给父类引用。

// 子类对象赋值给父类引用
Father father = new Son();

向下转型,例如,将父类强制转换成子类。

// 将父类强制转换成子类
// 注意使用 instanceof 检查,保证转换安全性
((Son)father);

还是以上面的类为例,准备两个父子关系的类,其中,Animal 是父类,Dog 是子类。

  • 在父类 Animal 中写了一个方法 sound()
  • 在子类 Dog 中重写了从父类中继承的 sound() 方法,同时,写了一个子类中特有的方法 play()
public class Application {

    public static void main(String[] args) {

        // 对象类型之间的转换

        // 向上转型:子类对象赋值给父类引用
        // 自动完成
        Animal obj = new Dog();

        // 向下转型:将父类强制转换成子类
        // 这样就可以调用子类私有的方法了
        ((Dog)obj).play();
    }
    
}

五、总结

本文讲述面向对象编程的三大特性之——多态。通过案例分析,介绍了多态的基本概念、相关使用,以及优缺点等。此外,文章还讲述了关键字 instanceof 的使用方式,以及对象之间的类型转换


一些参考资料

狂神说 Java 零基础:https://www.bilibili.com/video/BV12J41137hu/
TIOBE 编程语言走势: https://www.tiobe.com/tiobe-index/
Typora 官网:https://www.typoraio.cn/
Oracle 官网:https://www.oracle.com/
Notepad++ 下载地址:https://notepad-plus.en.softonic.com/
IDEA 官网:https://www.jetbrains.com.cn/idea/
Java 开发手册:https://developer.aliyun.com/ebook/394
Java 8 帮助文档:https://docs.oracle.com/javase/8/docs/api/

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java中,对象类型的转换是指将一个对象从一种类型转换为另一种类型。这种转换可以分为两种类型:向上转型和向下转型。 向上转型是指将一个子类对象转换为父类对象。这种转换是隐式的,也就是说,不需要进行任何特殊的操作,因为子类对象可以自动转换为父类对象。 向下转型是指将一个父类对象转换为子类对象。这种转换是显式的,也就是说,需要进行特殊的操作,因为父类对象不能自动转换为子类对象。在进行向下转型时,需要使用强制类型转换符()。 需要注意的是,在进行向下转型时,如果父类对象不是子类对象的实例,那么就会抛出ClassCastException异常。因此,在进行向下转型时,需要先使用instanceof运算符进行类型检查,以确保转换的安全性。 ### 回答2: Java中继承和多态面向对象程序设计中的重要概念。继承是指一个类可以从另一个类中继承属性和方法,而多态是指一个对象可以表现出多种形态或多种操作。 对象类型的转换是指将对象从一种类型转换成另一种类型的操作。在Java中,对象类型的转换分为两种:向上转型和向下转型。 向上转型是指将子类对象转换成父类对象的操作,这种转换是自动的,无需显式地进行转换。例如,一个子类对象可以赋值给一个父类类型的变量。 向下转型是指将父类对象转换成子类对象的操作,这种转换需要显式地进行类型转换。在进行向下转型时,需要检查对象是否是所期望的子类类型,否则会抛出ClassCastException异常。 在Java中,对象类型的转换对于程序的灵活性和可扩展性有很大的作用。通过向上转型,可以让代码更具有通用性和扩展性,同时也可以提高代码的可读性和可维护性。而通过向下转型,可以充分利用子类的特性,更加灵活地进行编程。 总之,学习Java继承和多态以及对象类型的转换是非常重要的,可以帮助我们更好地理解面向对象编程的思想和实现,也可以提高我们的程序设计能力和开发效率。 ### 回答3: 在Java中,对象类型的转换是一种很常见的操作。这种转换通常会在继承和多态的情况下发生。在这里,我们将要探讨对象类型的转换以及它在Java继承和多态中的应用。 在Java中,所有的类都继承自Object类。这意味着所有的Java对象都是Object类型的,同时也都可以作为Object类型的参数传递。在Java中,对象类型的转换分为两种类型:向上转型和向下转型。向上转型是将一个子类对象转换为父类对象,而向下转型则是将父类对象转换为子类对象。 在Java中,向上转型很容易,因为子类对象可以随时转换为父类对象。例如,我们可以声明一个Person类并将一个Student对象赋值给它。这里,Student是Person的子类,所以我们可以将它转换为一个Person类型对象。 向下转型则需要进行强制类型转换。这种转换可以将父类对象转换为子类对象。例如,我们可以将一个Person对象强制转换为一个Student对象,但是在这之前我们需要进行对象类型的判断。如果Person对象本身就是一个Student对象,那么我们可以安全地将它转换为一个Student对象,否则就会出现类型转换异常。 Java对象类型的转换与继承和多态紧密相关。这种转换使得我们可以在程序中使用多态来调用不同的方法、属性和构造函数。例如,我们可以定义一个Animal类和一个Dog类。由于Dog类继承自Animal类,所以我们可以将一个Dog对象强制转换为一个Animal对象,并在程序中使用它们。这样,我们就可以在一个方法中调用Animal类或Dog类的方法,而不需要使用条件语句或多个方法。 Java对象类型的转换是一种很有用的操作,它使得我们可以使用多态来提高程序的灵活性和可扩展性。通过正确地使用向上转型和向下转型,我们可以保证程序的正确性和安全性。在继承和多态的情况下,对象类型的转换是一个非常重要的概念,我们必须充分理解并掌握它的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值