简述 抽象 封装 多态 继承

抽象、封装、多态和继承是面向对象编程(OOP)的四大基本概念,它们构成了面向对象设计的基础。

抽象(Abstraction)

含义

抽象是从众多事物中提取共有特征忽略非本质细节,将复杂的现实世界简化为模型的过程。它关注的是对象的本质特征,而忽略其非本质的细节。在编程中,抽象通常通过类和接口来实现,定义它们所应具备的属性和行为,但不关注具体实现。类定义对象的属性和行为,接口定义类应具备的能力。通过抽象,我们可以专注于对象的核心特征,而无需考虑所有的细节。

抽象通过定义抽象类(包含纯虚函数的类)来实现,抽象类不能被实例化,必须由子类来实现其纯虚函数

作用

抽象通过提取共同的特征(属性和行为),形成类或接口,从而实现代码的模块化和复用。因此,抽象使得程序员可以创建高度通用的代码,而不必关心具体对象的具体实现。例如,在Java中,抽象类和接口都用于定义抽象的行为,具体的实现则由子类来完成。

举例

设想我们要模拟一个“动物”系统。动物有许多种类,如猫、狗等。所有动物都有“吃”、“睡”、“喊叫”等行为,但不同的动物会以不同的方式执行这些行为。通过抽象,我们可以定义一个Animal抽象类,包含这些通用行为,而具体的实现由各个子类(如Cat、Dog)来完成。

//C++
#include <iostream>

// 抽象类
class Animal {
public:
    virtual void sound() = 0; // 纯虚函数,抽象方法,没有具体实现
};

//子类 1
class Dog : public Animal {
public:
    void sound() override {
        std::cout << "Woof!" << std::endl;
    }
};

//子类 2
class Cat : public Animal {
public:
    void sound() override {
        std::cout << "Meow!" << std::endl;
    }
};

int main() {
    Dog dog;
    Cat cat;

    dog.sound();  // 输出: Woof!
    cat.sound();  // 输出: Meow!

    return 0;
}

//java
abstract class Animal {
    abstract void sound(); // 抽象方法
}

class Dog extends Animal {
    void sound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    void sound() {
        System.out.println("Meow!");
    }
}

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

        dog.sound(); // 输出: Woof!
        cat.sound(); // 输出: Meow!
    }
}

这里的 Animal 类就是一个抽象类,定义了所有动物应该具备的行为,但没有具体实现,由其子类 Dog 类和 Cat 类来实现。

封装 (Encapsulation)

含义

封装是将对象的状态(属性)和行为(方法隐藏在类内部。只通过公开的方法(即接口)来访问和修改这些属性。封装的目的是保护对象的内部状态,避免外部代码直接访问和修改对象数据,从而造成不一致或错误。

作用

封装通过访问修饰符(如private、protected、public)来控制对象内部数据的访问权限。将数据成员设为私有(private),并通过公有(public)的成员函数(方法)来访问和修改它们,从而实现数据隐藏。这样,类可以通过公开的方法来提供对数据的访问,而不暴露数据的具体实现,增强了代码的安全性和可维护性。

举例

“人”类的属性(数据成员),例如名字和年龄,为了保证其不随意被外部代码修改,因此设为私有(private),同时提供公有(public)的成员函数(方法),例如 getter 和 setter 方法来实现对这些成员变量的访问和修改,从而实现数据隐藏。

//C++
#include <iostream>
#include <string>

class Person {
private:
    std::string name;
    int age;

public:
    void setName(const std::string& n) {
        name = n;
    }

    std::string getName() const {
        return name;
    }

    void setAge(int a) {
        if (a > 0) {
            age = a;
        }
    }

    int getAge() const {
        return age;
    }
};

int main() {
    Person person;
    person.setName("Alice");
    person.setAge(30);

    std::cout << "Name: " << person.getName() << std::endl;
    std::cout << "Age: " << person.getAge() << std::endl;

    return 0;
}

//java
class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        if (age > 0) {
            this.age = age;
        }
    }

    public int getAge() {
        return age;
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("Alice");
        person.setAge(30);

        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
    }
}

在这个示例中,name和age属性是私有的,只能通过公有的set和get方法来访问。

多态 (Polymorphism)

含义

多态是指同一操作在不同对象上可以表现出不同的行为。多态可以通过函数重载、运算符重载和虚函数来实现。多态的核心是通过父类或接口的引用指向子类对象,并通过使用基类指针或引用来调用子类的重写函数。在使用继承时,多态允许我们以父类的形式处理子类对象,但可以根据实际对象的类型调用相应的方法。

作用

