需求
1.输出各种动物叫声
2.输出各种动物腿数
3.实现修改参数功能
效果如下:
实现思路
分析:根据给出的类图,我们可以看出有3种动物,猫、鸭子和海豚,他们都属于父类Animal,而他们共有的特征(属性)在此项目有名字和叫声(也可以定义为方法,在此项目中我定为动物的属性),而腿数只有猫和鸭子有,海豚是没有的,所以这个项目要求我们修改腿数只有猫和鸭子,用接口实现这个修改方法,而且输入的腿数不正确,需要抛出异常,且程序要正常运行。
首先,根据类图和分析,我们先写出Animal(父类),代码如下:
package com.animal;
/**
* 动物类
* @author Administrator
*
*/
public abstract class Animal {
private String name; //动物名字
private String voice; //动物叫声
private int leg; //动物腿数
public int getLeg() {
return leg;
}
public void setLeg(int leg) {
this.leg = leg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVoice() {
return voice;
}
public void setVoice(String voice) {
this.voice = voice;
}
public Animal(){}
//没有腿数的带参构造方法,给海豚初始化
public Animal(String name,String voice) {
this.name=name;
this.voice=voice;
}
//带腿数参数的构造方法,给猫和鸭子初始化
public Animal(String name,int leg,String voice) {
this.name=name;
this.leg=leg;
this.voice=voice;
}
}
可以看出父类的属性有名字、叫声、腿数,那么海豚继承这个父类也会有腿数属性,但是海豚是没有腿数的,那么我们可以写两个构造方法,一个是带腿数的构造方法,一个是不带腿数的构造方法,我们在给海豚初始化的时候使用不带腿数参数的构造方法就好了。
父类写完了,我们接下来写父类的子类,猫、鸭子和海豚类!
猫类:
public class Cat extends Animal {
public Cat(){}
public Cat(String name,int leg,String voice) {
super(name,leg,voice);
}
}
鸭子类:
public class Duck extends Animal {
public Duck(){}
public Duck(String name,int leg,String voice) {
super(name,leg,voice);
}
}
海豚类:
public class Delph extends Animal {
public Delph(){}
public Delph(String name,String voice) {
super(name,voice);
}
}
现在三个子类都写好各自的构造方法(为了方便等下初始化属性的时候赋值);
接下来还有一个AnimalMgr类和一个接口LuSheng,这个接口就是为了实现修改动物的数据(名字和腿数),但是在修改这个腿数之前,我们可以发现这个程序运行的时候,先输出了一个界面,展示了动物的信息,并且输入0的时候,修改完宠物的信息之后,还是会弹出这个界面,可以用do-while循环,那么我们可以先写好这个界面,也就是程序的整体的框架,修改腿数的功能稍后再写。
分析一下这个界面,我们可以发现,第一行只是输出那些字而已,很简单,System.out就可以了,但是下面展示的动物信息,输出了猫、鸭子、海豚的信息,我们可以很容易就想到数组,遍历输出。(不要问我为什么很容易想到数组,因为所以没有道理)而用面向对象的思想来思考,这三个动物都属于动物,那么我们可以定义一个动物类的数组,然后把猫、鸭子、海豚都放到这个动物数组里面,然后给他们赋值遍历输出这个数组就输出了各个动物的信息了。
那么接下来我们就在AnimalMgr类实现这个界面:
1 public class AnimalMgr {
3 Animal [] animal=new Animal[3]; //创建一个动物数组,存放动物信息
4
5 public AnimalMgr() {
6 animal[0]=new Cat("小猫",4,"喵喵喵");
7 animal[1]=new Duck("小鸭",2,"嘎嘎嘎");
8 animal[2]=new Delph("海豚","嘤嘤嘤");
9 Scanner input=new Scanner(System.in);
10 int type=0;
11 do {
12 System.out.println("动物名字\t腿的条数\t动物叫");
13
14 System.out.println("是否修改数据:按0进行修改,其他数字退出");
15 type=input.nextInt();
16 if(type==0) {
17 //进行修改操作
18 }
19 }while(type==0);
20
21 }
我在AnimalMgr类里面声明了一个动物数组,重写了一个无参构造函数,将程序的主体放入这个无参构造函数内,然后创建了三个对象,小猫、鸭子和海豚,接着用do-while循环实现程序的循环操作。我们写一个Text类,只需要创建AnimalMgr的对象就可以了(整个main方法只有一行代码,就是创建AnimalMgr的对象,因为我的整个程序都在AnimalMgr类的无参构造方法AnimalMgr中),测试效果如下:
接下来我们就需要将动物的信息输出,也就是存在动物数组里的遍历输出,我们可以在AnimalMgr类下面写一个方法showInfo遍历动物数组,输出信息:
public void showInfo() {
for (Animal animal2 : animal) {
System.out.println(animal2.toString());
}
}
forech遍历输出,再使用Object类的toString方法,但是这时候输出的是数组对象的地址,那么我们可以重写一下toSting方法,我们可以在Animal类下重写:
package com.animal;
/**
* 动物类
* @author Administrator
*
*/
public abstract class Animal {
private String name; //动物名字
private String voice; //动物叫声
private int leg; //动物腿数
public int getLeg() {
return leg;
}
public void setLeg(int leg) {
this.leg = leg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVoice() {
return voice;
}
public void setVoice(String voice) {
this.voice = voice;
}
public Animal(){}
public Animal(String name,String voice) {
this.name=name;
this.voice=voice;
}
public Animal(String name,int leg,String voice) {
this.name=name;
this.leg=leg;
this.voice=voice;
}
/**
* 重写toString方法
*/
public String toString() {
return this.name+"\t"+this.leg+"\t"+this.voice;
}
}
这时候我们再次测试,会发现,海豚也输出了腿数,为0,这是因为我们重写的toString方法中,return有返回leg这个参数,即使海豚的构造方法和赋值都没有给这个参数,但是程序会默认给一个0,这时候我们可以在海豚类中再次重写toString方法:
public class Delph extends Animal {
public Delph(){}
public Delph(String name,String voice) {
super(name,voice);
}
/**
* 重写海豚toString方法
*/
public String toString() {
return this.getName()+"\t\t"+this.getVoice();
}
}
再次运行程序:
可以看到现在我们程序的界面和运行已经符合了需求,那么下一步,我们要做的就是修改腿数的操作了,对于修改腿数这个操作我们是通过LuSheng接口来实现的,而且只有小猫和小鸭可以修改,所以我们要在Cat类和Duck类中实现这个接口,而需求中,当猫输入的腿数不符合4的时候,鸭子不符合2的时候抛出异常,且输出程序界面(即程序异常,程序仍然会继续运行),所以我们先创建一个接口类LuSheng:
package com.animal;
/**
* 用于修改动物腿数的接口
*/
public interface LuSheng {
void chang ()throws Exception;
}
在这个接口中写了一个chang()方法,且这个方法抛出了一个异常,因为这个接口的实现类Cat和Duck类中实现这个接口的方法chang()也需要抛出异常,会抛给这个接口,所以这个接口中的chang()方法也需要继续往上抛异常。
我们先写Cat类的修改方法(为了方便,我将修改名字的方法也写在了chang()方法中):
package com.animal;
import java.util.Scanner;
public class Cat extends Animal implements LuSheng {
public Cat(){}
public Cat(String name,int leg,String voice) {
super(name,leg,voice);
}
/**
* 实现LuSheng接口chang方法,修改猫腿数,输入错误,抛出异常
*/
public void chang() throws Exception { //实现接口方法抛出异常,关键字thorws
Scanner input=new Scanner(System.in);
System.out.println("请输入猫的名字:");
String name=input.next();
this.setName(name); //将输入的名字通过Set方法修改父类name属性,修改名字
System.out.println("请输入猫的腿数:");
int leg=input.nextInt();
if(leg!=4) {
throw new Exception("猫的腿数为4!"); //当输入的腿数不为4的是和,方法体中抛出一个异常信息 throw
}else {
this.setLeg(leg); //输入的是4的话就将4修改父类中的leg
}
}
}
Duck类:
package com.animal;
import java.util.Scanner;
public class Duck extends Animal implements LuSheng{
public Duck(){}
public Duck(String name,int leg,String voice) {
super(name,leg,voice);
}
/**
* 实现LuSheng接口chang方法,修改鸭子腿数,输入错误,抛出异常
*/
public void chang() throws Exception{
Scanner input=new Scanner(System.in);
System.out.println("请输入鸭的名字:");
String name=input.next();
this.setName(name);
System.out.println("请输入鸭的腿数:");
int leg=input.nextInt();
if(leg!=2) {
throw new Exception("鸭的腿数为2!");
}else {
this.setLeg(leg);
}
}
}
Delph类(只需要修改名字,不需要实现接口,所以写一个方法修改名字就好了,也叫chang()):
package com.animal;
import java.util.Scanner;
public class Delph extends Animal {
public Delph(){}
public Delph(String name,String voice) {
super(name,voice);
}
//修改海豚名字的方法:
public void chang() {
Scanner input=new Scanner(System.in);
System.out.println("请输入海豚的名字:");
String name=input.next();
this.setName(name);
}
/**
* 重写海豚toString方法
*/
public String toString() {
return this.getName()+"\t\t"+this.getVoice();
}
}
修改的方法已经写完了,我们只需要在type==0的判断操作后面进行调用这些修改方法就可以了,按照程序的顺序,首先是猫,然后是鸭子,最后是海豚,而我们在创建这个动物数组的时候,是运用了子类指向父类的数据类型创建的对象,也就是多态的实现,但是chang()方法是各个子类独有的,指向父类的数据类型是调用不了子类独有的方法的,所以我们可以遍历这个数组,当这个数据类型是猫的时候,我们强制向下转型,就可以调用子类的方法了,整个AnimalMgr代码如下:
package com.animal;
import java.util.Scanner;
public class AnimalMgr {
//创建对象数组
Animal [] animal=new Animal[3];
public AnimalMgr() {
animal[0]=new Cat("小猫",4,"喵喵喵"); //创建猫对象,放进动物数组animal[0]中
animal[1]=new Duck("小鸭",2,"嘎嘎嘎"); //创建鸭子对象,放进动物数组animal[1]中
animal[2]=new Delph("海豚","嘤嘤嘤"); //创建海豚对象,放进动物数组animal[2]中
Scanner input=new Scanner(System.in);
int type=0;
do {
System.out.println("动物名字\t腿的条数\t动物叫");
showInfo();
System.out.println("是否修改数据:按0进行修改,其他数字退出");
type=input.nextInt();
if(type==0) {
for (Animal animal2 : animal) { //遍历数组
if(animal2 instanceof Cat) { //当前对象为猫类的时候,强制向下转型
try {
((Cat) animal2).chang(); //调用Cat类的chang()方法,且捕抓异常,输出异常信息之后调出循环
} catch (Exception e) {
e.printStackTrace();
break;
}
}else if(animal2 instanceof Duck) { //当前对象为鸭子类的时候,强制向下转型
try {
((Duck) animal2).chang(); //调用Duck类的chang()方法,且捕抓异常,输出异常信息之后调出循环
} catch (Exception e) {
e.printStackTrace();
break;
}
}else {
((Delph)animal2).chang();
}
}
}
}while(type==0);
}
/**
* 展示动物信息
*/
public void showInfo() {
for (Animal animal2 : animal) {
System.out.println(animal2.toString());
}
}
}
当我们在调用各个类的实现接口的方法chang()方法的时候,我们可以用try-catch处理异常,且catch处理完异常之后,break退出forech循环,返回主界面,最终实现效果:
总结
程序是比较简单,但是综合性其实比较强,对象数组,接口,异常,重写,当然实现的方法有很多,比如如果将叫声定义为父类的抽象方法,各个子类再去重写实现,遍历的时候在调用这个类的叫声的方法就可以。还有将腿数leg弄为Cat和Duck的私有属性,在通过接口进行传参设置,但是我个人觉得leg属性放在父类Animal中更符合现实逻辑,这就是我面向对象的修改动物腿数(使用接口并抛出异常)的全部思路。