继续深入------多态

多态是面向对象编程中的一个重要概念,它允许使用一个基类的引用来引用派生类的对象。在Java中,多态性通过继承和接口来实现。以下是关于Java中多态性的一些重要概念和注意事项的笔记:

一、继承和多态性

继承和多态性是面向对象编程中两个重要的概念,它们之间存在紧密的关系。以下是关于继承和多态性的一些重要概念:

  • 多态性建立在继承的基础上。一个子类可以继承一个父类,并且可以被视为父类的实例。
  • 多态性是指一个对象可以在运行时表现出多种形态。
  • 在Java中,多态性主要通过方法的重写(Override)和接口来实现。
  • 多态性使得一个对象可以被看作是其父类的类型,从而在运行时可以动态地调用实际子类的方法。
  • Java中的动态绑定实现了多态性,即方法的调用在运行时确定。

通过使用父类的引用指向子类的对象,可以实现多态性。例如:

scala 复制代码
// 父类
class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

// 子类
class Dog extends Animal {
    void makeSound() {
        System.out.println("Bark");
    }
}

// 多态性的应用
Animal myPet = new Dog();
myPet.makeSound(); // 输出 "Bark"

二、方法的重写

方法的重写是指在子类中重新定义(覆盖)其父类中已经存在的方法。被重写的方法在父类和子类中具有相同的方法签名(方法名、参数列表和返回类型)。重写允许子类提供对继承的方法的特定实现,以适应子类的需求。以下是关于方法的重写的一些重要概念和规则:

1、方法名

  • 重写的方法在子类中必须具有与父类中被重写的方法相同的方法名。
  • 方法名、参数列表和返回类型必须保持一致。

2、@Override 注解

  • 在Java中,使用 @Override 注解可以显式声明一个方法是重写父类的方法。

  • 使用这个注解有助于提高代码的可读性,并在编译时检查是否正确重写了父类的方法。

示例代码:

scala 复制代码
class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Bark");
    }
}

3、访问修饰符

  • 重写的方法不能降低访问权限。例如,如果父类的方法是 protected,则子类中的重写方法不能是 private
  • 重写的方法可以使用相同的访问权限或更高的访问权限。

4、异常处理

  • 重写的方法不能抛出比被重写方法声明的更多的异常,但可以抛出相同的异常或其子类。
  • 子类的重写方法也可以不声明任何异常,即使父类的方法声明了异常。

5、super 关键字的使用

  • 在子类中,可以使用 super 关键字调用父类的被重写方法,以访问父类的实现。

  • super 关键字通常用于区分子类和父类的成员变量或方法。

scala 复制代码
class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        super.makeSound(); // 调用父类的 makeSound 方法
        System.out.println("Bark");
    }
}

6、注意事项

  • 重写的目的是在子类中提供特定实现,而不是完全改变父类的方法。
  • 子类的重写方法应该具有与父类方法相同的语义。
  • 重写方法不能被标记为 finalstaticprivate

三、抽象类和接口

抽象类(Abstract Class)和接口(Interface)是Java中用于实现抽象类型的两种主要机制。它们都允许定义不完整的类,由子类提供具体实现。以下是有关抽象类和接口的一些重要概念:

1、抽象类(Abstract Class):

  1. 定义:

    • 抽象类是一种不能被实例化的类,用 abstract 关键字标识。
    • 抽象类可以包含抽象方法和具体方法。
    • 抽象方法是一种没有实现体的方法,它由子类实现。
csharp 复制代码
javaCopy code
abstract class Shape {
    abstract void draw(); // 抽象方法
    void display() {
        System.out.println("Displaying shape");
    }
}
  1. 特点:

    • 抽象类可以包含构造方法,但不能被实例化。
    • 子类继承抽象类,必须实现抽象类中的所有抽象方法,否则子类也必须声明为抽象类。
    • 一个类可以继承一个抽象类,并且可以实现多个接口。
  2. 使用场景:

    • 当类的一部分行为是通用的,但有一些方法需要在子类中实现时,可以使用抽象类。
    • 抽象类提供了一种中间层次,允许共享代码和定义约定。

2、接口(Interface):

  1. 定义:

    • 接口是一种完全抽象的类型,使用 interface 关键字定义。

    • 接口只包含抽象方法,不包含任何实现体。

    • 一个类可以实现多个接口。

csharp 复制代码
javaCopy code
interface Drawable {
    void draw(); // 抽象方法
}
  1. 特点:

    • 接口只包含常量和抽象方法,不能包含实例变量和实例方法。
    • 类实现接口时,必须提供接口中定义的所有方法的具体实现。
    • 接口支持多继承,一个类可以实现多个接口。
  2. 使用场景:

    • 当一个类需要实现多个不相关的行为时,可以使用接口。
    • 接口提供了一种机制,使得不同类可以实现相同的接口,从而实现了一种多态性。

