Java核心技术 继承

时间:2023-02-16 12:15:56

超类和子类:

父类也叫超类

超类的private属性并不能被子类访问。假如我们编写一个Manager类继承Employee类。

public class Manager extends Employee
{
private double bonus;
. . .
public void setBonus(double bonus)
{
this.bonus = bonus;
}
}

Manager类的计算薪水方式肯定与Emplyee不同,因为它多了一个奖金bonus,那么我们应该怎么重写这个类呢?

public double getSalary()
{
return salary + bonus; // won't work
}

或许会采用上面的方式,实际上,这样会报错,原因就是子类并不能直接访问父类的private属性,尽管Manager继承了这个属性。

此时只能通过父类的getSalary方法来得到salary属性,可以使用关键字super:

public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}

 

有些人认为,super与this引用是类似的概念,实际上super并不是一个对象的引用,不能将super赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字。

并且,子类可以增加覆盖父类的域或方法,但不能删除继承的任何域和方法。

 

子类可以隐式的调用父类默认的构造方法。 
如果父类没有默认构造方法,必须显示调用。 

 

多态、动态绑定:

一个对象变量可以指示多种实际类型的现象被称为多态。

假设我们有如下语句:

Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
boss.setBonus(
5000);
Employee[] staff
= new Employee[3];
staff[
0] = boss;
staff[
1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[
2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e : staff)
System.out.println(e.getName()
+ " " + e.getSalary());

 

那么将会在控制台输出如下信息:

Carl Cracker 85000.0
Harry Hacker
50000.0
Tommy Tester
40000.0

这里很奇怪,为什么e能够判断出是Employee对象还是Manager对象呢,这里就是我们说的多态(一个对象变量可以指示多种实际类型)。

那么什么是动态绑定呢?

首先弄清对象方法的执行过程:

1.编译器属性查看该类和其父类中方法名相同的方法(父类同时还需要该方法为public)。

2.接下来,编译器查找参数类型相同的方法,并执行,如果在这个过程存在多个方法与之匹配,就会报告错误。

上面的过程就称为动态绑定,虚拟机一定调用与对象方法的实际类型最适合的那个方法,也就是说,如果在子类中找到该方法,即执行子类的方法,否则就去父类中寻找。

如果是private\static\final方法,那么编译器可以准确知道掉哦那个哪个方法,这种调用方式称为静态绑定。

由于每次调用方法都需进行搜索,开销很大,所以虚拟机预先为每一个类建立一个方法表,如图:

Java核心技术 继承

Java核心技术 继承

 

在运行的时候,也就是调用e.getSalary时,解析过程如下:

1.首先提取e的实际类型方法表,可能是Employee,也可能是Manager。

2.在方法表中搜索签名相同的方法。

3.虚拟机调用该方法。

 

阻止继承

有时候,不希望利用某个类定义子类。不允许扩展的类被称为final类。如果在定义类的时候利用了final修饰符就表明该类是final类。

类中特定的方法也可以被声明为final,这样子类就不能覆盖这个方法,final类中的所有方法都自动称为final方法。

 

受保护访问:

当我们把某个方法或者域的可访问标识设置为proteced时,表明这个类是受保护的,也就是说同个包以及其子类都能够访问。

但是这里有一个限定,假设我们把hireDay域设为proteced,那么Manager类只能访问Manager对象中的hireDay域而不能访问其他Employee对象的hireDay域。

这里有点混乱,个人认为机制是这样的:

子类继承父类,无论其属性的访问性为什么,都会被继承。也就是private的也会被继承到子类,但是子类不能访问继承来的private属性。

当设置为proteced时,同样被继承,但是此时可以访问,访问的是其本身对象继承来的域。所以不能访问其他对象的域。

 

equals方法:

Object类中的equals方法用于检测一个对象是否等于另一个对象。在Object中,这个方法将判断两个对象是否具有相同的引用。然而,对大多数类来说,这种判断并没有什么意义。经常需要检测两个对象状态的相等性,如果俩个个对象的状态相等,就认为这两个对象相等。

如比较两个雇员对象的姓名、薪水、和雇佣日期都一样,那就认为它们是相等的。

public class Employee
{
. . .
public boolean equals(Object otherObject)
{
// a quick test to see if the objects are identical
if (this == otherObject) return true;
// must return false if the explicit parameter is null
if (otherObject == null) return false;
// if the classes don't match, they can't be equal 返回对象所属的类
if (getClass() != otherObject.getClass())
return false;
// now we know otherObject is a non-null Employee
Employee other = (Employee) otherObject;
// test whether the fields have identical values

return Objects.equals(name, other.name)
&& salary == other.salary
&& Object.equals(hireDay, other.hireDay);


}
}

在子类中定义equals方法时,首先调用父类的equals,如果检测失败,那就不相等,如果检测成功,则继续比较子类中的实例域:

public class Manager extends Employee
{
. . .
public boolean equals(Object otherObject)
{
Chapter
5 Inheritance 230
if (!super.equals(otherObject)) return false;
// super.equals checked that this and otherObject belong to the same class
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}

 

有的人不会采用getClass(),而是采用如下的语句:

 

if (!(otherObject instanceof Employee)) return false;

实际上,这样做有可能出错。

如果x.equals(y)是相等的,那么y.equals(x)应该也是相等的,那么如果采用上面的语句,

当我们调用e.queals(m),其中e是Employee的对象,m是Manger的对象,那么返回的结果是相等的,如果反过来调用呢:

m.queals(e),这就使得Manager类受到了束缚,因为e并没有拥有m特有的那部分信息!

接下来的判断就会出现错误!

 

另外,每一个对象拥有一个属于增加的散列码,如果x.equslas(y)返回true,那么x.hashCode == y.hashCode也应该返回true.