✅如何理解面向对象和面向过程?
- 典型理解
- ✅扩展知识仓
- ✅面向对象的三大基本特征
- ✅封装
- ✅继承
- ✅多态
- ✅为什么Java不支持多继承?
- ✅菱形继承问题
- ✅Java 8 中的多继承
- ✅面向对象的五大基本原则?
典型理解
面向过程把问题分解成一个一个步骤,每个步骤用函数实现,依次调用即可。
我们在进行面向过程编程的时候,不需要考虑那么多,上来先定义一个函数,然后使用各种诸如if-else、for-each等方式进行代码执行。最典型的用法就是实现一个简单的算法,比如实现冒泡排序。
面向对象将问题分解成一人一个步骤,对每个步骤进行相应的抽象,形成对象,通过不同对象之间的调用,组合解决问题。
就是说,在进行面向对象进行编程的时候,要把属性、行为等封装成对象,然后基于这些对象及对象的能力进行业务逻辑的实现。比如想要造一辆车,上来要先把车的各种属性定义出来,然后抽象成一个Car类。
面向对象有封装、继承、多态二大基本特征,和单一职责原则、开放封闭原则、Liskov替换原则、依赖倒置原则和 接口隔离原则等五大基本原则。
✅扩展知识仓
✅面向对象的三大基本特征
三大基本特征:封装、继承、多态。
✅封装
**封装就是把现实世界中的客观事物抽象成一个Java类,然后在类中存放属性和方法。**比如:封装一个汽车类,其中包含了发动机、轮胎、底盘等属性,并且有启动、前进等方法。
//定义一个汽车Car类
public class Car {
// 私有变量
private String brand; // 品牌
private String model; // 型号
private int year; // 生产年份
private String color; // 颜色
private double price; // 价格
private Engine engine; // 引擎
private Transmission transmission; // 变速器
private boolean isOwned; // 是否拥有
// 构造方法
public Car(String brand, String model, int year, String color, double price, Engine engine, Transmission transmission) {
this.brand = brand;
this.model = model;
this.year = year;
this.color = color;
this.price = price;
this.engine = engine;
this.transmission = transmission;
this.isOwned = false;
}
// 公共方法:获取品牌
public String getBrand() {
return brand;
}
// 公共方法:设置品牌
public void setBrand(String brand) {
this.brand = brand;
}
// 公共方法:获取型号
public String getModel() {
return model;
}
// 公共方法:设置型号
public void setModel(String model) {
this.model = model;
}
// 公共方法:获取生产年份
public int getYear() {
return year;
}
// 公共方法:设置生产年份
public void setYear(int year) {
this.year = year;
}
// 公共方法:获取颜色
public String getColor() {
return color;
}
// 公共方法:设置颜色
public void setColor(String color) {
this.color = color;
}
// 公共方法:获取价格
public double getPrice() {
return price;
}
// 公共方法:设置价格
public void setPrice(double price) {
this.price = price;
}
// 公共方法:获取引擎对象
public Engine getEngine() {
return engine;
}
// 公共方法:设置引擎对象
public void setEngine(Engine engine) {
this.engine = engine;
}
// 公共方法:获取变速器对象
public Transmission getTransmission() {
return transmission;
}
// 公共方法:设置变速器对象
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
// 公共方法:判断是否拥有该汽车,如果拥有则返回true,否则返回false。在构造函数中,isOwned被设置为false,表示初始状态为未拥有。可以在后续操作中改变其状态。
public boolean isOwned() {
return isOwned;
}
}
✅继承
像现实世界中儿子可以继承父亲的财产、样貌、行为等一样,编程世界中也有继承,继承的主要目的就是为了复用。子类可以继承父类,这样就可以把父类的属性和方法继承过来。
比如:Dog类,可以继承Animal类,继承过来嘴巴,颜色、等属性,吃东西、奔跑等行为。
// Animal类
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 抽象方法,需要子类实现
public abstract void makeSound();
}
// Dog类,继承自Animal类
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 调用父类的构造函数
this.breed = breed;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
// 实现父类的抽象方法
@Override
public void makeSound() {
System.out.println("汪汪!");
}
}
在这个例子中,Animal类是一个抽象类,定义了一个抽象方法makeSound,它需要由子类来实现。Dog类继承了Animal类,并实现了makeSound方法,因此它是一个具体的类。在Dog类中,我们还添加了一个新的属性breed和一个相应的方法来获取和设置该属性的值。
✅多态
多态是指在父类中定义的方法被子类继承之后,可以通过重写,使得父类和子类具有不同的实现,这使得同一个方法在父类极其各个子类中具有不同的含义。
// 定义一个接口,名为Animal
public interface Animal {
// 定义一个抽象方法,用于发出动物的叫声
void makeSound();
}
// 定义一个类,名为Dog,实现Animal接口
public class Dog implements Animal {
// 重写makeSound方法,实现狗的叫声
@Override
public void makeSound() {
System.out.println("汪汪!");
}
}
// 定义一个类,名为Cat,实现Animal接口
public class Cat implements Animal {
// 重写makeSound方法,实现猫的叫声
@Override
public void makeSound() {
System.out.println("喵喵!");
}
}
// 定义一个类,名为Zoo,包含一个Animal类型的成员变量
public class Zoo {
// 定义一个Animal类型的成员变量,可以是任何实现Animal接口的类对象
private Animal animal;
// 构造函数,接受一个Animal类型的参数,用于初始化animal成员变量
public Zoo(Animal animal) {
this.animal = animal;
}
// 定义一个方法,让动物发出叫声
public void letAnimalMakeSound() {
// 使用多态性,调用animal对象的makeSound方法,实现不同的叫声
animal.makeSound();
}
}
// 在主函数中测试多态性的实现
public static void main(String[] args) {
// 创建一个Dog对象
Dog dog = new Dog();
// 使用Dog对象创建一个Zoo对象
Zoo zoo = new Zoo(dog);
// 调用Zoo对象的letAnimalMakeSound方法,让动物发出叫声
zoo.letAnimalMakeSound(); // 输出 "汪汪!"
// 创建一个Cat对象
Cat cat = new Cat();
// 使用Cat对象创建一个Zoo对象
zoo = new Zoo(cat);
// 调用Zoo对象的letAnimalMakeSound方法,让动物发出叫声
zoo.letAnimalMakeSound(); // 输出 "喵喵!"
}
在这个例子中,我们定义了一个Animal接口和两个实现了该接口的类Dog和Cat。我们还定义了一个Zoo类,其中包含一个Animal类型的成员变量。在Zoo类中,我们使用多态性来调用Animal对象的makeSound方法,实现了不同的叫声。在主函数中,我们分别使用Dog和Cat对象创建了两个Zoo对象,并调用了它们的letAnimalMakeSound方法,输出了不同的叫声。
✅为什么Java不支持多继承?
因为如果要实现多继承,就会像C++中一样,存在菱形继承的问题,C++为了解决菱形继承问题,又引入了虚继承。因为支持多继承,引入了菱形继承问题,又因为要解决菱形继承问题,引入了虚继承。而经过分析,人们发现我们其实真正想要使用多继承的情况并不多。所以,在Java 中,不允许“多继承”,即一个类不允许继承多个父类。
除了菱形的问题,支持多继承复杂度也会增加。一个类然承了多个父类,可能会继承大量的属性和方法,导致类的接口变得庞大、难以理解和维护。此外,在修改一个父类时,可能会影响到多个子类,增加了代码的耦合度。
在Java 8以前,接口中是不能有方法的实现的。所以一个类同时实现多个接口的话,也不会出现C++中的歧义问题。因为所有方法都没有方法体,真正的实现还是在子类中的。但是,Java 8中支持了默认函数(defaultmethod),即接口中可以定义一个有方法体的方法了。
而又因为Java支持同时实现多个接口,这就相当于通过implements就可以从多人接口中继承到多人方法了,但是,Java8中为了避免菱形继承的问题,在实现的多个接口中如果有相同方法,就会要求该类必须重写这个方法。
✅菱形继承问题
Java的创始人James Gosling曾经回答过,他表示:
"Java之所以不支持一个类继承多个类,主要是因为在设计之初我们听取了来自C++和Obiective-C等阵营的人的意见。因为多继承会产生很多歧义问题。”
Gosling老人家提到的歧义问题,其实是C++因为支持多继承之后带来的菱形继承问题。
假设我们有类B和类C,它们都继承了相同的类A。另外我们还有类D,类D通过多重继承机制继承了类B和类C.。
这时候,因为D同时继承了B和C,并且B和C又同时继承了A,那么,D中就会因为多重继承,继承到两份来自A中的属性和方法。
这时候,在使用D的时候,如果想要调用一个定义在A中的方法时,就会出现歧义。
因为这样的继承关系的形状类似于菱形,因此这个问题被形象地称为菱形继承问题。
而C++为了解决菱形继承问题,又引入了虚继承。
因为支持多继承,引入了菱形继承问题,又因为要解决菱形继承问题,引入了虚继承。而经过分析,人们发现我们其实真正想要使用多继承的情况并不多。
所以,在Java 中,不允许“声明多继承”,即一个类不允许继承多个父类。但是Java 允许“实现多继承”,即个类可以实现多个接口,一个接口也可以继承多个父接口。由于接口只允许有方法声明而不允许有方法实现(Java 8之前),这就避免了 C++ 中多继承的歧义问题。
✅Java 8 中的多继承
Java不支持多继承,但是是支持多实现的,也就是说,同一个类可以同时实现多个接口。
我们知道,在Java 8以前,接口中是不能有方法的实现的。所以一个类同时实现多个接口的话,也不会出现C++中的歧义问题。因为所有方法都没有方法体,真正的实现还是在子类中的。
💡那么问题来了?
Java 8中支持了默认函数 (default method ),即接口中可以定义一个有方法体的方法了。
public interface Pet {
public default void eat() {
System.out.println("Pet Is Eating");
}
}
而又因为Java支持同时实现多个接口,这就相当于通过implements就可以从多个接口中继承到多人方法了,这不就是变相支持了多继承么。
那么,Java是怎么解决菱形继承问题的呢? 我们再定义一个哺乳动物接口,也定义一个eat方法。
public interface Mammal {
public default void eat() {
System.out.println("Mammal Is Eating");
}
}
然后定义一个Cat,让他分别实现两个接口:
public class Cat implements Pet,Mammal {
}
这个时间编译会报错:
error: class Cat inherits unrelated defaults for eat() from types Mammal and Pet
这个时候,就要求Cat类中,必须重写eat()。
public class Cat implements Pet,Mammal {
@Override
public void eat() {
System.out.println("Cat Is Eating");
}
}
所以可以看到,Java并没有帮我们解决多继承的歧义问题,而是把这个问题留给开发人员,通过重写方法的方式自己解决。
✅面向对象的五大基本原则?
博客 : 设计模式
五大基本原则: 单一职责原则(Single-Responsibility Principle) 、开放封闭原则(Open-Closedprinciple) 、Liskov替换原则 (Liskov-Substituion Principle) 、依赖倒置原则(Dependency-InversionPrinciple)和接门隔离原则(Interface-Segregation Principle)。
单一职责原则 : 一个类最好只做一件事
开放封闭原则 : 对扩展开放、对修改封闭
里氏替换原则 : 子类必须能够替换其基类
依赖倒置原则 : 程序要依赖于抽象接口,而不是具体的实现
接口隔离原则 : 使用多个小的专门的接口,而不要使用一个大的总接口