目录
继承虽然可以帮助我们提高代码的复用性,但还不够,比如说数学老师和语文老师都要去上课,但是他们上课的内容不同,那么怎么设计呢?如果按照继承的思想,应该在它们的父类老师中设计,但这时又会出现一个问题,父类中如何写呢?如果仅仅说“老师在上课”似乎不妥,但又实在没有一种合适的词汇来表示。那么就需要用到多态了。
多态的概念与用法
多态,即多种形态。当要完成某个确定的动作时,不同的对象实现方法不同,就像上面的例子,数学老师要上数学课,语文老师要上语文课。去上课的对象不同,上课的内容也就不同,这就是多态。
Java中要实现多态,需要满足以下条件:
- 必须在继承体系下
- 子类必须要对父类中方法进行重写
- 通过父类的引用调用重写的方法
例如分别创建Teacher对象和其子类MathTeacher和ChineseTeacher对象:
再对其调用:
public class Test {
public static void main(String[] args) {
ChineseTeacher chineseTeacher = new ChineseTeacher("张三", "男");
MathTeacher mathTeacher = new MathTeacher("李四", "女");
chineseTeacher.todo();
mathTeacher.todo();
}
//输出:
//张三老师在上语文课!
//李四老师在上数学课!
}
可以发现虽然两个对象都调用了todo方法,但是实现的结果却不同。而造成这种不同的原因就在于方法重写。
方法重写
重写,也称覆盖(override)。即保持方法的外壳不变(方法名、参数、返回值不变),重新改写它的实现。比如说:
int i = 10;
i = 20;//可以理解为i的“重写”
也就是说int是它的外壳,不能改变,而它里面的值却可以改变。我们可以把方法的实现抽象理解为它的“值”。一个变量被static或final修饰后,它的值就不能再改变;类似的,一个方法被static和final修饰后也不能再重写。
对方法进行重写时,可以在前面加上@override,它可以帮助我们进行一些检验:如果找不到需要重写的方法,编译就会报错。这可以保证我们在重写的时候方法名、参数、返回值等都正确。
当然,IDEA也提供了一键重写的功能:(也可以通过快捷键重写)
由于重写是新的实现方法,因此设计重写应该在新的类中重写,这样可以保证在旧的类中依然可以正常使用。
重写和重载是两个不同的概念,重载仅仅是方法名相同,其他的部分都可以不同,重载不会覆盖原来的方法,因此重载后的方法是一个全新的(只有方法名相同)方法。
注意:
- 重写的方法访问权限不能比原始权限低
- 由于重写是在不同类中实现的,因此也不能重写由private修饰的类
- 不要重写构造方法,否则会造成一些难以发现的问题
动态绑定:
动态绑定指的是通过实例化对象访问某个方法时,JVM会沿着继承关系链依次寻找,直到找到目标方法。因此动态绑定的实现必须要满足继承关系。
向上转型与向下转型
创建的子类对象,可以当成父类对象来使用。这种使用方法就叫做向上转型。
为什么需要向上转型:
其实还是多态的思想:进行共性抽取和提高代码的利用率。
比如说:
public static void todo(Teacher teacher) {
teacher.todo();
}
public static void main(String[] args) {
todo(new ChineseTeacher("张三", "男"));
todo(new MathTeacher("李四", "女"));
}
由于发生了动态绑定,传入chineseTeacher对象时,访问的是ChineseTeacher类的todo方法,而传入mathTeacher对象时,访问的时MathTeacher类的todo方法。
实现向上转型的方式:
1、方法传参(如上述所示)
2、直接赋值,例如:
Teacher teacher = new ChineseTeacher();
3、方法返回值,当方法返回值类型时父类时,如果返回子类对象也会发生向上转型。
由于向上转型后不能再访问子类成员,若要在访问子类特有的成员,就要实现向下转型。
向下转型
向下转型的实现与向上转型类似,不过一个类向上转型后,如果要再次向下转型,最好转为原来的类,否则不安全。比如:
为了确保向下转型的成功,可以使用instanceof关键字。因此上述代码可以改为:
—>
if (对象 instanceof 类) {}; 含义:
如果该对象是由该类向上转型得到,执行代码。