5.1继承,类、超类和子类

继承所在的类时反复这些类的方法和域,可以添加一些新的方法和域,以满足新的需求
反射式指在程序运行期间发现更多的类及其属性的能能力

类,超类和子类

  • 继承关键字extends
public class Manager extends Employee {
方法域
}
  • 关键字extends表明正在构造的新类派生于一个已存在的类,已存在的类的方法称为超类、基类、父类;新类称为子类、孩子类;子类比超类拥有的功能更加丰富
  • 通过扩展超类定义子类的时候,仅需要指子类于超类不同之处。因此在设计类的时候,应该通过用的方法在超类中,而将具有特殊性的方法放在子类中

覆盖方法

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

animal : eat
dog : eat
animal : eat

子类构造器

super(n,s,year,month,day);

可以通过super实现对超类构造器的调用。使用super调用构造器的语句必须是子类构造器的第一天语句

  • 一个对象变量,可以指示多种实际类型的现行被称为多态,在运行时能够自动地调用哪个方法的现象称之为动态绑定
public class ManagerTest
{
   public static void main(String[] args)
   {
      //构造管理器对象
      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("Tommy Tester", 40000, 1990, 3, 15);

      // 打印所有员工对象的信息
      for (Employee e : staff)
         System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
//输出每个人的工资
   }
}
import java.time.*;

public class Employee
{
   private String name;
   private double salary;
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day)
   {
      this.name = name;
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public String getName()
   {
      return name;
   }

   public double getSalary()
   {
      return salary;
   }

   public LocalDate getHireDay()
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }
}
public class Manager extends Employee
{
   private double bonus;
   public Manager(String name, double salary, int year, int month, int day)
   {
      super(name, salary, year, month, day);
      bonus = 0;
   }

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

   public void setBonus(double b)
   {
      bonus = b;
   }
}

Carl Cracker 85000.0
Harry Hacker 50000.0
Tommy Tester 40000.0

继承层次

继承并不仅于一个层次,有一个公共超类派生出来的所有类被称为继承层次,在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承连
一个祖先可以拥有多个子孙继承链

多态

一个对象变量,可以指示多种实际类型的现行被称为多态,在运行时能够自动地调用哪个方法的现象称之为动态绑定

  • 有一个用来判断是否应该设计继承关系的简单规则,这就是“is-a”,它表明子类的每个对象也是超类
  • 在java程序设计语言中,对象变量时多态的
  • 不能将一个超类的引用赋给子类变量
    例如:不是所有的雇员都是经理,但每一个经理都是雇员
  • 在java中,子类数组的引用可以转换成超类数组的引用,而不是需要采用强制类型转换
Manager[] manages = new Manage[10];
Employee[] staff = managers;

将manager转换为employee

理解方法的调用

下面假设要调用x.f(args),隐式函数x声明为c类的一个对象

  1. 编译器查看对象的声明类型和方法名,编译器将会一一列举所有c类中名为f的方法和超类中的访问属性public且名为f的方法(超类的私有方法不可访问)。
  2. 接下来,编译器将其看调用方法时提供的参数类型,如果在所有的为f的方法中存在一个与提供的参数类型完全匹配这个选择这个方法。这个过程被称为重载解析
    如果编译器没有找到与参数类匹配的 方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误
    如果在子类中定义了一个与超类签名相同的方法,那么子类中这个方法就覆盖了超类中的相同签名的方法
  3. 如果private方法,static方法,final方法或者构造器,那么编译器将可以准确的知道应该调用哪个方法,我们将这种调用方式称为静态绑定
  4. 采用动态绑定调用方法时,虚拟机一定要调用与x所引用对象的实际类型最合适的那个类的方法,假设x的实际类型时d,他是c类的子类,如果d类定义了方法f(string),就直接调用它;否则d类的超类中寻找f(string),以此类推
    虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用方法。如果调用super.f(param)编译器将被隐式参数超类的方法表进行搜索

动态绑定有一个非常重要的特性:无需对现存的代码进行修改,就可以对程序进行扩展
在覆盖一个方法的时候,子类方法不能低于超类方法的可见性,如果超类的方法是public,子类方法一定要声明为public

阻止继承:final类和方法

希望组织人们利用某个类定义子类,不允许扩展的类被称为final类,如果在定义类的时候使用了final修饰符就表明这个类是final类

public final class Executive extends Manager{
}

类中的特定方法也可以被声明为final,如果这样,子类就不能覆盖这个方法(final类中的所有方法自动的改成final方法)

  • 对于final域来说,构造对象之后就不允许改变它们的值了,如果将一个类声明为final,只有其中的方法自动生成为final而不包括域
  • 将方法或类声明为final主要的目的是:确保他们不会子子类中改变语义
  • 如果一个方法没有被覆盖并且很短,编译器就能对它进行优化处理,这个过程称为内联

强制类型转换

将一个类型强制转换成另一个类型的过程被称为类型转换

double x = 3.045
int nx = (int) x;

有时候需要将某个类的对象引用转换成另一个类的对象引用,用一对圆括号括将目标类名括起来,并放置在需要转换的对象引用之前就可以了

Manger boss = (Manager) staff[0];

进行类型转换的唯一目的:在在世忽略对象的实际类型后,使用对象的全部功能

  • 但将一个超类的引用赋给一个子类变量,必须进行类型转换,这样才能通过运行时的检查
    在类型转换之前,先查看否能够成功的转换,这个过程可以用instanceof操作符完成
if (staff[1] instanceof Manager)
{
  boss=(Manager)staff[1];
}
  • 综上所述
    只能在继承层次内进行类型转换。
    在将超类转换成子类之前,应使用instanceof进行检查

抽象类

如果自下而上在类的继承层次结构中上移,位于层的类更具有通用性,甚至更加的抽象
为了提高程序的清晰度,包含一个或多个抽象方法的类本身须被声明为抽象类的
除了抽象方法之外,抽象类还可以包含具体的数据和具体的方法
例如:

public abstract class Person
{
  private String name;
  public Person(String name)
  {
  this.name = name;
  }
  public abstract String getDescription();
  public String getName(){
    return name;
  }  
}
  • 抽象方法充当占位的角色,他们的具体实现在子类中,扩展抽象类可以有两种选择。
    一种是:在抽象类中定义部分抽象类方法或不定义抽象类方法,这样就将子类也标记成抽象类;另一种时定义全部的抽象方法,这样一来,子类也就不是抽象的了
  • 抽象类不能被实例化,可以定义一个抽象类的对象变量,但是它至只能引用非抽象子类的对象

受保护访问

任何声明为private的内容对其它类都时不可见的,则会对于子类也完全适用,即子类也不能访问超类的私有域
希望超类中的某些方法被子类访问,或允许子类的方法访问超类的某个域,可以使用protected

  1. 仅对本类可见——private
  2. 对所有类可见——public
  3. 对本包和所有子类可见——protected
  4. 对本包可见——默认,不需要修饰符

你可能感兴趣的:(5.1继承,类、超类和子类)