一、面向过程&面向对象
面向对象是基于面向过程这一概念产生而来的,因此要了解面向对象,首先得了解什么是面向过程。
什么是面向过程?
面向过程侧重于按顺序执行一系列步骤,强调函数(方法)和数据的分离,使用过程和函数来完成任务。与面向对象编程相比,缺乏抽象和封装,更直接地解决问题。
例如:
public class A{
public static int countArea(int height,int width){
return height*width;
}
public static void main(String[] args){
int height = 2;
int width = 3;
//长方形面积为:6
System.out.println("长方形面积为:"+countArea(height,width));
}
}
什么是面向对象?
通过模块化的对象,将数据和操作数据的方法封装在一起。核心思想包括类与对象的概念、封装、继承、多态,提供了更灵活、可维护的代码结构。
例如:
public class Rectangle{
private int height = 2;
private int width = 3;
public Rectangle(int height,int width){
this.height = height;
this.width = width;
}
public void setHeight(int height){
this.height = height;
}
public void setWidth(int width){
this.width = width;
}
public int countArea(){
return height*width;
}
}
public class B{
public static void main(String[] args){
Rectangle r = new Rectangle(2,3);
//长方形面积为:6
System.out.println("长方形面积为:"+r.countArea());
}
}
单单从简单的例子可能并不容易看出面向对象编程的好处,设想:涉及到复杂的开发时,面向过程的编程仅仅数据与方法的分离就会产生许多问题,例如代码的可读性低,难以维护等,为了能够解决面向过程开发中存在的一系列问题,掌握面向对象的开发思想是一名开发者必备的。
二、面向对象的三大特性
2.1 封装
封装是将对象的状态(属性)和行为(方法)打包在一起,并对外部隐藏内部实现细节。通过访问修饰符限制对对象内部的直接访问,提高了代码的安全性和可维护性。可以参考以上Rectangle类的例子。
2.2 继承
继承允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以重用父类的代码,同时可以进行扩展或修改,提高了代码的重用性和可扩展性。
例如:车辆都有点火"启动"和"熄火"的行为,因此创建一个父类Vehicle。卡车能"运货"、大巴能"运人",大巴与卡车又属于车辆,因此子类又有自己独有的方法,而子类继承父类就不需要多次重复写"启动"和"熄火"这两个方法。
public class Vehicle {
public void start() {
System.out.println("车辆启动...");
}
public void stop() {
System.out.println("车辆停止...");
}
}
public class Truck extends Vehicle {
private String brand;
public Truck(String brand) {
this.brand = brand;
}
public void shipping() {
System.out.println("卡车:" + brand + " 正在运货...");
}
}
2.3 多态
多态指的是一个对象可以以多种形式呈现。在编译时多态中,通过方法的重载实现;而在运行时多态中,通过方法的重写和接口实现。多态提高了代码的灵活性和可维护性,使得同一接口可以有不同的实现。
例如:要实现画图工具类,先定义一个接口Shape,所有的实现类都要实现draw方法进行画图,但是draw方法的具体实现各不相同,因此这个draw抽象方法不需要方法体,具体实现由实现类来给出。在使用时,用父类Shape声明参数类型,而后直接调用draw方法,具体调用哪个实现类则由new或其他方式给出,降低了代码的耦合度。
// 接口
public interface Shape {
void draw();
}
// 具体实现类
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("画了一个圆...");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("画了一个正方形...");
}
}
// 使用多态
public class DrawingProgram {
public static void main(String[] args) {
Shape shape1 = new Circle();
Shape shape2 = new Square();
shape1.draw(); // 调用的是 Circle 类的 draw 方法
shape2.draw(); // 调用的是 Square 类的 draw 方法
}
}
三、面向对象设计的基本原则
面向对象设计有一些重要的原则,这些原则帮助开发者创建灵活、可维护、可扩展的软件系统。以下是其中一些常见的原则:
-
单一职责原则(Single Responsibility Principle - SRP): 一个类应该只有一个引起变化的原因。换句话说,一个类应该只有一个职责。这有助于保持类的简单性和可维护性。
-
开放-封闭原则(Open-Closed Principle - OCP): 软件实体(类、模块、函数等)应该对扩展是开放的,但对修改是封闭的。通过增加新的代码而不修改已有代码,实现对系统的扩展。
-
里氏替换原则(Liskov Substitution Principle - LSP): 子类型必须能够替换掉它们的基类型。在使用继承时,子类应该能够替代其基类而不影响程序的正确性。
-
依赖倒置原则(Dependency Inversion Principle - DIP): 高层模块不应该依赖于底层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。通过依赖注入和接口等方式,实现松耦合。
-
接口隔离原则(Interface Segregation Principle - ISP): 不应该强迫一个类实现它不需要的接口。客户端不应该被迫依赖它们不使用的方法。
-
合成/聚合复用原则(Composition/Aggregation Reuse Principle - CARP): 优先使用合成/聚合而不是继承。通过组合现有的对象,而不是通过继承类来实现代码的重用。
-
迪米特法则(Law of Demeter - LoD,又称最少知识原则): 一个对象应该对其他对象有最少的了解。一个类不应该直接调用其他类的内部方法,而是通过中介者或者委托来实现。
这些原则一起帮助开发者设计出符合良好设计原则的、易于维护和扩展的面向对象系统。这些原则通常被称为 SOLID 原则,其中 SOLID 分别代表了这些原则的首字母。