超类和子类:
父类也叫超类
超类的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方法,那么编译器可以准确知道掉哦那个哪个方法,这种调用方式称为静态绑定。
由于每次调用方法都需进行搜索,开销很大,所以虚拟机预先为每一个类建立一个方法表,如图:
在运行的时候,也就是调用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.