JavaRush /Java 博客 /Random-ZH /面向对象原则

面向对象原则

已在 Random-ZH 群组中发布
Java 是一种面向对象的语言。这意味着您需要使用面向对象的风格来编写 Java 程序。而这种风格是基于程序中对象和类的使用。

面向对象编程的基本原则:

面向对象编程原理 - 1让我们尝试借助示例来了解什么是类和对象,以及如何在实践中应用 OOP 的基本原则:抽象、继承、多态性和封装。

什么是对象?

我们生活的世界是由物体组成的。如果我们环顾四周,我们会发现我们周围都是房屋、树木、汽车、家具、餐具、电脑。所有这些项目都是对象,每个项目都有一组特定的特征、行为和目的。我们已经习惯了物体,并且总是将它们用于非常特定的目的。例如,如果我们需要上班,我们需要汽车;如果我们想吃饭,我们需要餐具;如果我们需要放松,我们需要一张舒适的沙发。一个人习惯于客观地思考来解决日常生活中的问题。这是在编程中使用对象的原因之一,这种创建程序的方法称为面向对象。让我们举个例子。想象一下,您开发了一款新手机型号,并希望开始批量生产。作为一名手机设计师,您知道它的用途、功能以及由哪些部分组成(外壳、麦克风、扬声器、电线、按钮等)。然而,只有您知道如何连接这些部件。然而,您并不打算亲自生产手机;为此,您拥有一大批员工。这样您就不必每次都解释如何连接手机的各个部件,并且使生产中的所有手机都一样,在开始生产之前,您需要以电话结构的描述。在 OOP 中,这样的描述、绘图、图表或模板称为类,在执行程序时从中创建对象。 类是对尚未创建的对象的描述,就像由字段、方法和构造函数组成的通用模板,而对象是根据此描述创建的类的实例。

面向对象抽象

现在让我们考虑如何从现实世界中的对象移动到程序中的对象,以手机为例。这种通信方式的历史已超过 100 年,现代电话与其 19 世纪的前身不同,是一种复杂得多的设备。当我们使用手机时,我们不会考虑它的结构和内部发生的过程。我们只需使用手机开发商提供的功能——按钮或触摸屏来选择号码并拨打电话。最早的电话接口之一是旋钮,转动旋钮即可拨打电话。当然,这不太方便。尽管如此,手柄还是正确地发挥了其功能。如果您查看最现代的第一部电话,您可以立即识别出最重要的细节,这些细节对于 19 世纪末的设备和超现代的智能手机都很重要。这是拨打电话(拨打号码)和接听电话。从本质上讲,这就是手机之所以成为手机而不是其他东西的原因。现在我们已经应用了 OOP 中的原则 - 突出显示对象最重要的特征和信息。OOP 的这一原则称为抽象。 OOP 中的抽象也可以定义为将现实世界问题的元素表示为程序中的对象的一种方式。抽象总是与有关对象或对象的属性的某些信息的概括相关联,因此主要是在要解决的问题的背景下将重要信息与无关紧要信息分开。在这种情况下,可以有多个抽象级别。让我们尝试将抽象原则应用到我们的手机上。首先,让我们重点介绍从最初到现在最常见的手机类型。例如,它们可以用图 1 所示的图表形式表示。 面向对象编程原理 - 2现在,借助抽象,我们可以突出显示此对象层次结构中的一般信息:对象的常见抽象类型 - 电话、电话的一般特征电话 - 其创建年份和通用界面 - 所有电话都能够接听和发送电话。在 Java 中它是这样的:

public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outputNumber);
    public abstract void ring (int inputNumber);
}
基于这个抽象类,我们将能够使用其他基本的 Java OOP 原则在程序中创建新类型的手机,我们将在下面考虑。

封装

在抽象的帮助下,我们突出显示所有对象的共同点。然而,每款手机型号都是独一无二的,并且与其他手机型号有所不同。我们如何在程序中划定界限并指定这种个性?我们如何确保用户不会意外或故意损坏我们的手机,或尝试将一种型号转换为另一种型号?对于真实物体的世界,答案是显而易见的:你需要将所有部件放入手机机身中。毕竟,如果我们不这样做,并将手机的所有内部和连接它们的电线留在外面,肯定会有好奇的实验者想要“改进”我们手机的操作。为了避免对对象的设计和操作的这种干扰,OOP使用了封装原则——OOP的另一个基本原则,其中对象的属性和行为被组合在一个类中,对象的内部实现是隐藏的。用户,并提供一个开放的接口来处理该对象。程序员的工作是确定哪些属性和方法可以公开访问,哪些是对象的内部实现并且不应修改。

封装和访问控制

假设在生产过程中,有关它的信息被刻在手机背面:其制造年份或制造商公司的徽标。这些信息非常具体地描述了该模型的特征——它的状况。我们可以说,手机开发人员考虑到了这些信息的不变性——不太可能有人会想到删除雕刻。在 Java 世界中,未来对象的状态是使用字段在类中描述的,并且它们的行为是使用方法来描述的。更改状态和行为的能力是通过使用字段和方法的访问修饰符 - privateprotectedpublicdefault(默认访问)来实现的。例如,我们决定创建年份、手机制造商的名称以及其中一个方法属于类的内部实现,不能被程序中的其他对象更改。使用代码,该类可以描述如下:

