Java 面向对象-继承中的注意点

类继承中父类的构造和子类的构造使用

在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。

如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();,

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 12, 89);
    }
}

class Person {
    protected String name;
    protected int age;

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

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 调用父类的构造方法Person(String, int)
        this.score = score;
    }
}

如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法

向上转型

如果一个引用变量的类型是Student,那么它可以指向一个Student类型的实例:

Student s = new Student();

如果一个引用类型的变量是Person,那么它可以指向一个Person类型的实例:

Person p = new Person();

现在问题来了:如果Student是从Person继承下来的,那么,一个引用类型为Person的变量,能否指向Student类型的实例?

Person p = new Student(); // ???

测试一下就可以发现,这种指向是允许的!

因为Student继承自Person,因此,它拥有Person的全部功能。

Person类型的变量,如果指向Student类型的实例,对它进行操作,是没有问题的!

这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。

向上转型实际上是把一个子类型安全地变为更加抽象的父类型:

Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok

注意到继承树是Student > Person > Object,所以,可以把Student类型转型为Person,或者更高层次的Object

向下转型

和向上转型相反,如果把一个父类类型强制转型为子类类型,就是向下转型(downcasting)。例如:

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!

如果测试上面的代码,可以发现:

Person类型p1实际指向Student实例,Person类型变量p2实际指向Person实例。在向下转型的时候,把p1转型为Student会成功,因为p1确实指向Student实例,把p2转型为Student会失败,因为p2的实际类型是Person,不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。

因此,向下转型很可能会失败。失败的时候,Java虚拟机会报ClassCastException

为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false

Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true

Student n = null;
System.out.println(n instanceof Student); // false

instanceof实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。

如果一个引用变量为null,那么对任何instanceof的判断都为false

区分继承和组合 

在使用继承时,我们要注意逻辑一致性。

考察下面的Book类:

class Book {
    protected String name;
    public String getName() {...}
    public void setName(String name) {...}
}

这个Book类也有name字段,那么,我们能不能让Student继承自Book呢?

class Student extends Book {
    protected int score;
}

显然,从逻辑上讲,这是不合理的,Student不应该从Book继承,而应该从Person继承。

究其原因,是因为StudentPerson的一种,它们是is关系,而Student并不是Book。实际上StudentBook的关系是has关系。

具有has关系不应该使用继承,而是使用组合,即Student可以持有一个Book实例:

class Student extends Person {
    protected Book book;
    protected int score;
}

因此,继承是is关系,组合是has关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值