
✅如何理解面向对象和面向过程?
- 典型理解
- ✅扩展知识仓
- ✅面向对象的三大基本特征
- ✅封装
- ✅继承
- ✅多态
- ✅为什么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 {@Overridepublic void eat() {System.out.println("Cat Is Eating");}
}
所以可以看到,Java并没有帮我们解决多继承的歧义问题,而是把这个问题留给开发人员,通过重写方法的方式自己解决。
✅面向对象的五大基本原则?
博客 : 设计模式
五大基本原则: 单一职责原则(Single-Responsibility Principle) 、开放封闭原则(Open-Closedprinciple) 、Liskov替换原则 (Liskov-Substituion Principle) 、依赖倒置原则(Dependency-InversionPrinciple)和接门隔离原则(Interface-Segregation Principle)。
单一职责原则 : 一个类最好只做一件事
开放封闭原则 : 对扩展开放、对修改封闭
里氏替换原则 : 子类必须能够替换其基类
依赖倒置原则 : 程序要依赖于抽象接口,而不是具体的实现
接口隔离原则 : 使用多个小的专门的接口,而不要使用一个大的总接口