public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    //findComutator
    //openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("I'm calling a number");
}

public void ring() {
    System.out.println("Дзынь-дзынь");
}

 }
修饰符private使类的字段和方法仅在该类中可用。这意味着private不能从外部访问字段,也不能private调用方法。隐藏对方法的访问openConnection还使我们有机会自由更改该方法的内部实现,因为该方法保证不会被其他对象使用,也不会中断它们的操作。为了使用我们的对象,我们call使用ring修饰符将方法保持打开状态public提供用于处理对象的公共方法也是封装机制的一部分,因为如果完全拒绝对对象的访问,它将变得毫无用处。

遗产

我们再看一下手机图表。您可以看到它代表了一个层次结构,其中位于下方的模型具有位于分支上的较高模型的所有特征,以及它自己的特征。例如,智能手机使用蜂窝网络进行通信(具有手机的属性),是无线且便携的(具有无绳电话的属性),并且可以接听和拨打电话(具有电话的属性)。既然这样,我们就可以谈谈对象属性的继承了。 在编程中,继承是使用现有的类来定义新的类。 让我们看一个使用继承创建智能手机类的示例。所有无绳电话均由可充电电池供电,具有一定的使用寿命(以小时为单位)。因此,让我们将此属性添加到无线电话类中:

public abstract class WirelessPhone extends AbstractPhone {

    private int hour;

    public WirelessPhone(int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
手机继承了无线电话的属性,我们还在这个类中添加了call和方法的实现ring

public class CellPhone extends WirelessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outputNumber) {
        System.out.println("Calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("A subscriber is calling you" + inputNumber);
    }
}
最后是智能手机类别,与传统手机不同,它拥有成熟的操作系统。您可以将该操作系统支持的新程序添加到您的智能手机,从而扩展其功能。使用代码,该类可以描述如下:

public class Smartphone extends CellPhone {
    
    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program){
    System.out.println("Installing" + program + "For" + operationSystem);
}

}
正如您所看到的,Smartphone我们创建了很少的新代码来描述该类,但我们得到了一个具有新功能的新类。使用OOP继承原理可以显着减少代码量,从而使程序员的工作更加轻松。

多态性

如果我们观察所有手机型号,那么,尽管这些型号的外观和设计存在差异,但我们可以识别它们的一些共同行为 - 它们都可以接听和拨打电话,并且具有一组相当清晰和简单的控制按钮。应用我们已经知道的面向对象编程的基本原则之一,即编程术语中的抽象,我们可以说电话对象有一个公共接口。因此,手机用户可以使用相同的控制按钮(机械或触摸)轻松使用不同的型号,而无需了解设备的技术细节。因此,您经常使用手机,并且可以轻松地通过固定电话拨打电话。OOP 中程序可以使用具有相同接口的对象而无需了解对象内部结构的信息的原理称为多态性。让我们想象一下,在我们的程序中,我们需要描述一个可以使用任何电话型号呼叫另一个用户的用户。操作方法如下:

public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// here it is polymorphism - using the abstract type AbstractPhone phone in the code!
        phone.call(number);
    }
}
 }
现在让我们描述一下不同的手机型号。第一批手机型号之一:

public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outputNumber) {
        System.out.println("Turn the Handle");
        System.out.println("Give me the phone number, sir");
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
普通固定电话:

public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outputNumber) {
        System.out.println("I'm calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
最后,一个很酷的视频电话:

public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outputNumber) {
        System.out.println("I connect a video channel for the subscriber" + outputNumber );
    }
    @Override
    public void ring(int inputNumber) {
        System.out.println("You have an incoming video call..." + inputNumber);
    }
  }
让我们在方法中创建对象main()并测试该方法callAnotherUser

AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Andrey");
user.callAnotherUser(224466,firstPhone);
// Rotate the knob
// Tell me the number of the subscriber, sir
user.callAnotherUser(224466,phone);
//Call number 224466
user.callAnotherUser(224466,videoPhone);
//I connect the video channel for subscriber 224466
通过在对象上调用相同的方法user,我们得到了不同的结果。call方法内特定方法实现的选择callAnotherUser是根据程序执行期间调用对象的特定类型动态进行的。这就是多态性的主要优点——程序执行过程中实现的选择。在上面的电话类示例中,我们使用了方法重写,这是一种在不更改方法签名的情况下更改基类中定义的方法实现的技术。这本质上是方法替换,程序运行时调用的是子类中定义的新方法。通常,在重写方法时,会使用注释@Override,它告诉编译器检查被重写和重写方法的签名。 因此,为了确保您的程序风格符合 OOP 概念和 OOP java 原则,请遵循以下提示:
  • 突出物体的主要特征;
  • 创建对象时突出显示公共属性和行为并使用继承;
  • 使用抽象类型来描述对象;
  • 尝试始终隐藏与类的内部实现相关的方法和字段。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION