freeCodeCamp/guide/chinese/java/inheritance/index.md

7.2 KiB
Raw Blame History

title localeTitle
Inheritance 遗产

遗产

Java继承是指Java类从其他类inherit属性的能力。可以把它想象成一个从父母那里继承属性的孩子这个概念非常相似。在Java lingo中它也被称为_扩展_类。一些简单的事情要记住

  • 扩展或继承的称为子类
  • 正在扩展或继承的称为超类

因此继承为Java提供了_重用_代码或在类之间共享代码的强大功能

让我们用Vehicle类和Car类的经典示例来描述它:

public class Vehicle { 
    public void start() { 
        // starting the engine 
    } 
 
    public void stop() { 
        // stopping the engine 
    } 
 } 
 
 public class Car extends Vehicle { 
    int numberOfSeats = 4; 
 
    public int getNumberOfSeats() { 
        return numberOfSeats; 
    } 
 } 

在这里,我们可以看到Car类继承了Vehicle类的属性。因此,我们不必为Car的方法start()stop()编写相同的代码,因为这些属性可以从其父类或超类中获得。因此,从Car类创建的对象_也_将具有这些属性

Car tesla = new Car(); 
 
 tesla.start(); 
 
 tesla.stop(); 

:rocket: 运行代码

但是,父类是否有孩子的方法?不,它没有。

因此,每当您需要在多个类之间共享一些共同的代码时,最好有一个父类,然后在需要时扩展该类!减少代码行数,使代码模块化,并简化测试。

什么可以继承?

  • 来自父级的所有protectedpublic字段和方法

什么不能继承?

  • private领域和方法
  • 构造函数。虽然子类构造函数_必须_调用超类构造函数如果已定义稍后详细说明
  • 多个班级。 Java仅支持单继承 ,也就是说,您一次只能继承一个类。
  • 场。子类不能覆盖类的各个字段。

输入铸造和参考

在Java中可以引用子类作为其超类的_实例_ 。它被称为面向对象编程OOP中的_多态_ ,即对象采用多种形式的能力。例如,可以将Car类对象引用为Vehicle类实例,如下所示:

Vehicle car = new Car(); 

虽然相反是不可能的:

Car car = new Vehicle(); // ERROR 

:rocket: 运行代码

由于您可以将Java子类作为超类实例引用因此可以轻松地将子类对象的实例强制转换为超类实例。可以将超类对象强制转换为子类类型但_前提是该对象实际上是子类的实例_ 。所以请记住这一点:

Car car = new Car(); 
 Vehicle vehicle = car; // upcasting 
 Car car2 = (Car)vechile; //downcasting 
 
 Bike bike = new Bike(); // say Bike is also a subclass of Vehicle 
 Vehicle v = bike; // upcasting, no problem here. 
 Car car3 = (Car)bike; // Compilation Error : as bike is NOT a instance of Car 

:rocket: 运行代码

现在您知道如何通过父子关系共享代码。但是,如果你不喜欢子类中特定方法的实现并想为它编写一个新方法呢?那你怎么办呢?

覆盖它!

Java允许您_覆盖_或重新定义超类中定义的方法。例如您的Car类具有与父Vehicle不同的start()实现,因此您执行此操作:

public class Vehicle { 
    public void start() { 
      System.out.println("Vehicle start code"); 
    } 
 } 
 
 public class Car extends Vehicle { 
    public void start() { 
      System.out.println("Car start code"); 
  } 
 } 
 
 Car car = new Car(); 
 car.start(); // "Car start code" 

:rocket: 运行代码

因此覆盖子类中的方法非常简单。虽然有一个_陷阱_ 。只有具有与子类方法_完全相同的方法签名_的超类方法才会被覆盖。这意味着子类方法定义必须具有完全相同的名称相同数量和类型的参数并且具有完全相同的顺序。因此 public void start(String key)不会覆盖public void start()

备注

  • 您不能覆盖超类的私有方法。 (很明显,不是吗?)
  • 如果您在子类中重写的超类方法突然被删除或方法改变了怎么办它会在运行时失败因此Java为您提供了一个漂亮的注释@Override ,您可以将其放在子类方法上,这将警告编译器这些事件!

Java中的注释是一种很好的编码实践但它们并不是必需的。编译器足够智能可以自行解决覆盖问题。与其他OOP语言不同Java中的注释不一定会修改方法或添加额外的功能。

如何调用超类方法?

有趣的你问一下!只需使用关键字super

public class Vehicle() { 
    public void start() { 
      System.out.println("Vehicle start code"); 
    } 
 } 
 
 public class Car extends Vehicle { 
    public void run() { 
      super.start(); 
  } 
 } 
 
 Car car = new Car(); 
 car.run(); // "Vehicle start code" 

:rocket: 运行代码

注意 :尽管可以使用super调用来调用父方法,但是不能使用链式super调用来继承继承层次结构。

如何知道班级的类型?

使用instanceof关键字。拥有大量的类和子类,知道哪个类是运行时哪个类的子类会有点混乱。因此,我们可以使用instanceof来确定对象是类的实例,子类的实例还是接口的实例。

Car car = new Car(); 
 
 boolean flag = car instanceof Vehicle; // true in this case! 

构造函数和继承

如前所述构造函数不能由子类直接继承。虽然子类_需要_将其父构造函数作为其自己的构造函数中的第一个操作来调用。怎么样?你猜对了,用super

public class Vehicle { 
    public Vehicle() { 
        // constructor 
    } 
    public void start() { 
      System.out.println("Vehicle start code"); 
    } 
 } 
 
 public class Car extends Vehicle { 
    public Car() { 
      super(); 
    } 
    public void run() { 
      super.start(); 
  } 
 } 

:rocket: 运行代码

请记住,如果超类没有定义任何构造函数,则不必在子类中明确地调用它。 Java在内部为您处理在使用除_默认构造_函数之外的任何其他构造函数调用超类的情况下调用super构造_函数_ 。

如果没有定义其他构造函数那么Java将调用默认的超类构造函数 即使未明确定义 )。

恭喜现在你对继承了解一切阅读有关在Abstract Classes和Interfaces中继承事物的高级方法的更多信息!