来源:The Geek Asian 让我们看一下面向对象编程的四个基本原理,并尝试了解它们是如何工作的。面向对象编程(OOP)是主要的编程范式之一。它可以很简单,也可以相反,非常复杂。这完全取决于您决定如何开发应用程序。 OOP 有 4 个支柱:
- 封装。
- 遗产。
- 抽象。
- 多态性。
1. 封装
我们都研究过封装,即隐藏数据元素并允许用户使用公共方法访问数据。我们称这些为 getter 和 setter。现在让我们忘记这一点并找到一个更简单的定义。封装是一种限制用户直接更改数据成员或类变量以维护数据完整性的方法。我们如何做到这一点?我们通过将访问修饰符切换为私有并公开可用于访问数据的公共方法来限制对变量的访问。下面我们看一下具体的例子。这将帮助我们理解如何使用封装来维护数据完整性。不带封装:/**
* @author thegeekyasian.com
*/
public class Account {
public double balance;
public static void main(String[] args) {
Account theGeekyAsianAccount = new Account();
theGeekyAsianAccount.balance = -54;
}
}
在上面的代码片段中,main()方法直接访问balance变量。这允许用户将任何双精度值设置为Account类的余额变量。如果允许任何人将余额设置为任何无效数字(例如本例中的 -54),我们可能会失去数据完整性。带封装:
/**
* @author thegeekyasian.com
*/
public class Account {
private double balance;
public void setBalance(double balance) {
if(balance >= 0) { // Validating input data in order to maintain data integrity
this.balance = balance;
}
throw new IllegalArgumentException("Balance cannot be less than zero (0)");
}
public static void main(String[] args) {
Account theGeekyAsianAccount = new Account();
theGeekyAsianAccount.setBalance(1); // Valid input - Allowed
theGeekyAsianAccount.setBalance(-55); // Stops user and throws exception
}
}
在此代码中,我们限制了对balance变量的访问,并添加了一个setBalance()方法,允许用户设置Account的余额值。setter 在将提供的值分配给变量之前检查它。如果该值小于零,则会引发异常。这确保了数据的完整性不会受到损害。在解释了上面的例子之后,我希望封装作为 OOP 四大支柱之一的价值是显而易见的。
2. 继承
继承是一种获取具有共同特征的另一个类的属性的方法。这使我们能够提高可重用性并减少代码重复。当子元素继承其父元素的属性时,该方法还具有子父交互的原理。让我们深入研究两个简单的示例,看看继承如何使代码更简单、更可重用。没有继承:/**
* @author thegeekyasian
*/
public class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
}
public class Square {
private int width; // Duplicate property, also used in class Rectangle
public Square(int width) {
this.width = width;
}
public int getArea() { // Duplicate method, similar to the class Rectangle
return this.width * this.width;
}
}
这两个相似的类共享width属性和getArea()方法。我们可以通过进行一些重构来增加代码重用,其中Square类最终继承自Rectangle类。具有继承性:
/**
* @author thegeekyasian
*/
public class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
}
public class Square extends Rectangle {
public Square(int width) {
super(width, width); // A rectangle with the same height as width is a square
}
}
通过简单地扩展Rectangle类,我们得到了Square类作为Rectangle类型。这意味着它继承了Square和Rectangle共有的所有属性。在上面的示例中,我们看到继承如何在使代码可重用方面发挥重要作用。它还允许类继承其父类的行为。
3. 抽象
抽象是一种通过隐藏对象不必要或不相关的细节来仅向用户呈现基本细节的技术。它有助于降低用户侧的操作复杂性。抽象允许我们向用户提供一个简单的界面,而不需要复杂的细节来执行操作。简而言之,它使用户能够驾驶汽车,而无需他们确切了解发动机的工作原理。让我们先看一个例子,然后讨论抽象如何帮助我们。/**
* @author thegeekyasian.com
*/
public class Car {
public void lock() {}
public void unlock() {}
public void startCar() {
checkFuel();
checkBattery();
whatHappensWhenTheCarStarts();
}
private void checkFuel() {
// Check fuel level
}
private void checkBattery() {
// Check car battery
}
private void whatHappensWhenTheCarStarts() {
// Magic happens here
}
}
在上面的代码中, lock()、unlock()和startCar() 方法是公共的,其余方法是类的私有方法。我们让用户“驾驶汽车”变得更加容易。当然,他可以在使用 startCar()启动汽车之前手动检查checkFuel()和checkBattery(),但这只会使过程变得复杂。通过上面的代码,用户所需要做的就是使用startCar(),该类将处理其余的事情。这就是我们所说的抽象。
4、多态性
OOP 四大支柱中的最后一个也是最重要的是多态性。多态性意味着“多种形式”。顾名思义,它是一个允许您以多种或不同方式执行操作的函数。当我们谈论多态性时,除非我们谈论它的类型,否则没有太多可讨论的。多态性有两种类型:- 方法重载——静态多态(Static Binding)。
- 方法重写——动态多态(动态绑定)。
方法重载-静态多态:
方法重载或静态多态性,也称为静态绑定或编译时绑定,是一种在编译时确定方法调用的类型。方法重载允许我们拥有多个具有相同名称、不同参数数据类型或不同数量参数或两者兼而有之的方法。但问题是,为什么方法重载(或静态多态性)有用?让我们看一下下面的示例,以更好地理解方法重载。没有方法重载:/**
* @author thegeekyasian.com
*/
public class Number {
public void sumInt(int a, int b) {
System.out.println("Sum: " + (a + b));
}
public void sumDouble(double a, double b) {
System.out.println("Sum: " + (a + b));
}
public static void main(String[] args) {
Number number = new Number();
number.sumInt(1, 2);
number.sumDouble(1.8, 2.5);
}
}
在上面的示例中,我们创建了两个具有不同名称的方法,只是为了将两种不同类型的数字相加。如果我们继续进行类似的实现,我们将有多个具有不同名称的方法。这将降低代码的质量和可用性。为了改善这一点,我们可以通过对不同的方法使用相同的名称来使用方法重载。这将允许用户有一个选项作为对不同类型的数字求和的入口点。当两个或多个方法具有相同名称但不同参数时,方法重载就会起作用。返回类型可以相同或不同。但是如果两个方法具有相同的名称、相同的参数,但返回类型不同,那么这将导致重载和编译错误!使用方法重载:
/**
* @author thegeekyasian.com
*/
public class Number {
public void sum(int a, int b) {
System.out.println("Sum: " + (a + b));
}
public void sum(double a, double b) {
System.out.println("Sum: " + (a + b));
}
public static void main(String[] args) {
Number number = new Number();
number.sum(1, 2);
number.sum(1.8, 2.5);
}
}
在相同的代码中,通过一些小的更改,我们能够重载这两个方法,使两个方法的名称相同。用户现在可以将其特定数据类型指定为方法参数。然后它将根据所提供的数据类型执行操作。此方法绑定是在编译时完成的,因为编译器知道将使用指定的参数类型调用哪个方法。这就是为什么我们称其为编译时绑定。
方法重写 - 动态多态性:
与方法重载不同,方法重写允许您具有与多个方法完全相同的签名,但它们必须位于多个不同的类中。问题是,它有什么特别之处?这些类具有 IS-A 关系,即它们必须相互继承。换句话说,在方法重写或动态多态中,方法在运行时被调用时动态处理。这是基于对其初始化的对象的引用来完成的。这是方法重写的一个小例子:/**
* @author thegeekyasian.com
*/
public class Animal {
public void walk() {
System.out.println("Animal walks");
}
}
public class Cat extends Animal {
@Override
public void walk() {
System.out.println("Cat walks");
}
}
public class Dog extends Animal {
@Override
public void walk() {
System.out.println("Dog walks");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.walk(); // Animal walks
Cat cat = new Cat();
cat.walk(); // Cat walks
Dog dog = new Dog();
dog.walk(); // Dog walks
Animal animalCat = new Cat(); // Dynamic Polymorphism
animalCat.walk(); // Cat walks
Animal animalDog = new Dog(); // Dynamic Polymorphism
animalDog.walk(); //Dog walks
}
}
在这个最重要的示例中,我们动态地将“Dog”和“Cat”类型的对象分配给“Animal”类型。这允许我们在运行时动态地调用引用实例的walk()方法。我们可以使用方法重写(或动态多态性)来做到这一点。我们对 OOP 四大支柱的简短讨论到此结束,我希望您发现它有用。
GO TO FULL VERSION