Java基础(五)继承和多态
阅读原文时间:2022年04月03日阅读:1

  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);

  • 不要过多地使用反射

      反射是很脆弱的,即编译器很难帮助人们发现程序中的错误,因此只有在运行时才发现错误并导致异常。