02 什么是面向对象?

老规矩还是金字塔形式的介绍,实际的教程中,总是提到面向对象编程与面向对象编程语言,我认为面向编程是一种思想,那学习过程中就应该单纯点。

1. 什么是面向对象?

跟上节内容类似,提起面向对象你不得不提起面向过程编程,如果按照历史发展脚步来说,面向对象可以理解为面向过程的一种进化。

  • 面向对象编程(OO) 更注重对象的抽象和封装,通过定义类来组织数据和行为,以便更容易理解和维护代码。
  • 面向过程编程(OP) 更注重过程和函数,通过将数据和操作分离,强调顺序执行的步骤。

面向对象编程中有两个非常重要、非常基础的概念,那就是类(class)和对象

1.1 什么是类?

  • 定义: 类是面向对象编程(OOP)中的一个基本概念,它是对象的模板或蓝图,描述了一类对象所共有的属性和行为。

  • 特点: 类定义了对象的结构,包括属性(也称为成员变量)和方法(也称为成员函数)。属性表示对象的状态,而方法表示对象的行为。

  • 目的: 类的目的是为了创建对象。当你创建一个类的实例,你实际上是在内存中分配了一块空间,按照类的定义创建了一个对象。

  • 类是面向对象的基础单元: 类是面向对象编程的基本构建块。它定义了对象的结构和行为,是对象的抽象。 对象是类的实例

面向对象编程是一种编程范式编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石 。

1.2 面向对象与面向过程相比的优点?

面向对象编程相比起面向过程编程的优势主要有三个。

易于理解和理念上的接近现实世界:

对于大规模复杂程序的开发,程序的处理流程并非单一的一条主线,而是错综复杂的网状结构。面向对象编程比起面向过程编程,更能应对这种复杂类型的程序开发。

面向对象编程相比面向过程编程,具有更加丰富的特性(封装、抽象、继承、多态)。利用这些特性编写出来的代码,更加易扩展、易复用、易维护。

从编程语言跟机器打交道的方式的演进规律中,我们可以总结出:面向对象编程语言比起面向过程编程语言,更加人性化、更加高级、更加智能。

2. 什么是面向对象分析和面向对象设计?

面向对象编程(OOP),编程是一个动词性质的词汇,绝大部分的项目在实际行动前,都会做一些前瞻性的工作。那就是面向对象分析(OOA)和面向对象设计(OOD),连起来读 分析,设计,开工(编程),怎么样是不是整个流程都连贯起来了。

面向对象分析就是要搞清楚做什么

面向对象设计就是要搞清楚怎么做

面向对象编程就是将分析和设计的的结果翻译成代码的过程。

之所以在前面加"面向对象"这几个字,是因为我们是围绕着对象或类来做需求分析和设计的。 分析和设计两个阶段最终的产出是类的设计,包括程序被拆解为哪些类,每个类有哪些属性方法,类与类之间如何交互等等

3. 封装、继承、抽象、多态

封装、继承、多态、抽象是面向对象的四大特性,是面向对象编程的基石,那么我们应该好好理解一下这四大特征。

3.1 封装

1. 什么是封装?

封装 通常指的是将数据(即状态)和操作数据的方法(即行为)打包到一个单一的单元中。这个单元就是类。

通过这种方式,我们可以控制对数据的访问,以及在访问数据时执行的操作。这种封装提供了一种隐藏实现细节的方法,同时使得代码更加模块化和可维护。

下面举一个例子:

js 复制代码
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String newName) {
        if (newName != null && !newName.isEmpty()) {
            this.name = newName;
        } else {
            System.out.println("Invalid name");
        }
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int newAge) {
        if (newAge >= 0 && newAge <= 150) {
            this.age = newAge;
        } else {
            System.out.println("Invalid age");
        }
    }

    public static void main(String[] args) {
        // 使用Person类
        Person person1 = new Person("Alice", 30);
        System.out.println(person1.getName());  // 输出: Alice
        System.out.println(person1.getAge());   // 输出: 30

        person1.setName("Bob");
        person1.setAge(25);

        System.out.println(person1.getName());  // 输出: Bob
        System.out.println(person1.getAge());   // 输出: 25
    }
}

Person 类封装了一个人的基本信息,包括姓名和年龄。对姓名和年龄的访问通过getNamesetNamegetAgesetAge 方法进行。这里的关键是,我们可以在 setAamesetAge 方法中添加条件检查,以确保我们不会设置无效的姓名和年龄。这种条件检查的逻辑是被封装在类的内部的,外部用户只需调用相应的方法,而不用担心具体的实现细节。

2. 封装有什么优点?

  1. 隐藏实现细节: 封装允许你将对象的实现细节隐藏起来,只暴露必要的接口。这样,用户只能通过对象的公共方法来与对象交互,而不需要了解其内部的具体实现。这有助于防止用户对对象的误操作,同时提高了系统的安全性。
  2. 简化复杂性: 封装使得系统更易理解。通过将对象划分为模块,并将每个模块的实现细节隐藏,开发人员可以更专注于理解和处理每个独立的部分,而不必担心整个系统的复杂性。
  3. 提高代码的可维护性: 通过隐藏实现细节,封装降低了代码的耦合度。当对象内部的实现发生变化时,只需要更新公共接口而不会影响到其他部分的代码。这使得代码更容易维护,因为修改只需在一个地方进行。
  4. 提高重用性: 封装使得对象的具体实现与其外部的使用者相分离。这种独立性使得对象更容易被复用在不同的上下文中,而不需要担心对其他代码的影响。
  5. 降低系统的复杂性: 封装有助于将系统划分为更小、更易管理的部分。每个部分都有其独立的职责,这样系统的整体结构更加清晰,更容易理解。
  6. 增加安全性: 通过隐藏对象的内部实现细节,封装提供了一层保护,防止外部代码直接访问和修改对象的状态。这有助于确保对象的状态得到正确的维护和管理。

优点太多了,都是必应搜索来的,根据上面的例子也可以看出来,只是一个setAge里面我们做安全性检查,假如针对这个name我们要做去重,做加密等操作,这些都是封装在setAge里面,只需要在调用处调用即可,对外就是设置姓名,是不是很便于理解与维护。

3.2 抽象

1. 抽象是什么

封装主要讲的是如何隐藏信息、保护数据,而抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。

js 复制代码
// 抽象类
abstract class Shape {
    // 抽象方法
    abstract void draw();

    // 具体方法
    void display() {
        System.out.println("Displaying the shape.");
    }
}

// 子类
class Circle extends Shape {
    // 实现抽象方法
    void draw() {
        System.out.println("Drawing a circle.");
    }
}

class Rectangle extends Shape {
    // 实现抽象方法
    void draw() {
        System.out.println("Drawing a rectangle.");
    }
}

public class Main {
    public static void main(String[] args) {
        Circle circle = new Circle();
        circle.draw();
        circle.display();

        Rectangle rectangle = new Rectangle();
        rectangle.draw();
        rectangle.display();
    }
}

在这个例子中,Shape 是一个抽象类,它包含一个抽象方法 draw 和一个具体方法 displayCircleRectangleShape 的子类,它们必须实现抽象方法 draw。抽象类提供了一种通用的结构,但需要子类提供具体实现。

2. 抽象的优点?

  1. 代码的可维护性: 抽象通过隐藏对象的具体实现细节,使得系统更易于维护。如果对象的内部实现需要修改,只需确保对外部接口的兼容性,而不需要修改使用该对象的其他部分的代码。
  2. 代码的可扩展性: 抽象允许通过创建新的子类或实现新的接口来扩展系统。新的类可以继承现有的抽象类,从而获得共享的属性和方法,然后添加新的功能或修改现有功能。
  3. 多态性的支持: 抽象为多态性提供了基础。通过抽象类和接口,可以实现在不同的类中使用相同的接口或抽象类,从而实现多态性,提高代码的灵活性和可复用性。
  4. 系统结构的清晰性: 抽象可以帮助创建清晰、模块化的系统结构。通过将对象划分为抽象的层次结构,每个层次都有其特定的职责,系统的整体结构更容易理解。
  5. 问题领域建模的提升: 抽象有助于更好地建模问题领域。通过从具体的实例中提取通用的特征,可以更好地反映问题领域的本质,使得代码更贴近真实世界的概念。
  6. 接口的定义和实现分离: 抽象允许将接口的定义与实现分离。抽象类和接口定义了对象的公共接口,而具体的实现细节则由具体的子类或实现类提供。这有助于降低耦合度,提高系统的灵活性。

特别重要的例子就是上面的draw,对于circle来说 draw就是画一个圆,如果不采取抽象的方法,那我们在实际实现中,要分别实现drawCricledrawShapedrawRectangle,当要再实现一个六边形,就需要修改现在的代码,增加一个六边形的方法。 假如使用抽象方法,那就直接增加一个六边形对象,在类内实现draw方法就可以了。

3.3 继承

1.什么是继承?

封装主要讲的是如何隐藏信息、保护数据, 而抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。 继承是为了解决代码复用,子类可以复用父类的代码,并且可以在其基础上进行扩展或修改。这有助于提高代码的重用性和可扩展性。

继承是用来表示类之间的is-a 关系,比如猫是一种哺乳动物。从继承关系上来讲,继承可以分为两种模式,单继承和多继承。

js 复制代码
// 基类
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    void makeSound() {
        System.out.println("Some generic sound");
    }
}
js 复制代码
// 子类
class Dog extends Animal {
    Dog(String name) {
        // 调用父类的构造函数
        super(name);
    }

    // 子类可以重写父类的方法
    @Override
    void makeSound() {
        System.out.println("Woof! Woof!");
    }

    // 子类可以拥有自己的方法
    void wagTail() {
        System.out.println(name + " is wagging its tail.");
    }
}

跟绝大多数教材一样,是一个很简单的例子,一些关键注释都加到代码里面了。

2. 继承的优点?

继承最大的一个好处就是代码复用。

假如我们现在要写8个动物,都有动物叫这一个动作,那么继承无疑是很好的选择。但是,需要注意过度使用继承可能导致层次结构复杂和紧耦合的问题,因此在设计中需要权衡使用。

3.4 多态

多态是允许使用相同的接口来处理不同的对象。多态性分为编译时多态性(静态多态性)和运行时多态性(动态多态性)两种。

js 复制代码
// 抽象基类
abstract class Shape {
    abstract void draw();
}

// 圆形类
class Circle extends Shape {
    void draw() {
        System.out.println("Drawing a circle");
    }
}

// 矩形类
class Rectangle extends Shape {
    void draw() {
        System.out.println("Drawing a rectangle");
    }
}
js 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建 Circle 对象
        Shape myCircle = new Circle();

        // 创建 Rectangle 对象
        Shape myRectangle = new Rectangle();

        // 调用 draw 方法,具体的实现由对象的实际类型决定
        myCircle.draw();      // 输出: Drawing a circle
        myRectangle.draw();   // 输出: Drawing a rectangle
    }
}

在这个例子中,我们创建了一个基类 Shape 的引用 myCirclemyRectangle,分别指向 CircleRectangle 的对象。通过调用 draw 方法,实际调用的是对象的实际类型的 draw 方法。这就是运行时多态性的体现,同样的方法调用可以根据对象的实际类型执行不同的操作。

2.多态的实现

对于多态特性的实现方式,除了利用"继承加方法重写"这种实现方式之外,我们也利用接口类语法来实现。

实现多态要有哪些语法机制:

第一个语法机制是编程语言要支持父类对象可以引用子类对象,

js 复制代码
 Shape myCircle = new Circle();

第二个语法机制是编程语言要支持继承,

js 复制代码
// 圆形类
class Circle extends Shape {
    void draw() {
        System.out.println("Drawing a circle");
    }
}

第三个语法机制是编程语言要支持子类可以重写(override)父类中的方法,

js 复制代码
// 圆形类

void draw() {
    System.out.println("Drawing a circle");
}

3.多态的优点

多态性是一种强大的编程工具,通过提供统一的接口和动态绑定,它使得代码更灵活、可扩展,更容易适应变化。这使得面向对象的软件系统更易于设计、实现和维护。

像上面代码的draw()方法,只需要在不同的对象调用draw方法,就可以实现对各种形状的输出,这显然提高了代码的复用性以及灵活性。

总结

总的来说,封装,抽象,多态,继承,从不同的方向优化了面向对象编程。极大的提高了面向对象编程的灵活性,扩展性,可维护性,等。

相关推荐
原野心存8 分钟前
java基础进阶——继承、多态、异常捕获(2)
java·java基础知识·java代码审计
进阶的架构师13 分钟前
互联网Java工程师面试题及答案整理(2024年最新版)
java·开发语言
黄俊懿13 分钟前
【深入理解SpringCloud微服务】手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法
java·后端·算法·spring cloud·微服务·架构
木子020422 分钟前
java高并发场景RabbitMQ的使用
java·开发语言
夜雨翦春韭33 分钟前
【代码随想录Day29】贪心算法Part03
java·数据结构·算法·leetcode·贪心算法
大霞上仙1 小时前
jmeter学习(1)线程组与发送请求
java·学习·jmeter
笃励1 小时前
Java面试题二
java·开发语言·python
易雪寒2 小时前
IDEA在git提交时添加忽略文件
java·git·intellij-idea
打码人的日常分享2 小时前
企业人力资源管理,人事档案管理,绩效考核,五险一金,招聘培训,薪酬管理一体化管理系统(源码)
java·数据库·python·需求分析·规格说明书
27669582922 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东