目录
- 文章导读
- 面向对象与面向过程
- 是什么?
- 两者的比较
- 面向对象的三大特性
- 封装
- 怎么理解
- 优点:
- 代码示例
- 继承
- 怎么理解
- 优点
- 代码示例
- 多态
- 怎么理解
- 优点
- 示例代码
- 总结
文章导读
本文不纠结语言的选择,仅仅介绍面向对象这一个编程思想的运用,以及与另一大主流的编程思想:面向过程的区别,我可以毫不夸张的说,面向对象这个词大家肯定都有所耳闻,但要真正理解它比找对象还要难!
面向对象与面向过程
是什么?
- 面向对象思想是一种以对象为中心的编程方式,它将问题领域中的实体(事物、概念)抽象为对象,并通过定义对象的属性和行为来描述和操作这些实体。
- 在面向对象思想中,对象是一个具体的实例,它具有特定的属性和行为。对象的属性表示对象的状态或特征,而对象的行为则表示对象可以执行的操作或方法。
- 类是面向对象编程的一个核心概念,它是一种用来定义对象的模板或蓝图。类定义了一组共同的属性和行为,这些属性和行为会被类的实例(对象)所继承和应用。
- 举个例子来说明,假设我们有一个类叫做"Person",它定义了人的属性(如姓名、年龄)和行为(如说话、行走)。通过这个类,我们可以创建多个Person对象,每个对象都有自己独立的姓名和年龄,并可以调用说话和行走方法。
- 面向过程编程是一种基于过程或者操作的编程方式。它将问题视为一系列的步骤或操作,按照顺序依次执行这些操作。面向过程的程序主要由函数或者过程组成,通过调用这些函数来实现特定的功能。C语言就是典型的面向过程的语言。
两者的比较
-
面向对象思想是将问题领域中的实体抽象为对象,并通过定义类的属性和行为来描述和操作这些对象。类是对象的模板,而对象则是类的具体实例化结果。通过面向对象的编程,可以更好地模拟现实世界中的问题,提高代码的可读性、扩展性和复用性。
-
而面向过程编程更注重功能的实现和步骤的顺序,适用于解决特定的、相对简单的问题。两种范式各有优劣,选择使用哪种编程范式取决于具体的问题和需求。那就让我们举一个具体的例子:假设我们要编写一个程序来模拟汽车的行驶过程
-
面向对象的实现方式:
- 创建一个"汽车"类,该类具有属性(如品牌、颜色、速度)和行为(如加速、刹车)。
- 在程序中创建多个汽车对象,每个对象代表一辆具体的汽车,并具有各自的属性和行为。
- 使用对象之间的消息传递,调用汽车对象的方法来模拟加速和刹车的操作。
-
面向过程的实现方式:
- 定义函数来表示汽车的行为,如"加速"和"刹车",这些函数接收汽车的参数进行处理。
- 在程序中创建多个汽车变量,并通过传递参数调用函数来模拟汽车的加速和刹车操作。
-
对比两种实现方式:
面向对象的实现方式更符合现实中的问题描述和设计,我们将汽车作为一个独立的对象来抽象,并通过定义类的属性和行为来描述汽车。多个具体的汽车对象可以同时存在,并且可以执行各自的行为,并且可以方便地扩展和修改汽车的属性和行为。
总的来说面向对象的编程思维之所以为大多数程序员使用,还要得益于他的三大特性!以下所有代码均使用Java这个主流语言来举例。
-
面向对象的三大特性
封装
怎么理解
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外的接口使其与外部发生联系。用户无需关心对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
优点:
- 减少耦合:可以独立地开发、测试、优化、使用、理解和修改
- 减轻维护的负担:可以更容易被理解,并且在调试的时候可以不影响其他模块
- 有效地调节性能:可以通过剖析来确定哪些模块影响了系统的性能
- 提高软件的可重用性
- 降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的
代码示例
public class Person {private String name;private int gender;private int age;public String getName() {return name;}public String getGender() {return gender == 0 ? "man" : "woman";}public void work() {if (18 <= age && age <= 50) {System.out.println(name + " is working very hard!");} else {System.out.println(name + " can't work any more!");}}
}
Person 类封装 name、gender、age 等属性,外界只能通过get()
方法获取一个 Person 对象的 name 属性和 gender 属性,而无法获取 age 属性,但是 age 属性可以供 work()
方法使用。
注意到 gender 属性使用int
数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改 gender 属性使用的数据类型时,也可以在不影响客户端代码的情况下进行。
继承
怎么理解
继承是面向对象编程中的一个重要特性,它允许一个类(称为子类)从另一个类(称为父类或基类)继承属性和方法。通过继承,子类可以重用父类的代码,并在此基础上添加、修改或覆盖自己的特定行为。
继承实现了 IS-A 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。
Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 向上转型 。
Animal animal = new Cat();
优点
- 代码重用:继承允许子类继承父类的属性和方法,子类不需要重新编写已经存在的代码,增加了代码的重用性。
- 扩展性:子类可以在父类的基础上扩展功能,添加自己特有的属性和方法,实现新的行为,从而满足特定需求。
- 维护性:通过继承,当基类需要修改时,只需要在基类中进行修改,所有的子类都会受到影响,减少了代码的维护成本。
代码示例
// 定义一个父类Animal
class Animal {protected String name;public Animal(String name) {this.name = name;}public void eat() {System.out.println(name + " is eating.");}
}// 定义一个子类Dog,继承自Animal
class Dog extends Animal {public Dog(String name) {super(name);}public void bark() {System.out.println(name + " is barking.");}
}// 主函数
class Main {public static void main(String[] args) {Dog dog = new Dog("Bobby");dog.eat(); // 调用从父类继承的eat()方法dog.bark(); // 调用子类自己的bark()方法}
}
在上述示例中,Animal类作为父类,有一个属性name和一个方法eat()
。Dog类作为子类继承了Animal类,同时自己添加了一个方法bark()
。在主函数中,我们创建了一个Dog对象,并调用了从父类继承的eat()
方法和子类自己的bark()
方法。通过继承,Dog类可以重用Animal类的代码,并扩展自己的行为。
多态
怎么理解
多态是面向对象编程中的一个重要概念,它允许使用不同的对象来调用相同的方法,实现了同一行为的多种表现形式。多态性提高了代码的灵活性、扩展性和可维护性。
多态分为编译时多态和运行时多态:
- 编译时多态主要指方法的重载
- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定
运行时多态有三个条件:
- 继承
- 覆盖(重写)
- 向上转型
优点
- 灵活性:多态使得代码可以适应不同的对象类型,可以通过更改对象的类型来实现不同的行为,增加了代码的灵活性。
- 扩展性:通过多态,可以很容易地增加新的子类,而不用修改现有的代码,提供了代码的扩展性。
- 可维护性:多态降低了代码的耦合性,使得代码更易于维护和理解。
示例代码
public class Instrument {public void play() {System.out.println("Instument is playing...");}
}public class Wind extends Instrument {public void play() {System.out.println("Wind is playing...");}
}public class Percussion extends Instrument {public void play() {System.out.println("Percussion is playing...");}
}public class Music {public static void main(String[] args) {List<Instrument> instruments = new ArrayList<>();instruments.add(new Wind());instruments.add(new Percussion());for(Instrument instrument : instruments) {instrument.play();}}
}运行结果:
Wind is playing...
Percussion is playing...
代码中,乐器类(Instrument)有两个子类:Wind 和 Percussion,它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。
补充:
访问父类的构造函数:可以使用super()
函数访问父类的构造函数,从而委托父类完成一些初始化的工作。应该注意到,子类一定会调用父类的构造函数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其它函数,那么就可以使用 super关键字。
总结
本文详细地介绍了面向对象编程的基本概念和特性,以及类、对象、继承、封装和多态的具体定义和应用。通过这篇文章,读者可以深入了解面向对象编程的思想和方法,并能够运用它来解决实际问题。如果短时间内无法理解,真的没有关系,在写代码读优秀的代码的过程中慢慢理解这些概念,你一定会豁然开朗的!
至于一些高级的面向对象的知识,比如设计原则、设计模式、抽象类等,这些在我们真正编程的时候很少会用到,等到你成为一名真正意义上的架构师,再去系统学习也不迟,作者水平有限,这里就不涉及了。