假设现在有个需求是:
做一个动物园中各个动物开始吃东西的程序
需求分析:
代码部分:
//定义 猫 对象
class Cat{
private String name; //猫的名称
private String color; //猫的颜色
Cat(String name,String color){ //定义带参构造方法,传入猫的名称和特征
this.name = name;
this.color = color;
};
public void eat() { //重写父类叫的方法,单独为猫设置吃食物
System.out.println("猫吃食物。。。。。");
}
}
//定义 狗 对象
class Dog{
private String name;//名称
private String type;//种类
Dog(String name,String type){ //定义带参构造方法,传入狗的名称和特征
this.name = name;
this.type = type;
};
public void eat() { //重写父类叫的方法,单独为狗设置吃食物
System.out.println("狗吃食物。。。。。");
}
}
//定义一个 动物园 类
class Zoo{
private String name; //动物园名称
Zoo(String name){ //定义带参构造方法
this.name = name;
};
public void eat(Cat cat) //让猫开始吃东西
{
cat.eat(); //调用猫的吃东西的方法
}
public void eat(Dog dog) //让狗开始吃东西
{
dog.eat(); //调用狗的吃东西的方法
}
}
启动程序,开始吃食物:
public static void main(String[] args) {
Cat cat = new Cat("小花","花色");//new一只猫
Dog dog = new Dog("小黑","藏獒");//new一只狗
Zoo zoo = new Zoo("大动物园");//new一个动物园
zoo.eat(cat);//猫在动物开始吃东西
zoo.eat(dog);//狗在动物园开始吃东西
}
执行结果:
这样就完成需求中的 动物园的动物在吃东西。
但是:
如果还有其他动物呢,比如狮子、老虎、孔雀、骆驼.......等等 ,我们就需要在Zoo类中为每个动物定义不同的吃东西方法。
现在我们使用Java中的继承来优化一下代码:
//定义一个 动物 类
class Animal{
private String name; //动物共有的属性 name
Animal(String name){ //声明带参构造方法
this.name = name;
};
public void eat() //动物共有的 吃食物 的方法
{
System.out.println("吃东西。。。。。。");
}
}
2.猫、狗等继承自动物(类):
//定义 猫 对象
class Cat extends Animal{
private String color; //猫的颜色
Cat(String name,String color){ //定义带参构造方法,传入猫的名称和特征
super(name); //将实参传递给父类中的name
this.color = color;
};
}
//定义 狗 对象
class Dog extends Animal{
private String type;//种类
Dog(String name,String type){ //定义带参构造方法,传入狗的名称和特征
super(name); //将实参传递给父类中的name
this.type = type;
};
}
3.这时,动物园对象(类)中写法是这样的:
//定义一个 动物园 类
class Zoo{
private String name; //动物园名称
Zoo(String name){ //定义带参构造方法
this.name = name;
};
public void eat(Animal animal) //动物开始吃东西
{
animal.eat(); //调用动物吃东西的方法
}
}
4.开始执行:
public static void main(String[] args) {
Cat cat = new Cat("小花","花色");//new一只猫
Dog dog = new Dog("小黑","藏獒");//new一只狗
Zoo zoo1 = new Zoo("大动物园");//将猫放进动物园
zoo1.eat(cat);//猫开始吃东西
zoo1.eat(dog);//猫开始吃东西
}
5.执行结果:
这样我们就解决了,需要重复为每个动物定义eat()吃东西这个操作了。
但是:
由于吃eat()这个方法是“动物”对象提供的,因此不能很好的区分哪个动物在吃,我们的需求是要清楚那个动物在吃,象最初代码执行结果那样,该怎么做呢?
解决方法就是,每个动物重写一下“动物”对象的吃eat()方法,也就是重写父类方法:
/定义 猫 对象
class Cat extends Animal{
private String color; //猫的颜色
Cat(String name,String color){ //定义带参构造方法,传入猫的名称和特征
super(name);
this.color = color;
};
@Override
public void eat() { //重写父类吃的方法,重新为小猫设置吃食物
System.out.println("猫吃食物。。。。。");
}
}
//定义 狗 对象
class Dog extends Animal{
private String type;//种类
Dog(String name,String type){ //定义带参构造方法,传入狗的名称和特征
super(name);
this.type = type;
};
@Override
public void eat() { //重写父类吃的方法,重新为小狗设置吃食物
System.out.println("狗吃食物。。。。。");
}
}
其他类不变(动物、动物园)
执行结果为:
好了 大功告成!
这时大家的疑惑可能就是
1.为什么在动物园对象中我们eat()参数为Animal(动物),而传递的实参是Cat对象或Dog对象;
2.为什么在动物园对象中我们eat()调用的是Animal(动物)的eat(),实际执行出来的确实Cat对象和Dog对象的eat().
解答:
1.因为我们的Animal对象是其他(Cat对象/Dog对象)的父类,
而java中有这样一个特性:父类引用可以指向子类对象,也就是说Animal对象中的eat()中的参数Animal对象是一个引用,当我们传递其字类对象时,就表示我们将这个父类引用指向了一个子类对象,Java这样设计的好处就是降低代码耦合性,提升代码可扩展性(如:我们后续添加需要其他动物对象,只要其是Animal(动物类)的子类,那么我们的动物园对象中的代码是不用改变的)
2.因为Java有多态,我们的子类(Cat/Dog)继承自父类并重写了父类(Animal)中eat(),那么在调用父类(Animal)中eat()时,在运行期Java会通过判断我们传入的是哪个子类对象,再去确定调用对应的eat()(如果子类没有重写父类方法,就默认调用父类的eat()),这个就是Java的多态也就是动态绑定。
至此得出:
多态的前提条件:
1.要有继承;
2.要有重写;
3.父类引用指向子类对象