# 您需要了解的关于接口和继承的所有信息 在本章中,我们将介绍一些重要的概念,例如接口、它们如何工作以及它们在 Java 中的用法。我们将使用一个实际的例子来讨论继承。本章还将介绍函数重载和函数重写的概念及其区别。 本章将介绍以下主题: * 接口 * 继承导论 * 函数重载 * 函数重写 # 接口 接口是 JavaOOPS 中使用的核心概念之一,因此我们有必要熟悉接口及其使用。 接口类似于类。接口和类之间的唯一区别是接口将有方法,但没有实体。困惑的?在类中,我们通常定义一个方法,然后开始向其中写入代码。例如,在一个类中,如果我们想要编写任何代码,我们只需使用`public void`声明该类,然后继续该类中的其余代码,如下所示: ```java public void getData() { } ``` 在接口中,只能定义方法的签名;我们不能在方法内部编写任何代码。但是为什么呢?在接口内编写方法签名有什么用?这个面向对象的概念在 Java 中有什么用途?您可能会想到这些问题,所以让我们试着用实际场景来理解接口的概念。 # 使用与交通灯系统的接口 考虑典型的交通灯系统,它被世界各地使用来维持交通规则。每个国家都有自己的交通规则,例如在道路的左侧或右侧驾驶。尽管各国的交通规则各不相同,但有些规则在全球范围内都适用,每个国家都需要遵守。其中一个规则是使用交通灯来控制交通流,其中红灯表示停车,琥珀色/黄色灯表示准备好发动机,绿灯表示移动车辆。假设这些全球规则是由中央交通管理局实施的,我们希望实施澳大利亚交通系统。这个系统将有自己的规则,但我们需要确保它遵循中央交通管理局制定的全球规则。 通过这个例子,我们将试图理解接口的概念。这里,中央交通管理局充当接口,澳大利亚交通规则充当实现接口的类;也就是说,澳大利亚交通系统必须遵守中央交通管理局界面中提到的规则/方法。任何接口中定义的方法都只是签名,因此类将定义并实现接口中存在的方法。让我们看看 Java 代码中的这个例子。 我们定义接口的方式与定义类的方式相同。在这个红绿灯示例中,让我们将该类命名为`CentralTraffic`。我们现在有一个现成的界面,如下所示: ```java package demopack; public interface CentralTraffic { public void greenGo(); public void redStop(); public void FlashYellow(); } ``` 我们可以在语法中看到,我们写的不是`class`,而是`interface`。我们在接口中定义方法时使用的方法与在类中定义方法时使用的方法相同,但请记住,我们不能让方法体定义该方法,因为这是一个接口,这样做会引发错误。创建另一个类来实现此接口,并将其命名为`AustralianTraffic`。一旦我们有了一个 Java 类,我们需要实现它的`CentralTraffic`接口,我们使用`implements`关键字来实现,如下所示: ```java public class AustralianTraffic implements CentralTraffic { ``` 在使用前一句话之后,我们的 IDE 将显示一个错误,当您将鼠标悬停在错误上方时,您将看到一些与错误相关的建议。一个建议是导入`CentralTraffic`,另一个建议是添加未实现的方法。单击这些建议以解决错误,您将得到以下代码: ```java package coreJava; import demopack.CentralTraffic; public class AustralianTraffic implements CentralTraffic { public static void main(String[] args) { } @Override public void greenGo() { // TODO Auto-generated method stub System.out.println(" greengo implementation") } @Override public void redStop() { // TODO Auto-generated method stub System.out.println(" redstop implementation") } @Override public void FlashingYellow() { // TODO Auto-generated method stub System.out.println(" flash yellow implementation") } } ``` `CentralTraffic`接口中定义的所有方法都可以在`AustralianTraffic`类中看到,在这里我们也可以按照自己的意愿实现这些方法。现在,如果我们从 Java 类中删除`greenGo`方法,它会给我们一个错误。由于它是在接口中定义的方法,我们必须实现接口中定义的所有方法。 接口方法是在`public static void main`之外定义的,要执行这些方法,我们应该在`main`方法中为它们创建一个类对象,如下所示: ```java CentralTraffic a= new AustralianTraffic(); ``` 这行代码表示我们已经为`AustralianTraffic`类创建了一个对象来实现`CentralTraffic`接口中的方法。主类应如下所示: ```java public class AustralianTraffic implements CentralTraffic { public static void main(String[] args) { CentralTraffic a= new AustralianTraffic(); a.redStop(); a.FlashYellow(); a.greenGo(); } ``` 现在,在从接口实现方法之后,我们可以在 Java 类中定义我们自己的特定于国家的方法(规则),如下所示: ```java public void walkonsymbol() { System.out.println("walking"); } ``` 在我们的`main`方法中,如果我们尝试使用`a.`调用我们国家特有的方法,就像我们在`main`类中调用其他方法一样,那么我们将发现我们无法这样做,因为`walkonsymbol`方法是特定于某个特定国家(即`AustralianTraffic`类)的,并且它没有在`CentralTraffic`中实现。对于`walkonsymbol`方法,我们需要在`main`类中针对`AustralianTraffic`类创建另一个对象,如下所示: ```java AustralianTraffic at=new AustralianTraffic(); at.walkonsymbol(); ``` 与接口相关的另一条信息是,一个类可以实现多个接口。假设我们创建了另一个接口,比如`ContinentalTraffic`,并定义了另一个与交通灯相关的规则,比如火车符号,以指示火车正在经过。我们可以通过添加逗号在`AustralianTraffic`类中实现此接口,如下所示: ```java public class AustralianTraffic implements CentralTraffic, ContinentalTraffic { ``` 对于这个接口,我们需要遵循与`CentralTraffic`接口相同的步骤,例如将`ContinentalTraffic`导入`AustralianTraffic`,添加未实现的方法,在主类中创建一个特定于`ContinentalTraffic`的对象,等等。 现在您对接口和类之间的区别有了一个大致的了解。我们学习了如何定义接口,如何在另一个类中实现它们,以及如何使用对象调用它们。 # 遗产 继承是 Java 中另一个重要的 OOP 概念。让我们以车辆为例来理解继承的概念,就像我们使用交通灯系统来理解接口一样。车辆的基本特性是颜色、档位、后视镜、制动器等。比如说,我们正在制造一款新车,该车在某些性能方面有一定的改进,比如发动机的 CC 更高,可能设计与旧款不同。现在,要创建具有这些新功能的新车辆,我们仍然需要旧车辆的基本功能,如后视镜和制动器,这些功能默认存在于车辆中 让我们以前面的示例为例,使用 Java 来反映这些关系,以便理解继承的概念。在我们的示例中,如果我们有一个车辆类,并输入该车辆的基本特征作为该类中存在的方法,那么当我们为新车辆创建一个类时,它可以继承为该车辆创建的类的特征,我们不必为这些特性编写代码,因为它们可以通过继承提供给我们。 让我们从代码开始。创建一个`parentClassdemo`类,它将是我们的父类。在这个类中,我们将定义我们的方法,如下所示: ```java package coreJava; public class parentClassdemo { String color = "red"; public void Gear() { System.out.println("gear code is implemented"); } public void Brakes() { System.out.println("brakes code is implemented"); } public void audiosystem() { System.out.println("audiosystem code is implemented"); } } ``` 现在,我们将在子类中继承这些方法。用 Java 创建一个`childClassDemo`。我们使用`extends`关键字继承父类,如下所示: ```java package coreJava; public class childClassDemo extends parentClassdemo { public void engine() { System.out.println("new engine"); } public void color { System.out.println(color); } public static void main(String[] args) { childClassDemo cd=new childClassDemo(); cd.color(); } ``` 在这里,我们使用`extends`关键字继承了`childClassDemo`类中的`parentClassdemo`类。在这个`childClassDemo`类中,我们定义了自己的`engine`方法,并使用了`color`方法,该方法是从`parentClassdemo`类继承的。然后我们创建了一个`cd`对象,并使用它从继承的类中调用方法 # 更多关于继承的信息 让我们讨论一些关于 Java 继承的臭名昭著的棘手问题和误解 让我们从一些关于继承的更著名的问题开始。请看下面的代码块: ```java class X { //Class X members } class Y { //Class Y members } class Z extends X, Y { //Class Z members } ``` 在前面的代码片段中,我们有`X`和`Y`类以及其中的一些数据字段或方法。`Z`类继承`X`和`Y`类。允许这样做吗?答案是否定的。Java 不允许多个继承,而 C++ 则允许。因此,在这里,我们可以得出结论,前面的代码片段是不正确的,将抛出一个错误。 这也是继承和接口之间的区别之一,因为接口允许我们一次使用多个接口。 请看以下示例: ```java class A { int i = 10; } class B extends A { int i = 20; } public class MainClass { public static void main(String[] args) { A a = new B(); System.out.println(a.i); } } ``` 这里,我们有一个`A`类,它有一个`i`变量。还有一个`B`类扩展了`A`类,我们还将其局部`i`变量设置为`20`。现在,在`MainClass`中,我们为`B`类创建了一个对象。这一步实际上意味着什么?这里,我们正在创建一个对象,并说这个`B`类的对象应该引用`A`类的属性。虽然我们有权通过这个`a`对象访问这个`B`类,但我们只能访问`A`类的属性或方法,因为`B`类在这里有权访问`A`类,因为我们正在扩展它。 这里的问题是`a.i`将打印什么?`20`或`10`?答案是,它将打印`10`的变量值,因为`A a = new B();`明确告诉`a`它是`B`类的对象,但我们需要访问`A`类中存在的方法。如果我们希望此输出为`20`,我们将语法更改为`B a = new B();` 如果你参加 Java 测验或复杂的面试,你可能会遇到这样的问题。这些是您必须了解的有关继承的重要信息,您可以据此进行规划。 # 函数重载 当一个类有多个同名方法时,就会发生函数重载。如果我们在类中定义了两次`getData`方法,可以说`getData`函数重载,如下代码所示: ```java package coreJava; //function overloading public class childlevel extends childClassDemo { public void getData(int a) { } public void getData(String a) { } public static void main(String[] args) { childlevel cl=new childlevel(); cl.getData(2); cl.getData("hello") } } ``` 在使用同名函数的多个实例时,我们需要记住一些规则。第一条规则是函数重载方法中存在的参数数量应该不同,第二条规则是参数数据类型应该不同。如果我们将两个`getData`方法都保留为`int a`参数,它将抛出一个错误,因此我们需要为每个方法提供不同数量的参数。现在,当您打印这些时,您将获得`2`和`hello`的输出。我们可以看到打印了两个不同的参数,但方法名相同。让我们再添加一个带有两个参数的`getData`实例,如下所示: ```java public void getData(int a, int b) { } ``` 我们现在有两个具有相同数据类型的`getData`实例,但参数的数量不同 在现实世界中,您可能还会遇到函数重载,例如,当您在修补程序中被要求在电子商务网站中使用支付方法时。网站可能会使用不同的`getPayment`方法来确认付款,一种`getPayment`方法以借记卡为参数,另一种`getPayment`方法以信用卡为参数,另一种`getPayment`方法可能以礼品卡为参数。因此,我们将不同类型的参数传递给相同的`getPayment`方法。在本例中,我们坚持使用`getPayment`作为方法名,并将不同的参数传递给它,从而将函数重载的概念引入到这个特定场景中。 # 函数重写 在本节中,让我们讨论 Java 中的另一个重要特性是函数重写。让我们继续学习学习继承时看到的相同示例。 在该示例中,我们有一个名为`parentClassdemo`的父类和一个名为`childClassDemo`的子类,子类继承了父类,如下所示: ```java package coreJava; public class childClassDemo extends parentClassdemo { public void engine() { System.out.println("new engine"); } public static void main(String[] args) { childClassDemo cd=new childClassDemo(); cd.color(); } ``` 在这里,我们在子类中定义了`engine`方法,它打印一个新引擎,我们还有另一个方法`color`,它在父类中定义,我们使用一个对象来调用它。如果我们打印它,我们将得到`color`方法的输出,正如它在父类中定义的那样。现在,我们在子类中创建一个新方法,并将其命名为`color`,定义如下: ```java public void color() { System.out.println("update color"); } ``` 我们有两个`color`方法的实例,一个在父类中定义,另一个在子类中定义。这里是函数重写概念开始发挥作用的地方。如果您运行子类,您将得到`update color`的输出。这是因为子类中定义的新的`color`方法覆盖了父类中的`color`方法。 这总结了函数重写的整个概念,其中两个方法具有相同的名称、签名和参数。在函数重载中,我们有名称相同但参数不同的方法。这是函数重载和函数重写之间的主要区别之一。 # 总结 在本章中,我们介绍了一些重要的 JavaOOP 概念,如接口、继承、函数重载和函数重写。我们通过一个例子来了解每个概念,这有助于我们更好地详细理解这些概念。 在下一章中,我们将了解 Java 代码中最重要的概念之一:数组。我们将看到不同数组的外观,以及如何初始化和显示它们。