[实验目的]
1、掌握java 继承中父类及其子类的定义方法。
2、掌握子类重写父类同名方法的方法。
3、掌握接口的用法。
(1) 学习如何定义接口 ;
(2) 掌握接口的实现方式 ;
(3) 使用实现了接口的类 ;
(4) 理解接口与抽象类的区别。
[实验要求]
- 复习理论教学中所学的内容。
- 认真进行实验预习,查阅参考书,书写源程序,书写实验预习报告。
- 认真总结实验并书写实验报告。
[实验内容]
- 类的继承性练习
(1) 程序源代码如下。
public class Student
{
protected String xm; //姓名,具有保护修饰符的成员变量
protected int xh;//学号
void setdata(String xm,int xh) //设置数据的方法
{
this.xm=xm;
this.xh=xh;
}
public void print() //输出数据的方法
{
System.out.println(xm+", "+xh);
}
}
(二)创建将被继承的类
(1) 程序功能:通过Student类产生子类CollegeStudent,其不仅具有父类的成员变量xm(姓名)、xh(学号),还定义了新成员变量xy(学院)、bj(bj)。在程序中调用了父类的print 方法,同时可以看出子类也具有该方法。
package project6;
public class CollegeStudent extends Student{
public static void main(String[] args) {
CollegeStudent c=new CollegeStudent();
c.setdata("李华", 1,"计科","大数据二班");
c.print();
}
private String xy;
private String bj;
void setdata(String xm,int xh,String xy,String bj) {
super.setdata(xm, xh);
this.xy=xy;
this.bj=bj;
}
public void print() {
super.print();
System.out.println(xy+" "+bj);
}
}
(三)了解成员方法的覆盖方式
编写覆盖了Object 类toString方法的一个类,并用System.out.println()输出该类的一个对象
package project6;
public class ToString {
private String s;
public static void main(String[] args) {
ToString t=new ToString("李华");
System.out.println(t.toString());
}
public ToString() {
}
public ToString(String s) {
this.s=s;
}
@Override
public String toString() {
return "我的名字是"+s;
}
}
试着以Point类为例,尝试为Object类的clone()和equals()方法进行覆盖,Point类包含私有成员x,y,构造方法1(包含两个参数a,b),构造方法2(参数为Point p),clone方法,equals方法,toString方法。用TestPoint类进行测试
ackage project6;
public class TestPoint {
public static void main(String[] args) throws CloneNotSupportedException {
Point p1=new Point(1,1);
Point p2;
Point p3=new Point(2,2);
Point p4=new Point(p3);
p2=(Point)p1.clone();
System.out.println("p1:"+p1.toString());
if(p2==null)
System.out.println("p2==null");
else
System.out.println("p2:"+p2.toString());
System.out.println("p3:"+p3.toString());
System.out.println("p4:"+p4.toString());
System.out.println("p1与p2相等吗?"+p1.equals(p2));
System.out.println("p1与p3相等吗?"+p1.equals(p3));
System.out.println("p3与p4相等吗?"+p3.equals(p4));
}
}
class Point{
private int x;
private int y;
public Point(int a,int b) {
x=a;
y=b;
}
public Point(Point p) {
this.x=p.x;
this.y=p.y;
}
@Override
public Object clone() throws CloneNotSupportedException{
Point p=new Point(this.x,this.y);
return p;
}
@Override
public boolean equals(Object o) {
if(o instanceof Point) {
if((x==((Point)o).x)&&(y==((Point)o).y))
return true;
else
return false;
}
else
return false;
}
@Override
public String toString() {
return "x="+x+","+"y="+y;
}
}
(四)this、super和super()的使用
程序功能:程序功能:说明this、super 和super()的用法。程序首先定义Point(点)类,然后创建点的子类Line(线)。最后通过TestLine类输出线段的长度。程序中通过super(a,b)调用父类Point 的构造方法为父类的x 和y 赋值。在子类Line 的setLine方法中,因为参数名和成员变量名相同,为给成员变量赋值,使用this 引用,告诉编译器是为当前类的成员变量赋值。在length 和toString 方法中使用父类成员变量时,使用super 引用,告诉编译器使用的是父类的成员变量。
package project6_1;
public class TestLine {
public static void main(String[] args) {
Line l=new Line(10,10);
l.setLine(new Point(6,7));
System.out.println("l的信息为:"+l.toString());
System.out.println("l的长度为:"+l.length());
}
}
class Point{
private int x;
private int y;
public Point(int a,int b) {
x=a;
y=b;
}
public Point(Point p) {
this.x=p.x;
this.y=p.y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
class Line extends Point{
private Point p;
public Line(int a, int b) {
super(a, b);
}
public void setLine(Point p) {
this.p=p;
}
public double length() {
double len=Math.sqrt((super.getX()-p.getX())*(super.getX()-p.getX())+(super.getY()-p.getY())*(super.getY()-p.getY()));
return len;
}
@Override
public String toString() {
return "父类的点为:("+super.getX()+","+super.getY()+")"+" ,子类的点为:("+p.getX()+","+p.getY()+")";
}
}
(五) 接口的实现与运用
实验任务 :
本实验的任务是设计和实现一个 Soundable 接口 , 该接口具有发声功能 , 同时还能够调节声音大小。 Soundable 接口的这些功能将会由 3 种声音设备来具体实现 , 它们分别是收音机 Radio 、随身昕 Walkman 和手机 Mobilephone 。最后还要设计一个应用程序类来使用这些实现了 Soundable 接口的声音设备类。程序运行时 , 先询问用户想听哪种设备 , 然后程序就会按照该设备的工作方式来发出声音。
实验步骤 :
(1) 仔细阅读程序, 并完成其中的代码1~代码3。
// InterfaceTest.java
import java.util.Scanner;
interface Soundable {
public void increaseVolume( );
public void decreaseVolume( );
public void stopSound( );
public void playSound( );
}
class Radio implements Soundable {
public void increaseVolume( ) {
System.out.println("增大收音机音量");
}
public void decreaseVolume( ) {
System.out.println("减小收音机音量");
}
public void stopSound( ) {
System.out.println("关闭收音机");
}
public void playSound( ) {
System.out.println("收音机播放广播");
}
}
class Walkman implements Soundable {
public void increaseVolume( ) {
System.out.println("增大随声听音量");
}
public void decreaseVolume( ) {
代码1 System.out.println("减小随声听音量"); //代码1// 输出减小随声听音量
}
public void stopSound( ) {
System.out.println("关闭随声听");
}
public void playSound( ) {
System.out.println("随声听发出音乐");
}
}
class Mobilephone implements Soundable {
public void increaseVolume( ) {
System.out.println("增大手机音量");
}
public void decreaseVolume( ) {
System.out.println("减小手机音量");
}
public void stopSound( ) {
System.out.println("关闭手机");
}
public void playSound( ) {
System.out.println("手机发出来电铃声");
}
}
class People {
private String name;
private int age;
public void listen(Soundable s) {
s.playSound( );
}
}
public class InterfaceTest {
public static void main(String[] args) {
int i;
People sportsman = new People( );
Scanner scanner = new Scanner(System.in);
Soundable[] soundDevice = new Soundable[3];
//往声音设备数组中放入能发声的设备
soundDevice[0] = new Radio( );
soundDevice[1] = new Walkman( );
soundDevice[2]=new Mobilephone(); //代码2//创建手机对象并赋值给soundDevice[2]
System.out.println("你想听什么? 请输入选择:0-收音机 1-随声听 2-手机");
i = scanner.nextInt( );
//开始听声音
sportsman.listen(soundDevice[i]);
soundDevice[i].increaseVolume( );
代码3 soundDevice[i].stopSound(); //代码3//调用stopSound( )方法
}
}
(2) 打开文本编辑器编辑 InterfaceTest.java 并保存 , 然后在Eclipse 环境中进行编译 , 编译的结果将会产生 6 个 class 文件 , 其中包括 Soundable.class, 虽然 Soundable 本身是一个接口 , 但编译之后也会产生 class 文件。
(3) 编译之后运行这个程序 , 观察所得结果。
思考
(1) 请问在 InterfaceTest 类中 ,SoundDevice[] 数组是什么类型的 , 该数组为什么能存放 3 种不同的对象 Radio、Walkman 和 Mobilephone 呢 ?
答
SoundDevice[] 数组是接口类型。
Radio、Walkman 和 Mobilephone三个类都继承了接口Soundable,接口Soundable是那三个类的父亲,父类的引用可以指向任意一个子类的对象,所以该数组为什么能存放 3 种不同的对象 Radio、Walkman 和 Mobilephone。
(2) 在程序中 ,Soundable 是一个接口 , 那么该接口是否可以被实例化呢 ? 请在InterfaceTest 类的 main() 方法中加入以下语句试验一下 , 并分析结果。
Soundable Sound=new Soundable(),
答
结果出现错误:
原因是Soundable 是一个接口,类似抽象类,只能声明引用,不能用new操作符给对象实例化。
(3) 现在假定要为程序增加一个闹钟类 Clock, 该类也实现 Soundable 接口 , 能够发出滴答声 , 请将以下的 Clock 类加入到 InterfaceTest.java 程序中 , 并在 InterfaceTest 类的 main() 方法中加入 SoundDevice[3] new Clock(); 语句。
class Clock implements Soundable{
public void Stopsound(){
System.out.println(" 关闭闹钟 ");
}
public void Playsound(){
system.out.println(" 闹钟发出滴答声 ");
}
}
修改之后 , 重新编译 InterfaceTest.java 并运行它 , 观察结果。
报错:
Exception in thread "main" java.lang.AbstractMethodError: Receiver class project6.Clock does not define or inherit an implementation of the resolved method 'abstract void increaseVolume()' of interface project6.Soundable.
原因应该是increaseVolume() 和 decreaseVolume() 方法没有实现。
(4) 在第 (3) 小题中由于新加入的 Clock 类仅仅实现了 Soundable 接口的stopsound() 和 playsound() 方法 , 而 increaseVolume() 和 decreaseVolume() 方法没有实现 , 因此它实质上是一个抽象类 , 而抽象类是不能实例化的 , 所以导致编译错误。但是按照常理 , 闹钟的滴答声确实是不可以增大或减小的 , 那么如何解决这个问题呢 ? 现在请在 Clock 类中加入下面两个含 {} 空方法体的方法实现 , 再编译运行程序 , 看看会有什么变化。
public void increaseVolume(){}
public void decreaseVolume(){}
答
成功运行,没有报错:
(5) 现在请模仿本实验的程序设计出一个自己的接口程序 , 要求先设计一个 moveable 可移动接口 , 然后分别设计 3 个类 , 即汽车 Car 、轮船 Ship 、飞机 Aircraft 来实现该接口 , 最后设计一个应用程序来使用它们。
package project6;
import java.util.*;
interface moveable{
public void startmove();
public void stopmove();
public void increasespeed();
public void decreasespeed();
}
class Car implements moveable{
public void startmove() {
System.out.println("汽车跑");
}
public void stopmove() {
System.out.println("汽车停");
}
public void increasespeed() {
System.out.println("汽车加速");
}
public void decreasespeed() {
System.out.println("汽车减速");
}
}
class Ship implements moveable{
public void startmove() {
System.out.println("船跑");
}
public void stopmove() {
System.out.println("船停");
}
public void increasespeed() {
System.out.println("船加速");
}
public void decreasespeed() {
System.out.println("船减速");
}
}
class Aircraft implements moveable{
public void startmove() {
System.out.println("飞机飞");
}
public void stopmove() {
System.out.println("飞机停");
}
public void increasespeed() {
System.out.println("飞机加速");
}
public void decreasespeed() {
System.out.println("飞机减速");
}
}
public class MoveableTest {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
moveable[] move=new moveable[3];
move[0]=new Car();
move[1]=new Ship();
move[2]=new Aircraft();
System.out.println("选择一种交通工具:0-汽车 1-船 2-飞机");
int i=input.nextInt();
show(move[i]);
}
public static void show(moveable m) {
m.startmove();
m.increasespeed();
m.decreasespeed();
m.decreasespeed();
}
}
(六)深复制(掌握Clonable接口的使用)
重写书中的程序清单10-6的Course类,增加一个clone方法,执行students域的深度复制。
package project6;
public class TestCourse {
public static void main(String[] args) {
Course course1 = new Course("Data Structures");
Course course2 = new Course("Database Systems");
Course course3;//重新定义一个Course对象
course1.addStudent("Peter Jones");
course1.addStudent("Brian Smith");
course1.addStudent("Anne Kennedy");
course2.addStudent("Peter Jones");
course2.addStudent("Steve Smith");
course2.dropStudent("Peter Jones");//删除course2里的学生Peter Jones
course3=(Course) course1.clone();//将course1复制到course3
System.out.println("Number of students in course1: "
+ course1.getNumberOfStudents());
String[] students1 = course1.getStudents();
for (int i = 0; i < course1.getNumberOfStudents(); i++)
System.out.print(students1[i] + ", ");
System.out.println();
System.out.println("Number of students in course2: "
+ course2.getNumberOfStudents());
String[] students2 = course2.getStudents();
for (int i = 0; i < course2.getNumberOfStudents(); i++)
System.out.print(students2[i] + ", ");
System.out.println();
System.out.println("除了删除了一个学生,以上其余的为书中原有输出");
//除了删除了一个学生,其余为书中原有输出
//修改course3的学生姓名前输出一次
System.out.println();
System.out.println("以下为本实验重新加入的输出:");
System.out.println("修改crouse3前:");
System.out.println("Number of students in course3: "
+ course3.getNumberOfStudents());
String[] students3 = course3.getStudents();
for (int i = 0; i < course3.getNumberOfStudents(); i++)
System.out.print(students3[i] + ", ");
//修改course3的学生姓名后输出一次
students3[1]="Li Hua";
System.out.println();
System.out.println("修改crouse3后:");
System.out.println("Number of students in course3: "
+ course3.getNumberOfStudents());
for (int i = 0; i < course3.getNumberOfStudents(); i++)
System.out.print(students3[i] + ", ");
// System.out.println(course1);
// System.out.println(course3);
}
}
class Course implements Cloneable{
private String courseName;
private String[] students = new String[100];
private int numberOfStudents;
public Course(String courseName) {
this.courseName = courseName;
}
public void addStudent(String student) {
students[numberOfStudents] = student;
numberOfStudents++;
}
public String[] getStudents() {
return students;
}
public int getNumberOfStudents() {
return numberOfStudents;
}
public String getCourseName() {
return courseName;
}
//删除学生
public void dropStudent(String student) {
for(int i=0;i<numberOfStudents;i++) {
if(students[i]==student) {
for(int j=i;j<numberOfStudents;j++) {
students[j]=students[j+1];
}
numberOfStudents--;
}
break;
}
}
@Override//重写clone()函数
public Object clone() {
try {
Course c=(Course)super.clone();
String[] cs=new String[numberOfStudents];
//cs=this.students.clone();
for(int i=0;i<numberOfStudents;i++) {
c.students[i]=this.students[i];
}
return c;
}
catch(CloneNotSupportedException ex) {
return null;
}
}
}