JAVA基本语法6-继承/重写/equals()方法/super/final

继承的起源

在面向对象编程中,可以通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类,这种方式称为继承(Inheritance)。

已有的类称为父类,而新类称为子类。父类也可以称为基类、超类,子类也可以称为派生类。这些术语可以互换,但是意思是相同的。

在面向对象编程中,为避免上面所出现的问题,我们需要根据一个简单的规则不断地测试我们的设计:一个对象“有(has a)”一个属性,一个对象“执行(does)”一个行为。

在面向对象编程中,当两个或多个类是不相同的,但是共享相同特征的时候,将共同的元素从类中拿出来,放到一个父类中。这些类继承父类,因此就继承了所有父类的特征,同时,每个子类中又保留了不同的特征。使用继承不仅可以重用已有的代码,从而避免代码重复,还可以创建一个更容易维护和修改代码的程序

调用子类的时候,先在堆中划分父类的存贮空间,再在这个空间叠加子类的存储空间,构成了一个完整的地址空间。

继承是类与类的继承,不是对象与对象的继承。

JAVA基本语法6-继承/重写/equals()方法/super/final_第1张图片

使用is-a关系判断继承

“is a”关系是一个简单而强大的规则,用于判断父类和子类的继承关系是否正确。当使用继承时,我们必须能够说子类“ is a(是一个)” 父类。如果这个语句是真的,那么继承关系就是对的。

例如,“一个拿年薪的员工是一个员工”是真的。

Java类中继承的实现

在Java中,一个类使用关键字extends继承其它类。关键字extends出现在类声明时的类名后,extends后面跟着的是要继承的类的名称。

例如,下面的语句用于声明Salary类是Employee类的子类:

 

public class Salary extends Employee

实例化子对象

  • Java中有向上转型的原则,子类可以转向父类例如:Employee employee = new Salary();

Salary类是Employee类的子类)

  • 子类可以用父类中公共的属性和行为
  • 父类不能用子类中特有的行为和属性
  • 通过new来创建对象
  • 并非先创建父类对象,再创建子类对象
  • 只创建了一个对象,先给父类的属性分配空间,然后再叠加子类的私有属性

单继承与多继承

某些OOP语言(例如C++)允许子类有多个父类。但是,在Java中这是不允许的。在Java中,一个类只能有一个父类。多继承在Java中是不允许的。Java语言的目标之一是创建一个易于使用和理解的面向对象编程语言。而多继承只能给编程语言带来混淆。

一个Java类只能有一个父类。但是这并不意味着一个类没有祖父、曾祖父等等。一个Java类可以有父类、父类也可以父类,依此类推。

所有的根类Object

Java语言API中包含了一个名为Object的特殊类,它是整个Java类层次中的根类。Object类在java.lang包中,是每个Java类的父类,要么是直接的父类,要么就是间接父类。

如果我们编写了一个类,并且没有显式地继承另一个类,那么编译器就会在我们的类声明中添加“extends Object”。如果我们编写的类继承了除Object以外的其它类,那么该类仍然是Object的子类,因为最终它的上代还是要继承Object类。

方法重写

子类可以重写从父类继承的方法,从而允许子类添加或者改变父类中方法的行为。这称为方法重写,是OOP的特征之一。

当子类重写父类的方法时,必须遵循如下的规则:

  • 子类的方法的返回值的类型、方法名和形式参数列表,必须和父类中的是相同的。如果子类中的方法与父类中的方法同名,但是子类的方法改变了形式参数列表,那么这是方法重载,而不是方法重写。不要混淆这两个概念,二者的用法是完全不同的。
  • 访问修饰符必须不小于父类中的访问修饰符。例如,如果父类的方法是public,那么子类的必须是public。如果父类的方法是protected,那么子类必须是protected或public(public比protected访问权更大)。
  • 子类中重写的异常不能抛出比父类更多的异常,其原因我们将在后面异常处理一章中讲解。

Object类中的toString()方法是声明为public的,因此,在Radio中的toString()方法必须声明为public。

JAVA基本语法6-继承/重写/equals()方法/super/final_第2张图片

Equals()方法

Object类有一个用于判断两个对象是否相等的equals()方法。我们编写的每一个类应该覆盖equals()方法,以让类的用户判断什么时候类的实例是相等的。

一个必须遵循的常用规则就是:如果两个对象是相等的,那么它们必须产生相同的哈希码。因此,如果一个类重写了equals()方法,通常也需要重写hashCode()方法。

equals()方法比较两个对象,测试二者是否相等。比较运算符“==”用于检测是否两个引用指向同一对象,这种比较方法是与equals()完全不同的。如果e1和e2指向不同的对象,那么不管e1是否与e2相等,表达式e1==e2的计算结果都是false。

  • ==两边如果是基本数据类型,直接比较两个的值是否相等,如果两边的数据类型是引用类型,那么比较是引用指向的对象是否是同一个对象
  • String引用中的equals()方法进行了重写,比较的是该引用最终指向的那个字符串具体值
  • 其他的类还是引用的Object中的equals(),比较的是引用指向的对象是否是同一个对象
  • 如果要在自己定义的类中定义比较的方式,可以选择重写equals()方法,如果不重写该方法,我们调用的依旧是继承了Object的equals()方法       (比如说:我定义了多个学生卡,而我现在认为只要卡里面的姓名相同就是同一个学生的卡,那么我们就可以重写equals()方法,只去比较姓名就可以了,那么现在这个学生卡类的equals()方法赋予的意义就是判断两张卡是否拥有者是同一个人)

