# JAVA 实验二:掌握继承与接口的原理和使用方法
# 实验任务书
根据 “实验 2 说明” 文件夹中给出的源程序 Main.java,StuGroup.java(假设其中的类实现了接口 MyGroup),Student.java(其中的类 Student 是抽象类, print ( ) 方法是抽象方法)。
要求:
(1)创建源程序 MyGroup.java,其中的内容是接口 MyGroup 的声明。
(2)创建源程序 Undergraduate.java,其中的类 Undergraduate 继承源程序 Student.java 中的抽象类 Student,实现其中的抽象方法 print ( )。
(3)创建源程序 Postgraduate.java,其中的类 Postgraduate 继承源程序 Student.java 中的抽象类 Student,实现其中的抽象方法 print ( ),并且,在父类的基础上增加两个新的属性:导师和研究方向。
(4)StuGroup.java,MyGroup.java,Undergraduate.java,Postgraduate.java 和 Student.java 在同一个包中。Main.java 在无名包中。
(5)在程序中做出注释,哪些地方是上转型?哪些地方体现了运行时多态?
上述源程序可以完成如下任务:
使用者根据提示信息,选择从键盘上输入本科生或者研究生的学生信息,可以添加学生信息、删除学生信息、显示学生信息、按照学生成绩排序等。
# 解决
# 项目文件结构
src (源文件夹)
students (包)
Student.java (学生信息类(抽象类))
StuGroup.java (实现接口 MyGroup)
MyGroup.java (接口)
Undergraduat.java (本科生类)
Postgraduate.java (研究生类)
Main.java (位于无名包下)
├── src (源文件夹)
│ ├── students (包)
│ ├── Student.java (学生信息类(抽象类))
│ ├── StuGroup.java (实现接口 MyGroup)
│ ├── MyGroup.java (接口)
│ ├── Undergraduat.java (本科生类)
│ └── Postgraduate.java (研究生类)
│ ├── Main.java (位于无名包下)
# Main.java
import students.*; | |
import java.util.*; | |
public class Main | |
{ | |
public static void main(String[] args) | |
{ | |
Scanner in=new Scanner(System.in); | |
StuGroup group=new StuGroup(20); //group 可以容纳 20 个学生 | |
int num; // 实际输入的学生信息 | |
System.out.println("要创建本科生信息表还是研究生信息表?\nA.本科生\tB.研究生"); | |
Scanner reader = new Scanner(System.in); | |
char choice = reader.next().charAt(0);//charAt (index) 用于返回索引处的字符 | |
switch(choice) | |
{ | |
case 'A': | |
case 'a': | |
System.out.println("请问要输入多少个本科生的信息?"); | |
num = reader.nextInt(); | |
Student stuB[] = new Undergraduate[num];//stuB [] 对象数组是子类 Undergraduate 对象的上转型对象 | |
for(int i=0;i<num;i++) | |
{ | |
System.out.println("请输入第"+(i+1)+"位学生的学号,姓名,数学、计算机成绩"); | |
stuB[i] = new Undergraduate(reader.nextInt(), reader.next(),reader.nextDouble(), reader.nextDouble());// 从键盘输入学生信息,并赋值给学生 | |
group.addStu(stuB[i]);// 参数为引用类型 | |
} | |
group.addStu(new Undergraduate(1312,"shgd",99,98.5)); | |
group.addStu(new Undergraduate(1316,"ddsa",89,88.5)); | |
System.out.println("添加后所有本科生的信息为:"); | |
group.print(); | |
break; | |
case 'B': | |
case 'b': | |
System.out.println("请问要输入多少个研究生的信息?"); | |
num = reader.nextInt(); | |
Student[] stuY = new Postgraduate[num]; //stuY [] 对象数组是子类 Postgraduate 对象的上转型对象 | |
for(int i=0;i<num;i++) | |
{ | |
System.out.println("请输入第"+(i+1)+"位学生的学号,姓名,数学、计算机成绩,导师和研究方向,以空格隔开"); | |
stuY[i] = new Postgraduate(reader.nextInt(), reader.next(),reader.nextDouble(), reader.nextDouble(),reader.next(),reader.next()); | |
group.addStu(stuY[i]); | |
} | |
group.addStu(new Postgraduate(1312,"shgd",99,98.5,"xyy","os")); // 与本科生对比体现运行时的多态性 | |
group.addStu(new Postgraduate(1316,"ddsa",89,88.5,"wcl","rgzn")); | |
System.out.println("添加后所有研究生信息为:"); | |
group.print(); | |
break; | |
default: | |
System.out.println("输入错误!!!"); | |
break; | |
} | |
System.out.println("是否需要删除某个学生信息?输入y确定,输入其他继续:"); | |
String words=reader.next(); | |
if(words.equals("Y")||words.equals("y")) | |
{ | |
System.out.println("删除第几个同学?"); | |
group.removeStu(reader.nextInt()); | |
System.out.println("最新的学生信息为:"); | |
group.print(); | |
group.sort(1); | |
group.print();// 按照数学成绩排序后输出 | |
group.sort(2); | |
group.print();// 按照计算机成绩排序后输出 | |
in.close(); | |
} | |
else | |
{ | |
System.out.println("最新的学生信息为:"); | |
group.print(); | |
group.sort(1); | |
group.print();// 按照数学成绩排序后输出 | |
group.sort(2); | |
group.print();// 按照计算机成绩排序后输出 | |
in.close(); | |
} | |
} | |
} |
# Student.java
package students; | |
public abstract class Student | |
{ | |
int num; | |
String name; | |
double mathscore; | |
double computerscore; | |
Student(int num,String name,double mathscore,double computerscore) | |
{ | |
this.num=num; | |
this.name=name; | |
this.mathscore=mathscore; | |
this.computerscore=computerscore; | |
} | |
// 学号 | |
public void getnum(int num) | |
{ | |
this.num=num; | |
} | |
public int setnum() | |
{ | |
return this.num; | |
} | |
// 姓名 | |
public void getname(String name) | |
{ | |
this.name=name; | |
} | |
public String setname() | |
{ | |
return this.name; | |
} | |
// 数学成绩 | |
public void getmath(double mathscore) | |
{ | |
this.mathscore=mathscore; | |
} | |
public double setmath() | |
{ | |
return mathscore; | |
} | |
// 计算机成绩 | |
public void getcomputer(double computerscore) | |
{ | |
this.computerscore=computerscore; | |
} | |
public double setcomputer() | |
{ | |
return computerscore; | |
} | |
abstract void print(); | |
} |
# StuGroup.java
package students; | |
public class StuGroup implements MyGroup //StuGroup 实现了 MyGroup 接口 | |
{ | |
private Student[] stu; | |
private int length; // 实际容量 | |
public StuGroup(int len) | |
{ | |
stu=new Student[len]; | |
} | |
private boolean isOverflow() // 辅助方法,判断数组是否溢出 | |
{ | |
if(this.length<stu.length) | |
return false; | |
else | |
return true; | |
} | |
public boolean isEmpty() // 判断数组是否为空 | |
{ | |
return this.length==0; | |
} | |
public boolean addStu(Student x) // 添加学生信息 | |
{ | |
if(isOverflow()) | |
return false; | |
stu[length++]=x; | |
return true; | |
} | |
public Student removeStu(int index) // 删除指定位置的学生信息 | |
{ | |
if(isEmpty()) | |
{ | |
System.out.println("学生表空的~~~删除失败~~~~"); | |
return null; | |
} | |
Student temp=stu[index-1]; | |
for(int i=index-1;i<this.length-1;i++) | |
{ | |
stu[i]=stu[i+1]; | |
} | |
this.length--; | |
return temp; | |
} | |
public void sort(int x) // 按照成绩排序,参数 1 按数学排序,参数 2 按计算机排序 | |
{ | |
if(x==1) | |
{ | |
for(int i =0;i<length-1;i++) | |
for(int j =0;j<length-i-1;j++)// 冒泡排序 | |
{ | |
if(stu[j].setmath()<stu[j+1].setmath()) | |
{ | |
Student temp;// 声明一个对象(引用),用于交换 | |
temp =stu[j+1]; | |
stu[j+1] = stu[j]; | |
stu[j] = temp; | |
} | |
} | |
System.out.println("按数学成绩降序排列为:"); | |
} | |
else if(x==2) | |
{ | |
for(int i =0;i<length-1;i++) | |
for(int j =0;j<length-i-1;j++) | |
{ | |
if(stu[j].setcomputer()<stu[j+1].setcomputer()) | |
{ | |
Student temp; | |
temp =stu[j+1]; | |
stu[j+1] = stu[j]; | |
stu[j] = temp; | |
} | |
} | |
System.out.println("按计算机成绩降序排名:"); | |
} | |
} | |
public void print() | |
{ | |
for(int i =0;i<length;i++) | |
stu[i].print();// 调用 Student 子类的 print()方法 | |
} | |
} |
# MyGroup.java
package students; | |
public interface MyGroup { | |
boolean addStu(Student x); | |
Student removeStu(int index); | |
void sort(int x); | |
} |
# Undergraduat.java
package students; | |
public class Undergraduate extends Student { | |
public Undergraduate(int num, String name, double mathscore, double computerscore) { | |
super(num, name, mathscore, computerscore); | |
// TODO 自动生成的构造函数存根 | |
} | |
void print() { | |
System.out.println("本科生信息:"+"学号:"+setnum()+" ,姓名:"+setname()+" ,数学成绩:"+setmath()+" ,计算机成绩:"+setcomputer()); | |
} | |
} |
# Postgraduate.java
package students; | |
public class Postgraduate extends Student { | |
String teacher; | |
String direction; | |
public Postgraduate(int num, String name, double mathscore, double computerscore, String teacher, String direction) { | |
super(num, name, mathscore, computerscore); | |
this.teacher = teacher; | |
this.direction = direction; | |
} | |
void getTeacher(String teacher) | |
{ | |
this.teacher =teacher; | |
} | |
String setTeacher() | |
{ | |
return this.teacher; | |
} | |
void getDirection(String direction) | |
{ | |
this.direction =direction; | |
} | |
String setDirection() | |
{ | |
return this.direction; | |
} | |
void print() { | |
System.out.println("研究生学生信息为:"+"学号:"+setnum()+",姓名:"+setname()+",数学成绩:"+setmath()+",计算机成绩:"+setcomputer()+"导师:"+setTeacher()+",研究方向:"+setDirection()); | |
} | |
} |
# 实验报告
# 实验目的
掌握继承与接口的原理和使用方法。
# 实验内容
根据 “实验 2 说明” 文件夹中给出的源程序 Main.java,StuGroup.java(假设其中的类实现了接口 MyGroup),Student.java(其中的类 Student 是抽象类, print ( ) 方法是抽象方法)。
要求:
(1)创建源程序 MyGroup.java,其中的内容是接口 MyGroup 的声明。
(2)创建源程序 Undergraduate.java,其中的类 Undergraduate 继承源程序 Student.java 中的抽象类 Student,实现其中的抽象方法 print ( )。
(3)创建源程序 Postgraduate.java,其中的类 Postgraduate 继承源程序 Student.java 中的抽象类 Student,实现其中的抽象方法 print ( ),并且,在父类的基础上增加两个新的属性:导师和研究方向。
(4)StuGroup.java,MyGroup.java,Undergraduate.java,Postgraduate.java 和 Student.java 在同一个包中。Main.java 在无名包中。
(5)在程序中做出注释,哪些地方是上转型?哪些地方体现了运行时多态?
上述源程序可以完成如下任务:
使用者根据提示信息,选择从键盘上输入本科生或者研究生的学生信息,可以添加学生信息、删除学生信息、显示学生信息、按照学生成绩排序等。
# 实验结果
- 本科生,有删除信息
- 本科生,无删除信息
- 研究生,有删除信息
- 研究生,无删除信息
# 实验分析
# 1、接口 Interface
在这次实验中,MyGroup 是接口,接口是一个抽象类型,是抽象方法的集合。类通过继承接口的方式,从而来继承接口的抽象方法。在 MyGroup 接口中,定义了 addStu () 增加学生信息函数、Student 类方法 removeStu () 删除学生信息类的函数和 sort () 排序函数。
接口中每一个方法是隐式抽象的,接口中的方法会被隐式的指定为 public abstract,所以其他类引入接口时,只需要 implements 接口,而这个类能实现接口中的方法。
# 2、上转型
上转型和下转型都是父类和子类之间对象的转换,上转型是通过子类对象 (小范围) 实例化父类对象 (大范围),在本次实验中体现上转型的是:
switch(choice) | |
{ | |
case 'A': | |
case 'a': | |
System.out.println("请问要输入多少个本科生的信息?"); | |
num = reader.nextInt(); | |
Student stuB[] = new Undergraduate[num];//stuB [] 对象数组是子类 Undergraduate 对象的上转型对象 |
这里 Student stuB [] = new Undergraduate [num]; stuB [] 对象数组是子类 Undergraduate 对象的上转型对象,Student 是父类,Undergraduate 是子类,通过父类定义一个对象,子类新建一个内存空间实例化父类的这个对象。
# 3、运行时多态
运行时多态的三个条件是
(1)、要有继承;
(2)、要有重写;
(3)、父类引用指向子类对象。
这里学生类的子类有两个,本科生和研究生,研究生类比本科生类多两个属性,因此在实现接口的方法(例如 addStu () 增加学生信息函数体现多态)
Student stuB[] = new Undergraduate[num]; | |
for(int i=0;i<num;i++) | |
{ | |
System.out.println("请输入第"+(i+1)+"位学生的学号,姓名,数学、计算机成绩"); | |
stuB[i] = new Undergraduate(reader.nextInt(), reader.next(),reader.nextDouble(), reader.nextDouble());// 从键盘输入学生信息,并赋值给学生 | |
group.addStu(stuB[i]);// 参数为引用类型 | |
} | |
group.addStu(new Undergraduate(1312,"shgd",99,98.5)); | |
group.addStu(new Undergraduate(1316,"ddsa",89,88.5)); |
对比研究生如下:
Student[] stuY = new Postgraduate[num]; | |
for(int i=0;i<num;i++) | |
{ | |
System.out.println("请输入第"+(i+1)+"位学生的学号,姓名,数学、计算机成绩,导师和研究方向,以空格隔开"); | |
stuY[i] = new Postgraduate(reader.nextInt(), reader.next(),reader.nextDouble(), reader.nextDouble(),reader.next(),reader.next()); | |
group.addStu(stuY[i]); | |
} | |
group.addStu(new Postgraduate(1312,"shgd",99,98.5,"xyy","os")); | |
group.addStu(new Postgraduate(1316,"ddsa",89,88.5,"wcl","rgzn")); |
Undergraduate 类和 Postgraduate 类都是 Student 的子类,可以看出上述函数 Student 父类分别定义两个对象,子类对象上转型到父类对象,使得 addStu () 函数添加的对象个数根据子类不同而不同。本科生类中,有四个属性,研究生六个属性,完美实现这个函数。
# 实验总结
1、接口是一个抽象类型,是抽象方法的集合,接口通常以 interface 来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
2、上转型: 通过子类对象实例化父类对象。
优点:提高了代码的简洁性。
语法规则:<父类型> < 引用变量名 > = new < 子类型 >();
此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,不是父类的方法。此时通过父类引用变量无法调用子类特有的方法。
3、运行时多态:
运行时多态的三个条件是
(1)、要有继承;
(2)、要有重写;
(3)、父类引用指向子类对象。
优点:提高了代码的扩展性,前期定义的代码可以使用后期的内容。
缺点:前期定义的内容不能调用后期子类的特有方法,调用的只能是父类。但如果是继承子类覆盖了父类方法,多态调用的仍是子类的方法