多态
一个对象变量可以指示多种实际类型的现象称为多态。
在运行时,能够自动选择适当的方法,成为动态绑定。
Java中,对象变量是多态的。某类型变量既可以引用本类对象,也可以引用任何一个子类的对象。
多态是方法的多态,属性没有多态。
public class Employee {
private int salary = 3000;
public int getSalary() {
return salary;
}
}
public class Manager extends Employee {
private int bonus=0;
public void setBonus(int bonus) {
this.bonus = bonus;
}
@Override
public int getSalary() {
int baseSalary = super.getSalary();
return baseSalary+bonus;
}
}
public class Application {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
Manager boss = new Manager();
Employee[] staff = new Employee[3];
staff[0]=e1;
staff[1]=e2;
staff[2]=boss;
boss.setBonus(5000);
for(Employee e : staff){
System.out.println(e.getSalary());
}
}
}
在Java中,子类引用的数组可以转换成超类引用的数组,不需要强制类型转换。
Manager[] managers = new Manager[10];
Employee[] staff = managers;
//managers 和 staff引用的是同一个数组
警告
staff[0] = new Employee();
这里staff[0]和manager[0]是相同的引用,把一个普通员工擅自归入经理行列中了。当调用manager[0].setBonus(5000)的时候,会试图调用一个不存在的实例字段,进而搅乱相邻存储空间的内容。
为了确保不发生这类破坏,所有数组都要牢记创建时的元素类型,并负责监督仅将类型兼容的引用存储到数组中。如果试图存储一个Employee类型的引用就会引发ArrayStoreException异常。
动态绑定
在运行时能够自动地选择适当的方法,称为动态绑定。动态绑定无须修改现有代码就可以对程序进行扩展。
在Java中,动态绑定是默认的行为,如果不希望让一个方法是虚拟的,可以将它标记为final。
阻止继承
不允许扩展的类称为final类,final类可以阻止派生出本类的子类。
public final class Executive extends Manager{
}
类中的方法也可以声明为final,这样子类就不能重写这个方法。(final类中的所有方法自动成为final方法)。
public class Employee{
public final String getName(){
return name;
}
}
字段也可以声明为final,对于final字段,构造对象之后就不允许改变它们的值。但只有final类中的方法自动成为final,不包括字段。
将方法或类声明为final可以确保它们不会在子类中改变语义。String类是final类,这意味着不允许任何人定义String类的子类,如果有一个String引用,引用的一定是String对象,而不可能是其他类的对象。
对象引用的强制类型转换
将某个类的对象引用转换成另外一个类的对象引用,转换语法与数值表达式的强制类型转换类似。
Manager boss = (Manager) staff[0];
只能在继承层次内进行强制类型转换。当承诺不符时,会产生一个ClassCastException异常,如果没有捕获这个异常,程序就会终止。
在将超类转换成子类之前,应使用instanceof进行检查。
if(staff[1] instanceof Manager){
boss = (Manager) staff[1];
}
如果类型转换不成功,编译器就不会让你完成这个转换。
String c = (string) staff[1];//编译错误 String类不是Employee的子类。
一般情况下,最好尽量少用强制类型转换和instanceof运算符,而需要重新设计超类添加方法。