前一篇:
这一篇,我们开始去分享多态。
在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。
假如,在food中定义了eat
class Food {
public void eat(){
System.out.println("you can eat food");
}
}
在子类中,可以复写下这个方法
class Mantou extends Food{ @Override public void eat(){
System.out.println("you can eat Mantou");
}
}
执行结果:
这样就完成了复写了Food类的eat方法。
不加Override的也是可以的,但是有一个好处就是,Override可以检查重写的方法是否正确,
写了错误,就会给我们报错。
在上一节中,我们已经知道,引用变量的声明类型可能与其实际类型不符,例如:
public class main {
public static void main(String[] args) {
Food food = new Mantou();
food.eat();
}
}
那么,一个实际类型为Mantou
,引用类型为Food
的变量,调用其eat()
方法,调用的是Food
还是Mantou
的run()
方法?
执行下:
运行一下上面的代码就可以知道,实际上调用的方法是Mantou
的run()
方法。因此可得出结论:
Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。这个非常重要的特性在面向对象编程中称之为多态。。
多态
多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
public class main {
public static void main(String[] args) {
Food food = new Mantou();
food.eat();
}
}
这样,很简单,我们就可以看到,实际运行 的方法是Mantou的方法。但是假设有这样的一个方法
public void runTwice(Food p) {
p.eat();
p.eat();
}
它传入的参数类型是Food,我们是无法知道传入的参数实际类型究竟是Food,还是Mantou,还是Food的其他子类,因此,也无法确定调用的是不是Food类定义的run()方法。
所以,多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。这种不确定性的方法调用,究竟有什么作用?
我们还是来举栗子。
假设我们有一个食物,原始价格是100,可以打折,初始时一折。
class Food {
protected double income=100;
public double getTax() {
return income * 0.1;
}
}
此时我们有一个mantou,需要打8折。
class Mantou extends Food{
@Override
public double getTax(){
return income*0.8;
}
}
最近几天时中秋,开始打折促销了。我们打9折
class yuebing extends Food{
@Override
public double getTax(){
return income*0.9;
}
}
那么我们最后要算某个食物最后的价格,
public double zhekou(Food food){
return food.getTax();
}
最后,我们去计算的时候,就可以
public static void main(String[] args) {
yuebing yuebing=new yuebing();
Main ne=new Main();
System.out.println( ne.zhekou(yuebing));
}
这样结果返回月饼的钱数
getTax。就是利用的多态。getTax只需要和income打交道就可以。
可见,多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。
通常类都是继承Object方法,那么有哪些比较重要的Object方法呢
toString()
:把instance输出为String
;equals()
:判断两个instance是否逻辑相等;hashCode()
:计算一个instance的哈希值。
在必要的情况下,我们可以覆写Object的这几个方法。
class yuebing extends Food{
@Override
public double getTax(){
return income*0.9;
}
@Override
public String toString(){
return "price:"+String.valueOf(income);
}
}
其他的覆写可以根据需要来。
在子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super
来调用
class yuebing extends Food{
@Override
public double getTax(){
return super.getTax();
}
}
final
继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override
class Food {
protected double income=100;
public final double getTax() {
return income * 0.1;
}
}
同时,我们可以看到
直接就无法继承了。
放在类上效果也是一样的。
final修饰的field必须在创建对象时初始化,随后不可修改
所以针对
final修饰符有多种作用:
final修饰的方法可以阻止被覆写;
final修饰的class可以阻止被继承;
final修饰的field必须在创建对象时初始化,随后不可修改。