《Java核心技术》 | 继承

一、类、超类和子类
1.1 多态

已经存在的类称为超类基类或者父类;新类称为子类派生类或者孩子类。通常,字类拥有比父类更多的功能。

在字类中可以增加域、增加方法或者覆盖超类的方法,但是绝对不能删除继承的任何域和方法

可以使用 super 方法实现对超类构造器的调用,其中 super 调用构造器的语句必须是字类构造器的第一条语句

public class Employee {

    private String name;
    private int age;
    private double salary;

    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    ...
}

public class Manager extends Employee {

    private double bonus;

    public Manager(String name, int age, double salary){
        //语句1
        super(name, age, salary);
        bonus = 10.00;
    }

}

如果字类没有显示的调用超类的构造器,将自动调用超累默认构造器(没有参数)。如果超类没有定义不带参数的构造器,但是定义了带参数的构造器,此时如果在子类构造器中没有显示的调用父类的带参数的构造器,即没有写 1 这条语句,则会产生错误

一个变量可以指示多种实际类型的现象称为多态。在运行时候能够自动选择调用哪个方法的现象称为动态绑定

这里有一个问题,如果我们使用以下的语句

//语句1
Manager[] managers = new Manager[10];
//语句2
Employee[] staff = managers;
//语句3
staff[0] = new Employee("123",12,12,34,23)

还是上面的继承关系,这里我们将子类(Manager 类)的引用转换成父类(Employee 类)的引用,前两句没有问题,且不用强制转换,想一下,如果一个人是经理,那么他也一定是雇员,雇员才是前提。此时相当于雇员(Employee) 里面存的都是经理(Manager) 的信息

但是第3句,本来规定存储经理(Manager) 信息的数组里面又添加了一条雇员(Employee) 信息,这显然是不对的。好比,这个岗位只是招研究生的,结果你一个本科生去投简历,那人家肯定不要你,一个道理,范围都定死了。

此时会报以下错误

如果第三句改成

staff[0] = new Manager("123",12,12,34,23);

那肯定不会出错了,因为本来就是规定存的经理(Manager),现在 new 一个经理,那正好符合规则

1.2 动态绑定过程

1.首先 new 一个字类的对象,创建一个字类的引用指向他。这个时候,如果调用下面的 getSalary(xxx) 方法,则编译器便会列举子类中名为 getSalary 的方法和其超类中访问属性为 public 且名为 getSalary 的方法

Manager manager = new Manager();
manager.getSalary(90);

2.在字类和父类查找名为 getSalary 方法的过程中,只要参数匹配,就调用这个方法。期间,编译器会自动作参数的类型转换,即如果传入的参数是 int 类型,如果定义的相同名称的方法的参数类型比他大(double,long) 等等,也会自动调用该方法,因为会进行自动转换。而如果没能找到与参数类型匹配的方法,或者有多个方法与之对应,则会报错

public class Employee {
    /*public double getSalary(int a){
        System.out.println("a");
        return a;
    }*/
}

public class Manager extends Employee {
    //语句1
    public double getSalary(double a){
        System.out.println("sal double");
        return 1;
    }
    
    //语句2
    public double getSalary(int a) {
        System.out.println("sal int a");
        return 1;
    }
}

public class ManagerTest {
    public static void main(String[] args) {
        Manager manager = new Manager();
        manager.getSalary(90);
    }
}

结果:sal int a

如果把语句2去掉,同时父类不变,那么

输出:sal double

3.如果是 private、static、final 方法,那么编译器肯定知道该调用哪个方法,该调用方式称为静态绑定。与之对应的是,对象自己去找应该调用哪个方法,这种调用方式是动态绑定

4.由于每次都要方法去字类父类中寻找对应的方法,很麻烦,开销很大。因此,虚拟机为每个类创建了一个方法表,列出了所有方法,在真正需要调用方法的时候,虚拟机直接查找方法表就行了。

比如 Employee 的方法表

方法名所在的类
getNameEmployee
getSalary()Employee
getHireDayEmployee

这里只列举了一部分,另外还有默认调用 Object 的类,没有写出

Manager 的方法类(部分),其中第一个类是从 Employee 中继承可得,另一个是自己增加的

方法名所在的类
getSalary()Employee
getSalary(int a)Manager
getSalary(double a)Manager

举例

比如 manager.getSalary(90) 这个方法。虚拟机直接在 Employee 和 Manager 类中搜索定义 getSalary 方法的类,由于 Manager 中就有两个相同签明的方法,于是就找参数为 int 的那个类就好了,此时虚拟机知道该调用哪个方法。

PS

覆盖一个方法的时候,字类方法不能低于父类方法的可见性。如果父类是 public 修饰的,由于字类不能比父类小,因此只能是 public。如果不写,则使 default,也会报错

如果父类是 protected,那么字类只能是 protected 或者 public,不写也会报错(不写则是 package-private)

class Employee {
    protected void aa(){}
}
class Manager {
    @Override
    void aa() {}    //报错
    
    protected void aa(){}  //正确
    
    public void aa(){}  //正确
    
}
1.3 fina 类及其方法

不允许进行扩展(继承)的类称为 final 类,当一个类被声明为 final 类型,那么其中的所有方法自动转为 final 方法,但是域不会转为 final

public final class A {

    void printA(){}
    
}

当使用以下语句时

被 final 修饰的方法不能被覆盖

public class A {

    final void printA(){}

}

1.4 强制类型转换

从上到下进行类型转换,即从大的范围转为小的范围

在程序设计中,尽量少的使用类型转换和 instanceof 运算符

1.5 抽象类

抽象类既可以声明一个没有实现的方法,也可以创建一个实现了的方法。字类继承了父类的抽象类之后,会对父类声明的方法加以实现

public abstract class Person {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public abstract String getDescription();

    public String getName() {
        return name;
    }

}

public class Employee extends Person{

    public Employee(String name) {
        super(name);
    }

    @Override
    public String getDescription() {
        return null;
    }
}

一个抽象类,即使不含抽象方法,也可以将类声明为抽象类

抽象类不能被实例化

如果将一个类声明为 abstract,就不能创建这个类的对象。如果定义了一个抽象类的对象,那么他是能通过一个非抽象字类来引用。如上面那个例子,最终可以通过以下方法来创建抽象类的实例,然后调用抽象类的方法

Person person = new Employee("aaa");
person.getName();

1.6 各个修饰符的范围
  1. private:仅对本类可见
  2. public:对所有类可见
  3. protected:对本包和所有字类可见
  4. default:对本包可见

二、装箱和拆箱
Integer a = 100;    //装箱
int b = a;  //拆箱

装箱调用 Integer.valueOf 方法,拆箱调用 intValue 方法

三、参考

《Java核心技术 卷1》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值