多态增强了程序的灵活性和可扩展性,使得相同的接口可以用于不同类型的对象。在Java中,多态通常通过方法重写(Override)和接口实现来实现,即子类可以根据自己的需要修改父类的方法,实现不同的行为,这使得代码更加动态和通用。

举例

Shape类作为父类,其有一个draw方法。具体的形状如Circle和Square都可以继承这个类,并提供各自的draw方法。我们可以通过多态,在不关心具体形状的情况下,调用形状的draw方法,再由具体形状如Circle和Square决定绘制的内容。

//C++
#include <iostream>

//父类
class Shape {
public:
	//绘画函数
    virtual void draw() const = 0; // 纯虚函数,抽象方法
};

//子类 1
class Circle : public Shape {
public:
    void draw() const override {	//绘制圆形
        std::cout << "Drawing a circle." << std::endl;
    }
};

//子类 2
class Square : public Shape {
public:
    void draw() const override {	//绘制方形
        std::cout << "Drawing a square." << std::endl;
    }
};

//接口函数
void renderShape(const Shape& shape) {
    shape.draw(); // 根据传入对象的类型调用相应的方法
}
//接口函数的另一种写法
/*void renderShape(Shape* shape) {
    shape->draw(); // 根据实际对象调用不同的draw方法
}*/

int main() {
    Circle circle;
    Square square;

    renderShape(circle);  // 输出: Drawing a circle.
    renderShape(square);  // 输出: Drawing a square.

    return 0;
}

//java
class Shape {
    void draw() {
        System.out.println("Drawing a shape.");
    }
}

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

class Square extends Shape {
    void draw() {
        System.out.println("Drawing a square.");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Square();

        shape1.draw(); // 输出: Drawing a circle.
        shape2.draw(); // 输出: Drawing a square.
    }
}

在这个例子中,renderShape函数接受一个Shape指针,但可以根据传入的具体对象(如Circle或Square)调用不同的draw方法,也即draw方法在不同的形状对象上有了不同的表现。

继承 (Inheritance)

含义

继承是指一个类(子类)从另一个类(父类)获取属性和方法的过程。子类不仅可以继承父类的行为,还可以根据需要扩展或修改父类的行为。即,子类不仅可以直接使用(复用)父类的成员函数,还可以添加自己的成员函数。继承有助于代码的重用和层次化设计。

作用

继承使得代码的复用性大大提高,减少了重复代码的编写。它还支持类之间的层次化设计,使得类的结构更清晰。例如,在Java中,子类使用extends关键字继承父类,并可以重写父类的方法。

举例

Vehicle类作为父类,它有“启动”的行为。现在,我们想创建一个Car类,除了启动外,还可以鸣笛。我们可以让Car类继承Vehicle类,这样它就自动获得了启动的功能,只需要额外添加鸣笛的功能即可。

//C++
#include <iostream>

//父类
class Vehicle {
public:
    void start() {	// 车辆启动方法
        std::cout << "Vehicle started." << std::endl;
    }
};

//子类
class Car : public Vehicle {
public:
    void honk() {	// 鸣笛方法
        std::cout << "Car horn honking." << std::endl;
    }
};

int main() {
    Car car;
    car.start();  // 调用父类的方法
    car.honk();   // 调用子类的方法

    return 0;
}


//java
class Vehicle {
    void start() {
        System.out.println("Vehicle started.");
    }
}

class Car extends Vehicle {
    void honk() {
        System.out.println("Car horn honking.");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();  // 调用父类的方法
        car.honk();   // 调用子类的方法
    }
}

在这个例子中,Car类继承了Vehicle类,因此可以使用start方法,还增加了自己的honk方法。

总结

抽象:关注的是“做什么”,而不关心“怎么做”。强调的是对对象共性的提取,隐藏细节,定义行为接口。
封装:是“隐藏细节,暴露接口”,通过控制访问来保护数据。强调的是数据保护,通过隐藏实现细节,控制外部对数据的访问。
多态 :是“一个接口,多种表现”,使得代码可以对不同的对象进行通用处理。强调的是操作的通用性,在不改变接口的前提下表现出多种行为。
继承 :是“复用和扩展”,通过共享父类的特性,减少代码重复。强调的是代码的复用,通过父子类关系共享行为和属性。

这些概念相辅相成,共同构成了面向对象编程的核心思想,使代码更加模块化、易于维护和扩展。

·····杂谈··········
抽象说:我们需要定义一个接口
封装说:我们要掩藏数据,只让外人看到这个接口
多态说:我们要针对这个接口,有多种表现行为
继承说:我们不仅可以使用基类的接口,还可以自己定义接口
·····················

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值