美文网首页
5.1继承,类、超类和子类

5.1继承,类、超类和子类

作者: 十六只猴子王 | 来源:发表于2019-03-18 19:42 被阅读0次

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

    类,超类和子类

    • 继承关键字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继承,类、超类和子类

          本文链接:https://www.haomeiwen.com/subject/lguvmqtx.html