super关键字

在前面,我们演示了子类如何通过方法重写,完全改变继承自父类的方法。有时,子类想要给继承的方法添加行为,但是并不是完全替换父类的方法。在这种情况下,可以使用关键字super来调用父类中重写的方法。

我们已经知道,每个对象都有一个对自身的引用,称为this引用。当在一个类中,引用类本身的成员变量或方法时,可以显式地使用this引用。类似地,一个类可以使用关键字super,来显式地引用从父类继承的成员变量或方法。我们可以把super看作是子对象对父对象的引用。

  • Super只能访问本类继承来自于父亲的那部分财产,this可以访问除了本类继承来自于父亲的那部分财产,也可以访问本类的属性。
  1. Super()指的是调用父类的构造,this()调用的是本类的其他构造,在一个构造函数中不能同时调用本类其他构造又调用父类构造。
  2. super()必须写在构造函数第一行。
  3. 如果没有显示的写super(),编译器会自动默认的添加进子类的构造函数的第一行。

Final关键字

  • final修饰变量就是声明一个常量,符号常量在被赋值后就不能被改变。
  • final类。一个类可以声明为最终类的。最终类不能被有子类。
  • final方法。一个方法可以声明为最终的。最终方法不能被重写。

final类

有时候,我们希望一个类成为最终类,不能被其它类继承,也就是不能有子类。在这种情况下,我们就需要在声明类时,在关键字class前加上关键字final,让该类成为一个最终类。例如:

 

public final class Hourly extends Employee{

//类定义...

}

 

final方法

当子类重写父类的一个方法时,父类中被重写的方法实质上被隐藏了。在子类中,要调用父类中的方法,唯一的方法是显式地调用。如果我们编写的方法有中很重要的行为,我们不想子类重写,可以将该方法声明为最终方法。最终方法不能被子类重写。

 

实例化过程

子类对象是对父类的扩展。当子类被实例化时,父类对象需要先被构造。更特别地,父类的构造器必须在子类的构造器执行之前执行。此外,如果子类有祖父类,那么祖父对象需要先构造。这个过程一直向继承树继续传递。

当一个对象被实例化时,发生下列事件序列:

  1. new运算符调用子类的构造器。
  2. 子类会用this关键字调用子类的其它构造器。最后,子类中的第一行代码中不是this()的构造器将被调用。
  3. 在子类的构造器中的任何语句执行前,父类的构造器必须用super关键字调用。如果没有显式地使用super调用,那么编译器会用super()调用无参数的父类构造器。
  4. 如果父类也是另一个类的子类,那么在父类的构造器中的任何语句执行前,父类构造器必须使用super调用更高一层父类的构造器。
  5. 本过程继续执行,直到到达类层次树的顶部,即Object类。
  6. Object类中的构造器执行,然后控制流程转到继承层次树中Object下面的类的构造器中。
  7. 构造器沿着继承层次树向下执行。最后一个要执行的构造器,实际上是第一步中先调用的那个构造器。

调用父类构造函数

关键字super用于调用父类的构造器。如果构造器没有显式地使用super关键字,那么编译器会自动添加一个无参数的super()来调用父类构造器。但是,如果父类没有无参数的构造器会发生什么呢?在这种情况下,调用空括号的super()不会通过编译。因此,子类构造器需要显式地调用父类构造器,并将恰当的实际参数传递给父类的构造器。

总结

本章的主要内容总结如下:

  • 继承是面向对象编程最重要的特征之一。它允许对一个已经存在的类进行扩展,从而编写一个新的类。新类继承了父类所有属性和行为。
  • "is a"关系是一个简单但是重要的用于判断一个继承是否设计良好的步骤。如果两个类存在继承关系,那么我们必须能够说子对象"is a"父对象。
  • 关键字extends用于实现继承。Java中的类只能继承一个类。
  • 如果一个类没有显式地继承另一个类,那么它的父类就是java.lang.Object。Object类是整个Java层次中最顶层的类,它包含了可以在任何对象调用的一些有用的方法,例如toString()、equals()和hashcode()。
  • 当子类包含与父类相同的方法时就是方法重写。
  • 关键字super用于子类显式地访问父类中的成员变量或者调用父类中的方法。
  • 一个用final修饰符声明的类不能被继承。一个用final修饰符声明的方法不能被重写。
  • 子类可以使用super()语法调用父类的构造器,并且super()必须是构造器的第一条语句。如果一个构造器没有显式地调用super()或this(),编译器会给构造器中添加一个无参数的super()调用。

 

你可能感兴趣的:(JAVA基础--知识汇总)