3、抽象类 vs 接口:

构造方法:

  • 抽象类可以有构造方法,接口不能有构造方法。

实现:

  • 一个类只能继承一个抽象类,但可以实现多个接口。

字段:

  • 抽象类可以包含实例变量(字段),而接口只能包含常量。

默认实现:

  • 抽象类可以包含具体方法的实现,而接口只能包含抽象方法。

设计思想:

  • 抽象类用于表示 is-a 关系,即一个类是另一个类的特殊类型。
  • 接口用于表示 has-a 关系,即一个类具有某种行为。

示例代码:

csharp 复制代码
// 抽象类
abstract class Animal {
    abstract void makeSound();
}

// 接口
interface Eater {
    void eat();
}

// 类继承抽象类和实现接口
class Dog extends Animal implements Eater {
    @Override
    void makeSound() {
        System.out.println("Bark");
    }

    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
}

四、类型转换

向上转型(Upcasting):

向上转型是从子类类型转换为父类类型的过程,它是自动进行的,不需要显式地指定类型。向上转型是安全的,因为子类对象可以视为父类对象,而且不会丢失任何信息。

csharp 复制代码
class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Bark");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        Animal myAnimal = myDog; // 向上转型
        myAnimal.bark();// 可以调用子类方法
    }
}

在上述例子中,myDog 对象通过向上转型赋值给了 myAnimal,这样可以使用 myAnimal 引用调用 Dog 类中的方法。

向下转型(Downcasting):

向下转型是从父类类型转换为子类类型的过程,它需要显式指定类型,并且在运行时需要进行类型检查。向下转型是有风险的,因为不是所有的父类对象都是子类对象。

typescript 复制代码
class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Bark");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Dog();
        
        // 向下转型
        if (myAnimal instanceof Dog) {
            Dog myDog = (Dog) myAnimal;
            myDog.bark(); // 调用子类的方法
        }
    }
}

在上述例子中,myAnimal 是一个 Animal 类型的引用,但实际上它引用的是 Dog 类型的对象。在进行向下转型之前,通过 instanceof 运算符进行类型检查,确保安全地进行转型。

注意:如果在向下转型时,对象不是目标类型的实例,将会抛出 ClassCastException 异常。因此,在进行向下转型之前,最好先进行类型检查。

五、instanceof操作符

instanceof 是Java中的一个关键字,用于在运行时判断一个对象是否是某个类的实例或某个类的子类的实例。它的语法形式如下:

object instanceof Class

其中,object 是要检查的对象,Class 是要检查的类或接口。instanceof 返回一个布尔值,如果对象是指定类或其子类的实例,则结果为 true,否则为 false

示例代码:

scala 复制代码
class Animal {
}

class Dog extends Animal {
}

public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Dog();

        // 使用 instanceof 进行类型检查
        if (myAnimal instanceof Dog) {
            System.out.println("myAnimal is an instance of Dog");
        }

        if (myAnimal instanceof Animal) {
            System.out.println("myAnimal is an instance of Animal");
        }
    }
}

在上述示例中,myAnimalDog 类型的实例,所以第一个条件为真。同时,由于 DogAnimal 的子类,第二个条件也为真。

使用场景:

  • 类型检查:instanceof 主要用于在进行类型转换之前,检查对象是否是目标类型的实例,以避免 ClassCastException 异常。
ini 复制代码
if (myAnimal instanceof Dog) {
    Dog myDog = (Dog) myAnimal;
    myDog.bark(); // 安全地进行向下转型
}
  • 接口实现检查:可以使用 instanceof 来检查对象是否实现了特定的接口。
java 复制代码
if (myObject instanceof MyInterface) {
    // 对象实现了 MyInterface 接口
    // 执行相应的操作
}
  • 对象类型的动态判断:在运行时根据对象的实际类型执行不同的逻辑。
java 复制代码
if (myObject instanceof Dog) {
     // 对 Dog 类型的对象执行某些操作
} else if (myObject instanceof Cat) {
    // 对 Cat 类型的对象执行其他操作
}

instanceof 操作符在Java中是一个非常有用的工具,它帮助我们在运行时进行类型检查,以确保代码的安全性。

六、注意事项

  • 多态性在编写灵活且可维护的代码时非常有用,但要注意不要滥用。在某些情况下,过度使用多态性可能导致代码难以理解和维护。
  • 多态性主要用于处理对象的通用接口,而不是针对每个具体的实现。

以上是关于Java中多态性的一些基本概念和注意事项的简要笔记。多态性是面向对象编程中非常强大的特性,它提高了代码的灵活性和可扩展性。

相关推荐
天天扭码12 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶13 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺17 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序25 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜42 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、43 分钟前
Spring Boot 注解
java·spring boot
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康1 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql