问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

面向对象编程 (OOP):深入理解继承、多态和抽象

创作时间:
作者:
@小白创作中心

面向对象编程 (OOP):深入理解继承、多态和抽象

引用
CSDN
1.
https://blog.csdn.net/weidl001/article/details/139340463

面向对象编程(OOP)是一种强大的编程范式,它通过将程序组织成对象的集合来简化软件设计和开发。本文将深入探讨OOP的核心概念,重点讲解继承、多态、抽象类和接口,以及它们在Java中的应用。

1. 简介

面向对象编程(OOP)是一种强大的编程范式,它通过将程序组织成对象的集合来简化软件设计和开发。与传统的程序设计方法相比,OOP提供了一种更自然、更易于理解和维护的方式来构建复杂的软件系统。OOP的核心概念包括:对象、类、继承、多态、封装。本文将深入探讨这些概念,重点讲解继承、多态、抽象类和接口,以及它们在Java中的应用。

2. 栈空间和堆空间

在深入了解OOP之前,我们需要先了解Java中的内存管理机制,尤其是栈空间和堆空间。

2.1 栈空间(Stack)

  • 定义和作用: 栈空间用于存储局部变量、方法参数和函数调用信息。它是Java程序运行时最基本的内存区域之一。
  • LIFO 原理: 栈空间遵循先进后出(LIFO)的原则,就像一个叠放盘子的架子,最后放进去的盘子最先被拿出来。
  • 存储数据类型: 栈空间主要存储以下数据类型:
  • 局部变量:定义在方法内部的变量,例如
    int age = 25;
    
  • 方法参数:传递给方法的变量,例如
    void calculateSum(int a, int b) { ... }
    
    中的ab
  • 函数调用信息:包括方法调用时的局部变量地址、返回地址等。
  • 特点:
  • 栈空间大小通常较小,而且速度较快。
  • 栈空间的分配和回收由Java虚拟机(JVM)自动管理,程序员不需要手动进行操作。

2.2 堆空间(Heap)

  • 定义和作用: 堆空间用于存储对象和数组。它是Java程序中用于动态内存分配的主要区域。
  • 特点:
  • 堆空间的空间大小通常比栈空间大得多。
  • 堆空间的分配和回收由垃圾回收器(Garbage Collector)自动管理,程序员一般不需要手动进行操作。
  • 堆空间通常比栈空间速度慢。

2.3 栈空间和堆空间的交互

当创建一个对象时,会发生以下步骤:

  1. 栈空间: 分配一个引用变量,该变量指向堆空间中对象的地址。例如:
    Car myCar = new Car();
    
    中的myCar
  2. 堆空间: 为新创建的对象分配一块内存块,其中包含对象的属性和方法。
  3. 连接: 引用变量指向堆空间中分配的内存块,这样就可以通过引用变量访问对象。
// 1. 栈空间:
// 创建一个 Car 类对象,并分配了一个引用变量 myCar。
Car myCar = new Car(); 
// 2. 堆空间:
// 将新创建的 Car 对象的属性和方法存储在堆空间中。
// 这里假设 Car 有两个属性:color 和 model。
// myCar 的值指向堆空间中对象的地址。
// 此时 myCar 就是一个指向堆空间对象的引用。
//         堆空间
// +-----+-----+---------+     // 
// | color| model|  ...   |     //
// +-----+-----+---------+
//       ^
//       |
//       myCar(栈空间)

3. 面向对象编程的基础

3.1 对象和类

  • 对象: 现实世界中事物的抽象表示。在编程中,对象是数据(属性)和操作数据的方法(行为)的封装。例如,一个"汽车"对象可以包含属性(例如颜色、品牌、型号、速度)和方法(例如启动、加速、刹车)。
  • 类: 创建对象的模板或蓝图。它定义了对象的属性和方法。例如,"汽车"类可以定义所有汽车共有的属性和方法,然后通过这个类创建多个不同的汽车对象。

用Java代码表示:

class Car {
    String color;
    String brand;
    String model;
    int speed;
    void start() {
        System.out.println("汽车启动");
    }
    void accelerate() {
        System.out.println("汽车加速");
    }
    void brake() {
        System.out.println("汽车刹车");
    }
}
public class Main {
    public static void main(String[] args) {
        Car myCar = new Car();
        myCar.color = "红色";
        myCar.brand = "宝马";
        myCar.model = "3系";
        myCar.start();
        myCar.accelerate();
    }
}

3.2 封装

  • 概念: 将对象的属性和方法结合起来,并隐藏对象的内部实现细节,只暴露接口供外部访问。
  • 目的:
  • 提高代码安全性:防止外部代码直接修改对象的私有属性。
  • 提升代码可维护性:修改对象的内部实现细节不会影响外部代码的使用。
  • 增强代码可重用性:可以根据需要创建不同的对象,而无需关注其内部实现细节。

访问修饰符:

修饰符
描述
访问范围
public
任何地方都可以访问
整个程序
private
只有类内部可以访问
类内部
protected
继承的类和同一个包内的类可以访问
继承类和同一个包
default(无修饰符)
只有同一个包内的类可以访问
同一个包

用Java代码示例:

class Person {
    private String name; // 私有属性,只能在 Person 类内部访问
    private int age; // 私有属性,只能在 Person 类内部访问
    public String getName() { // 公共方法,可以在任何地方访问
        return name;
    }
    public void setName(String name) { // 公共方法,可以在任何地方访问
        this.name = name;
    }
    public int getAge() { // 公共方法,可以在任何地方访问
        return age; 
    }
    public void setAge(int age) { // 公共方法,可以在任何地方访问
        this.age = age;
    }
}

4. 继承

  • 概念: 继承是OOP中一个重要的特性,它允许一个类继承另一个类的属性和方法。继承建立了"is-a"关系,子类拥有父类的所有属性和方法,并且可以添加自己的属性和方法。
  • 目的:
  • 代码复用:避免重复编写相同的功能代码。
  • 可扩展性:通过继承,可以在父类的基础上创建新的子类,实现新的功能。
  • 代码组织:将相关的功能组织到不同的层次结构中,使代码更易于理解和维护。

4.1 继承的概念

  • 父类(基类或超类): 被继承的类。
  • 子类(派生类或扩展类): 继承自另一个类的类。
class Animal {
    void eat() {
        System.out.println("动物在吃");
    }
}
class Dog extends Animal { // Dog 继承了 Animal 类
    void bark() {
        System.out.println("狗在叫");
    }
}

4.2 继承的优势

  • 代码复用: 继承允许子类复用父类的代码,避免重复编写相同的功能。例如,在Dog类中,eat方法不需要重新编写,可以直接继承自父类Animal
  • 可扩展性: 继承使得添加新的功能变得更容易。例如,我们可以通过继承Animal类,创建新的子类,比如Cat类,来添加猫的相关功能,例如meow()方法。
  • 代码组织: 继承可以帮助我们更好地组织代码,将相关的类组织到不同的层次结构中。例如,我们可以创建一个Pet类作为父类,然后创建DogCat等子类,这样可以使代码更加清晰、易于维护。

4.3 继承的类型

  • 单继承: 在Java中,一个子类只能继承一个父类。
  • 多继承: 一个子类可以继承多个父类。Java不支持真正的多继承,但可以使用接口来实现类似功能。
  • 层次化继承: 父类可以有子类,子类可以有孙类,形成层次结构。

4.4 继承中的方法重写(Overriding)

  • 概念: 子类可以选择重写父类的方法,以便在子类中提供不同的实现。
  • 条件:
  • 方法名相同。
  • 参数列表相同。
  • 返回值类型相同(或者返回值类型是父类返回值类型的子类)。
  • 访问修饰符的权限不能比父类更严格。
class Animal {
    void sound() {
        System.out.println("动物发出声音");
    }
}
class Dog extends Animal {
    @Override
    void sound() {  // 重写父类的 sound 方法
        System.out.println("狗叫");
    }
}

4.5 继承中的构造函数

  • 子类构造函数: 子类构造函数必须调用父类构造函数,才能初始化父类继承的属性。
  • super()方法: 在子类构造函数中,可以使用super()方法调用父类的构造函数。

4.6 继承中的方法隐藏

  • 概念: 子类的方法与父类的某个方法具有相同的方法名,但参数列表不同,这种情况称为方法隐藏。
  • 区别: 方法隐藏与方法重写不同,方法隐藏不会改变方法的实现,而方法重写会改变方法的实现。
class Animal {
    void sound() { // 父类方法
        System.out.println("Animal makes a sound");
    }
}
class Dog extends Animal {
    void sound(int age) { // 子类方法,与父类方法同名,但参数不同
        System.out.println("Dog barks, age: " + age);
    }
}

4.7 继承中final关键字

  • final关键字: 用于修饰类、方法和变量,表示它们是最终的,不能被继承或重写。
  • final类: 表示该类不能被继承。
  • final方法: 表示该方法不能被子类重写。
  • final变量: 表示该变量是一个常量,其值一旦被赋值就不能再改变。

5. 多态

  • 概念: 多态是指同一个操作在不同的对象上会产生不同的行为。
  • 目的:
  • 代码灵活性和可扩展性:可以通过父类引用指向子类对象,调用不同的实现方法。
  • 提高代码可读性:可以使用更简洁、更灵活的方式来编写代码。

5.1 多态的概念

  • 父类引用: 可以使用父类类型的变量来引用子类对象。
  • 方法调用: 当调用父类引用中的方法时,实际执行的是子类重写后的方法。
class Animal { 
    void sound() {
        System.out.println("动物发出声音");
    }
}
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("狗叫");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog(); // 父类引用指向子类对象
        myDog.sound(); // 调用的是 Dog 类的 sound 方法
    }
}

5.2 多态的类型

  • 方法重载(Overloading): 同一个类中,方法名相同,参数列表不同。编译器会根据参数类型和数量选择合适的重载方法。
  • 方法重写(Overriding): 子类重写父类的方法。当父类引用指向子类对象时,调用的是子类重写后的方法。

5.3 多态的优势

  • 代码灵活性和可扩展性: 多态可以使代码更加灵活和易于扩展。
  • 代码可读性: 多态可以使代码更加简洁和易于理解。

5.4 多态的应用场景

多态在实际开发中有很多应用场景,例如:

  • 工厂模式: 通过工厂类创建不同类型的对象。
  • 策略模式: 定义一组算法,并将它们封装为独立的类,以便在运行时选择合适的算法。

5.5 抽象类

  • 概念: 抽象类是用abstract关键字修饰的类,它不能被直接实例化,只能被子类继承。抽象类可以包含抽象方法和普通方法。
  • 特点:
  • 抽象方法:没有方法体,以abstract关键字修饰。子类必须重写抽象方法才能实例化。
  • 不能被直接实例化:只能通过子类来实例化。
abstract class Shape { // 抽象类
    abstract void draw(); // 抽象方法,没有方法体
    void print() { // 普通方法
        System.out.println("这是一个形状");
    }
}
class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("画一个圆形");
    }
}
public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle(); // 实例化子类 Circle
        circle.draw(); // 调用子类重写的 draw 方法
        circle.print(); // 调用父类的 print 方法
    }
}

5.6 接口

  • 概念: 接口是使用interface关键字声明的,它是一种特殊的抽象类,其中只包含抽象方法和常量。接口不能被直接实例化,只能被类实现。
  • 特点:
  • 只能包含抽象方法和常量。
  • 可以被多个类实现。
  • 提高代码的可扩展性和灵活性。
interface Drawable { // 接口
    void draw(); // 抽象方法
}
class Circle implements Drawable { // 实现接口
    @Override
    public void draw() {
        System.out.println("画一个圆形"); 
    }
}
public class Main {
    public static void main(String[] args) {
        Drawable circle = new Circle(); // 实例化 Circle 对象
        circle.draw();
    }
}

6. 总结

概念
描述
优势
对象
现实世界中事物的抽象表示,包含属性和方法
提供了一种更自然、更易于理解和维护的编程方式
创建对象的模板,定义对象的属性和方法
定义了对象的结构和行为
封装
将对象的属性和方法结合起来,隐藏实现细节
提高代码安全性、可维护性和可重用性
继承
允许子类继承父类的属性和方法
代码复用、可扩展性、代码组织
多态
同一个操作在不同的对象上会产生不同的行为
代码灵活性和可扩展性、提高代码可读性
抽象类
abstract修饰的类,不能被直接实例化,只能被子类继承
定义公共方法和属性,并强制子类实现抽象方法
接口
interface关键字定义,包含抽象方法和常量,可以被多个类实现
提高代码的可扩展性和灵活性

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号