1.多态
先来看一个例子,其中Employee类是父类,Manager类继承了Employee类:
public static void main(String[] args) { // construct a Manager object Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000); Employee[] staff = new Employee[3]; // fill the staff array with Manager and Employee objects staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); // print out information about all Employee objects for (Employee e : staff) System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()); }
在for循环中,尽管这里将e声明为Employee类型,但实际上e既可以引用Employee类型的对象,也可以引用Manager类型的对象。当e引用Employee对象时,e.getSalary()方法嗲用的是Employee类中的getSalary()方法;当e引用Manager对象时,e.getSalary()调用的是Manager类中的getSalary方法。
一个对象变量(例如:e)可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动地选择调用哪个方法的现象称为动态绑定。
一个用来判断是否应该设计为继承关系的简单规则,就是“is-a”规则,它表明子类的每个对象也是父类的对象。“is-a”规则的另一种表述是置换法则。它表明程序中出现的父类对象的任何地方都可以用子类对象置换。
在Java中,对象变量是多态的。也就是,一个对象变量既可以引用它本身的类对象,也可以引用它的任何一个子类的对象。
2.抽象类
父类中抽象方法可以不用实现,为了提高程序的清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象的。
除了抽象方法外,抽象类还可以包含具体数据和具体方法。
抽象方法充当着占位的角色,它们的具体实现在子类中。扩展抽象类可以有两种选择:一种是在抽象类中定义部分抽象类方法或者不定义抽象类方法,这样就必须将子类也标记为抽象类;另一种是定义全部的抽象方法,这样一来,子类就不是抽象的了。
类即使不含抽象方法,也可以将类声明为抽象类。
抽象类不能被实例化。也就是说,如果一个类声明为abstract,就不能使用创造这个类的对象,但是可以创建一个具体子类的对象。同时,可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。
例如:Person类是抽象类,Student是具体子类。
Person p = new Student();
3.受保护访问protected
在前面我们知道了,最好将类中的域标记为private,因为任何声明为private的内容对其他类都是不可见的,这对于子类来说也完全适用,即就连子类也不能访问父类的私有域。
如果希望父类中的某些方法允许被子类访问,或允许子类的方法访问父类的某个域,可以将这些方法或域声明为protected。
在实际应用中,要谨慎使用protected。如果需要将设计的类提供给其他程序员使用,而在这个域中设置了一些受保护域,由于其他程序员可以由这个类再派生出新类,同时可以访问其中的受保护域。在这种情况下,如果需要对这个类的实现进行修改,就必须通知所有使用这个类的程序员。这违背了OOP提倡的数据封装原则。
4总结Java中用于控制可见性的4个访问控制符
- private-仅对本类可见
- public-对任何类都可见
- protected-对本包和所有子类可见
- 没有修饰符-对本包可见
5.总结继承的设计技巧
- 将公共操作和域放在父类
- 不要使用受保护的域
protected定义的实例域并不能带来更好地保护,原因主要有两点:(1)子类集合时无限制的,任何一个人都能够由某个类派生一个子类,并编写代码以直接访问protected的实例域,从而破坏了封装性。(2)在Java中,同一个包中的所有类都可以访问protected域,而不管它是否为这个类的子类。
- 使用继承实现“is-a”关系
- 除非所有继承的方法都有意义,否则不要使用继承
- 在覆盖方法时,不要改变预期的行为
- 使用多态,而非类型信息
例如,下面这种情况就应该考虑使用多态性,如果两个action是相同的概念,就应该为这个概念定义一个方法,并将其放置在两个类的父类或接口中。
if(x是类型1) action(x); else if (x是类型2) action(x);
- 不要过多地使用反射
反射是很脆弱的,即编译器很难帮助人们发现程序中的错误,因此只有在运行时才发现错误并导致异常。