实验1 Java 程序的编辑、编译、运行环境
一、实验要求
●实验目的:
- 掌握JDK的安装与配置方法;
- 能够编写简单的Java程序。
●参考学时:2学时
●基本要求:
- 设置path环境变量(若尚未安装JDK,先安装该软件;若Path已设置,重新设置该值);
- 编写HelloWorld程序,并在命令模式下编译运行;
- 在Eclipse环境下编写计算整数N(N<=20)的阶乘程序FactorialTest类;
- 在Eclipse环境下编写按图1.1格式输出乘法口诀表的程序NineMultiplication。
图1.1乘法口诀表程序运行效果
注:其中,每行的多个乘法公式之间用‘\t’分割
●实验提示:
- path是Windows系统的一个环境变量,内容为分号分隔的若干个文件夹名称(如“C:\Windows;C:\Windows\System32;C:\java\bin”)。在CMD窗口中输入一个命令时,如果没有指定命令文件所在的文件夹位置,Windows首先在命令行提示符所表示的文件夹(称作“当前文件夹”或“默认文件夹”)内查找命令文件。如果此文件夹内确实存在命令文件,则开始运行该命令,否则依次查看path环境变量中的每个文件夹,直到找到命令文件或查找失败为止。根据上述特点,我们可以把包含“javac.exe”命令文件和“java.exe”命令文件的文件夹(通常为“c:\program files\java\jdk1.6.xxx\bin”)添加到path环境变量中,从而简化CMD窗口中输入的命令。
- Eclipse环境下应首先点击菜单“文件”-“新建”-“Java项目”创建Java项目(图1.2)。创建项目后点击菜单“文件”-“新建”-“类”(或按下“Alt-Shift-n”并选择“类”)在项目中新建Java类(图1.3):
图1.2 Eclipse环境下创建Java项目
图1.3“Alt-Shift-n”菜单
- 如果用函数的递归调用来实现阶乘计算部分,定义函数时应声明为static,如:
static long getFact(int i){
…
}
●评分标准:满分10分
- 按要求正确完成所有任务(4项任务各2分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表1.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | path环境变量设置 | 在cmd窗口中分别输入java和javac命令 | 不会出现“…不是内部或外部命令,也不是可运行的程序…” |
2 | 阶乘算法 | 无 | 阶乘结果正确 |
3 | 乘法口诀 | 无 | 乘法口诀表显示格式正确 |
●思考题
- 如果阶乘结果溢出,该怎么办?
- 若要输出如下乘法口诀表,该如何修改代码?
图1.4 第二种输出格式
图1.5第三种输出格式
二、代码实现
编写HelloWorld程序
package 实验一;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
编写计算整数N(N<=20)的阶乘程序FactorialTest类
package 实验一;
import java.util.Scanner;
public class FactorialTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long k = sc.nextInt();
System.out.println(getFact(k));
}
public static long getFact(long k) {
long k1;
if (k == 1 || k == 0) {
k1 = 1;
return k1;
} else if (k < 0) {
System.out.println("请重新输入");
} else {
k = getFact(k - 1) * k;
}
return k;
}
}
编写按图1.1格式输出乘法口诀表的程序NineMultiplication
package 实验一;
public class NineMultiplication {
public static void main(String[] args) {
NineFirst();
}
public static void NineFirst() {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + "*" + i + "=" + j * i + "\t");
}
System.out.println();
}
}
}
实验2 类和对象的使用
一、实验要求
●实验目的:
- 掌握类的定义过程,理解利用类进行封装的意义:定义属性和成员方法,定义构造方法,构造方法重载,this关键字(this前缀,this方法);理解构造方法的作用
- 掌握编写setter和getter方法,理解setter和getter方法的作用
- 掌握对象的创建过程:对象与类的关系,构造方法与对象创建的关系
- 掌握类的组合关系:通过组合定义更为灵活和复杂的类,同时达到代码重用目的
●参考学时:2学时
●基本要求:
- 实现一个Point类,该类包含表示坐标的两个int型变量x、y,构造方法Point()和Point(int x, int y),返回x值和y值的int getX()和int getY()方法,设置x值和y值的void setX(int x)和void setY(int y)方法,计算两点间距离的double distance(Point p)方法。其中计算平方根的方法是Math.sqrt(),如:double d=Math.sqrt(2);
- 实现一个Circle类,该类包含表示圆心的Point型变量center,表示半径的int型变量radius,以及构造方法Circle()、Circle(int x,int y,int r)、Circle(Point c,int r),返回周长和面积的double perimeter()、double area()方法,返回两个圆是否为同一个圆(返回0)、同心圆(返回1)、相交的圆(返回2)、分离的圆(返回3)、包含的圆(返回4)等关系的int relation(Circle c)等方法。π值可以用Math.PI常量。
- 实现测试上述两个类的Test类。该类在main方法中分别创建若干个Point对象和Circle对象,并调用相关方法,输出方法的返回值,验证其正确性。
- 根据relation()方法的返回值,main方法输出“同一圆”、“同心圆”等内容。
●实验提示:
- 同一类对象的属性值不同,对象之间才会有区别,所以创建对象后需要对其属性进行适当赋值,此过程俗称对象的“初始化”。由于对象创建时自动调用构造方法,所以把初始化工作交给构造方法完成更加合理,这就是为什么定义构造方法的原因。
- 为了初始化方便,面向对象技术要求类的编写人员应提供各种构造方法,即运用构造方法重载。其结果是使用该类的其他开发人员可以根据自己的需要决定调用哪一个构造方法。
- 不能在构造方法中编写任何输入输出有关的代码,否则该方法不仅具有初始化功能,还具有其它功能,从而违背面向对象程序设计的一个重要准则:“单一职责”原则。
- Circle类将圆心定义为Point对象,这种现象称之为类和类之间的“组合关系”(Composition)。利用构造方法初始化Circle对象时,必须实例化center成员对象,以保证此类属性非空,否则将会引起空对象错误。如以下两个构造方法都没有对center实例化:
Circle(){
radius=0;
}
Circle(int x,int y,int r){
center.x=x;
center.y=y;
radius=r;
}
正确的代码应该是:
Circle(){
center=new Point(0,0);
radius=0;
}
Circle(int x,int y,int r){
center=new Point(x,y);
radius=r;
}
- Circle(Point c,int r)构造方法中,可以把c对象直接赋给center,因为c是调用该构造方法之前实例化的Point对象的引用。
- Test类可以根据relation()方法的返回值显示两个圆的位置关系信息。在relation()方法中不应输出任何信息。
●评分标准:满分10分
- 按要求正确完成所有任务;标识符命名规范;(Point和Circle各3分,Test 2分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求;(1分)
- 实验报告撰写规范,内容完整。(1分)
●测试案例:
表2.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | Point类成员方法 | 创建p1=new Point()和p2=new Point(0,3) | P1.getX()值为0,p1.getY()值为0,p2.getX()值为0,p2.getY()值为3,p1.distance(p2) 值为3 |
2 | Circle()构造方法 | 创建Circle() | 圆心应为(0,0),半径、面积和周长皆为0 |
3 | Circle(int,int,int)构造方法、其它成员方法 | 创建Circle(1,1,10) | 圆心应为(1,1),半径为10,面积为314.159,周长为62.831 |
4 | Circle(int,int,int)构造方法中radius为负数 | 创建Circle(1,1,-1) | 圆心应为(1,1),半径、面积和周长皆为0 |
5 | Circle(Point,int)构造方法 | 利用(1,1)点p创建Circle(p,10) | 圆心应为(1,1),半径为10,面积为314.159,周长为62.831 |
6 | Circle(Point,int)构造方法中radius为负数 | 利用(1,1)点p创建Circle(p,-1) | 圆心应为(1,1),半径、面积和周长皆为0 |
7 | Circle(Point,int)构造方法中Point为空对象 | 创建Circle(null,10) | 圆心应为(0,0),半径为10,面积为314.159,周长为62.831 |
8 | Point类的set方法 | p.setX(10),p.setY(10) | 该点的坐标被变更为(10,10) |
9 | Circle类的set方法 | c.setRadius(20) | 该圆的半径被变更为20 |
10 | Circle类的set方法 | c.setRadius(-20) | 该圆的半径保持不变 |
11 | Circle类的set方法 | c.setCenter(new Point(20,20)) | 该圆的圆心被变更为(20,20)点 |
12 | Circle类的set方法 | c.setCenter(null) | 该圆的圆心保持不变 |
13 | relation方法 | 创建Circle()和Circle(0,0,0),并调用relation() | 输出“同一圆” |
14 | relation方法 | 创建Circle()和Circle(0,0,1) 并调用relation() | 输出“同心圆” |
15 | relation方法 | 创建Circle(0,0,10)和Circle(1,1,5) 并调用relation() | 输出“包含的圆” |
16 | relation方法 | 创建Circle(0,0,10)和Circle(0,5,10) 并调用relation() | 输出“相交的圆” |
17 | relation方法 | 创建Circle(0,0,10)和Circle(0,20,10) 并调用relation() | 输出“分离的圆” |
●思考题
- 为什么distance()和relation()方法只需要一个参数?提供两个或更多的参数有什么缺点?
- relation()方法只返回整数,而不是在方法内部直接输出判断结果。这么做有什么优点?
- Circle(Point c,int r)构造方法中,如果c为空对象该如何处理?
- 利用Point类,还可以实现哪些类似于Circle的类?
- 还可以定义哪些圆的关系?
二、代码实现
Point类
package 实验二;
public class Point//类声明:构建一个Point类
{
private int x;
private int y;
public Point()//无参的构造方法
{
this(0,0);
}
public Point(int x, int y)//有参的构造方法
{
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public double distance(Point p2)//构造方法:计算两点间的距离
{
double pointDistance;
pointDistance = Math.sqrt((x - p2.x) * (x - p2.x) + (y - p2.y) * (y - p2.y));
return pointDistance;
}
}
Circle类
package 实验二;
public class Circle {
private int radius;
private Point center;
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
if (radius < 0)// 半径小于零返回初值
{
return;
}
this.radius = radius;
}
public Point getCenter() {
if (center==null){
center =new Point(0,0);
}
return center;
}
public void setCenter(Point center) {
if (center == null)//圆心坐标为空返回初值
{
return;
}
this.center = center;
}
public Circle() {
this(0, 0, 0);
}
//有参的构造方法
public Circle(int a, int b, int radius) {
center = new Point(a, b);
if (radius < 0)//半径小于零为其赋零
{
this.radius = 0;
} else
this.radius = radius;
}
//有参的构造方法
public Circle(Point center, int radius) {
this.center = center;
this.radius = radius;
}
//构造方法:求圆的面积的方法
public double computerArea() {
double area = Math.PI * radius * radius;
return area;
}
//构造方法:求圆的周长的方法
public double computerPerimeter() {
double perimeter = 2 * Math.PI * radius;
return perimeter;
}
//构造方法:两比较个圆的位置(同一圆、同心圆、相交圆、分离圆、包含圆)
public int relation(Circle c) {
Point k1 = center;
Point k2 = c.center;
double d = k1.distance(k2);
if (d == 0)//同心圆
{
if (radius == c.radius) {
return 0;
}
return 1;
}
if ((d != 0) && (Math.abs(radius - c.radius) <= d && d < (radius + c.radius)))//相交圆
{
return 2;
}
if ((d != 0) && (d >= (radius + c.radius)))//分离圆
{
return 3;
} else//包含圆
{
return 4;
}
}
}
Test类
package 实验二;
import java.text.DecimalFormat;
public class Test//构建一个Test类
{
public static void main(String[] args)
{
DecimalFormat baoLiuXiaoShu;
baoLiuXiaoShu=new DecimalFormat("0.00");
// 第一个
Point p1;
Point p2;
p1=new Point(0,0);
p2=new Point(0,3);
/*
p1.setX(10);
p1.setY(10);
*/
System.out.println("("+p1.getX()+","+ p1.getY()+")"+"与"+"("+p2.getX()+","+p2.getY()+")"+"两点间距离为:");
System.out.println(baoLiuXiaoShu.format(p1.distance(p2)));
// 第二个
Circle q1;
Circle q2;
Circle q;
/*
q=new Circle(0,0,0);
*/
q=new Circle(1,1,10);
/*
q=new Circle(1,1,-1);
*/
/*
q.setRadius(20);
*/
/*
q.setRadius(-20);
*/
/*
q.setCenter(new Point(20,20));
*/
/*
q.setCenter(null);
*/
System.out.println("圆心坐标为"+"("+q.getCenter().getX()+","+q.getCenter().getY()+")"+" 圆的半径为"+q.getRadius()+" 圆的面积为:"+baoLiuXiaoShu.format(q.computerArea())+" 圆的周长为:"+baoLiuXiaoShu.format(q.computerPerimeter()));
/*
q1=new Circle(0,0,0);//同一圆
q2=new Circle(0,0,0);
*/
/*
q1=new Circle(0,0,0);//同心圆
q2=new Circle(0,0,1);
*/
q1=new Circle(0,0,10);//包含圆
q2=new Circle(1,1,5);
/*
q1=new Circle(0,0,10);//相交圆
q2=new Circle(0,5,10);
*/
/*
q1=new Circle(0,0,10);//分离圆
q2=new Circle(0,20,10);
*/
if(q1.relation(q2)==0)
{
System.out.println("圆心为"+"("+q1.getCenter().getX()+","+q1.getCenter().getY()+")"+" 半径为"+q1.getRadius()+" 与 圆心为"+"("+q2.getCenter().getX()+","+q2.getCenter().getY()+")"+" 半径为"+q2.getRadius()+" 的两圆是同一个圆");
}
if(q1.relation(q2)==1)
{
System.out.println("圆心为"+"("+q1.getCenter().getX()+","+q1.getCenter().getY()+")"+" 半径为"+q1.getRadius()+" 与 圆心为"+"("+q2.getCenter().getX()+","+q2.getCenter().getY()+")"+" 半径为"+q2.getRadius()+" 的两个圆是同心圆");
}
if(q1.relation(q2)==2)
{
System.out.println("圆心为"+"("+q1.getCenter().getX()+","+q1.getCenter().getY()+")"+" 半径为"+q1.getRadius()+" 与 圆心为"+"("+q2.getCenter().getX()+","+q2.getCenter().getY()+")"+" 半径为"+q2.getRadius()+" 的两个圆是相交的圆");
}
if(q1.relation(q2)==3)
{
System.out.println("圆心为"+"("+q1.getCenter().getX()+","+q1.getCenter().getY()+")"+" 半径为"+q1.getRadius()+" 与 圆心为"+"("+q2.getCenter().getX()+","+q2.getCenter().getY()+")"+" 半径为"+q2.getRadius()+" 的两个圆是分离的圆");
}
if(q1.relation(q2)==4)
{
System.out.println("圆心为"+"("+q1.getCenter().getX()+","+q1.getCenter().getY()+")"+" 半径为"+q1.getRadius()+" 与 圆心为"+"("+q2.getCenter().getX()+","+q2.getCenter().getY()+")"+" 半径为"+q2.getRadius()+" 的两个圆是包含的圆");
}
}
}
实验3 封装性、继承性与包
一、实验要求
●实验目的:
- 掌握包的定义和引用语法;
- 理解封装性的目的:为什么定义private变量、public方法?
- 掌握父子类之间的继承原则:哪些内容可以被继承,哪些内容不可以被继承
- 掌握super关键字的用法:super前缀,super方法
- 掌握方法覆盖的语法、注意事项
- 进一步熟练掌握类的组合关系:Color和ColoredCircle之间也是组合关系
●参考学时:2学时
●基本要求:
- 创建项目“Java实验”,将实验一、实验二分别组织到“实验1”、“实验2”包中,并合理设置实验2中的属性、方法的访问权限修饰符;
- 在“实验3”包中编写颜色类“实验3.Color”,其实现要求包括:
- 包含三个颜色分量red、green和blue(取值范围必须为0-255);
- 构造方法Color()和Color(int r,int g,int b):注意取值范围规定
- 设置颜色值方法
- void setRed(int v):注意取值范围规定
- void setGreen(int v) :注意取值范围规定
- void setBlue(int v) :注意取值范围规定
- 获取颜色值方法
- int getRed();int getGreen();int getBlue()
- 编写“实验2.Circle”类的子类“实验3.ColoredCircle”类,该类的圆心、半径等属性均使用Circle中的定义。其余实现要求包括:
- 表示圆周颜色的Color类对象borderColor和圆心颜色对象centerColor
- 至少定义如下几个ColoredCircle构造方法:
- ColoredCirle():将半径赋为0,将圆心赋为(0,0)点,将两个颜色赋为Color(0,0,0)对象
- ColoredCirle(Point center,int radius):半径为radius,圆心为center,颜色为(0,0,0)
- ColoredCirle(Color centerColor,Color borderColor):半径为0,圆心为(0,0)点,颜色分别为centerColor和borderColor
- ColoredCirle(Point center, int radius, Color centerColor, Color borderColor):圆心为center,半径为radius,圆心颜色为centerColor,边框颜色为borderColor
- 设置颜色方法:
- void setCenterColor(Color c)
- void setBorderColor(Color c)
- 获取颜色方法:
- Color getCenterColor()
- Color getBorderColor()
- 重新定义的relation(Circle c)方法(方法覆盖)
- 在“实验3”包中编写测试ColoredCircle类的测试类Test,并在该类的main方法中调用ColoredCircle类的所有方法(包括从Circle继承来的方法),输出返回值(如有必要),以验证其正确性
●实验提示:
- 创建“Java实验”项目的目的是将之前的实验1、实验2的内容,以及今后的所有实验文件统一组织在一个项目内,不同的实验具有不同的包名,实验与实验之间可跨包复用。完整的项目结构应符合图3.1所示内容:
图3.1实验项目文件夹的浏览效果
- Color类所表示的对象是“红绿蓝颜色模型”的简单抽象,即用三个整数(0-255范围)代表某颜色的三种原色取值。比如(red=0,green=0,blue=0)代表黑色,(red=255,green=0,blue=0)代表红色,(red=255,green=255,blue=255)代表白色。
- ColoredCircle是具有额外属性和方法的Circle子类,但是其本质仍然是“圆”,因为ColoredCircle同样具有圆心、半径属性(隐藏于父类Circle中)和计算面积、计算周长的方法。
- ColoredCircle类中唯一需要重写的父类方法是relation(Circle c)(注意relation(Circle c)方法的形参类型),因为在当前的抽象层次上,带颜色圆和一般圆的唯一不同点是圆和圆的关系不同,面积方法、周长方法、setter、getter方法无需改变。
- relation(Circle c)方法首先应判断c是Circle对象还是ColoredCircle对象。如果c不是ColoredCircle对象,则调用父类的relation方法,并把同一圆关系调整为同心圆(即带颜色的圆和不带颜色的圆不可能是同一圆,最多是同心圆)。如果c是ColoredCircle对象,则可以在同一个圆的基础上进一步判断颜色是否一样(即首先调用父类的relation方法,并根据返回值判断是否为同一圆。如果是,则进一步判断颜色是否相同。只有圆心重合、半径相同,且颜色相同,则两个带颜色圆的关系才是同一圆;颜色不同的圆心重合、半径相同圆归类为同心圆。父类的relation方法返回值为非同一圆,则无需判断颜色)。
- 任何一个类的缺省、无参构造方法将自动调用父类的无参构造方法。另一方面,任何子类都不能继承父类的构造方法。因此,在ColoredCircle类中编写上述若干构造方法,并在这些方法的开始处通过super()或super(…)调用父类的构造方法,以达到初始化父类中定义的成员变量目的。
●评分标准:满分10分
- 按要求正确完成所有任务(ColoredCircle3分,Color和包各2分,Test1分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表3.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 成员的private、public修饰 | 创建实验2.Point()对象p和实验2.Circle()对象c | p.x、p.y、c.center、c.radius提示语法错误 |
2 | Color类构造方法 | 创建Color() | getRed()、getGreen()和getBlue()皆返回0 |
3 | Color类构造方法 | 创建Color(255,255,255) | getRed()、getGreen()和getBlue()皆返回255 |
4 | Color类构造方法 | 创建Color(355,355,355) | getRed()、getGreen()和getBlue()皆返回0 |
5 | Color类getter、setter方法 | 调用setRed(355)、setGreen(355)、setBlue(355) | getRed()、getGreen()和getBlue()皆返回创建颜色对象时的原始red、green、blue值 |
6 | ColoredCircle类构造方法 | 调用ColoredCirle()创建圆; 调用ColoredCirle(null,-1); 调用ColoredCirle(null,null) | getRadius()、area()、perimeter()、getCenter().getX()、getCenter(). getY()、getBorderColor().getRed()、getCenterColor().getRed()等值皆为0 |
7 | ColoredCircle类构造方法 | 调用ColoredCirle(new Point(0,0), 10); | getRadius()为10,area()为314.159,perimeter()为62.831,getCenter().getX()、getCenter(). getY()、getBorderColor().getRed()、getCenterColor().getRed()等值皆为0 |
8 | ColoredCircle类构造方法 | 调用ColoredCirle( new Color(0,0,0), new Color(1,1,1) ) | getRadius()为0;area()为0;perimeter()为0;getCenter(). getX()为0;getCenter().getY()为0;centerColor的所有get方法返回值皆为0;borderColor的所有get方法返回值皆为1 |
9 | ColoredCircle类构造方法 | 调用ColoredCirle(Point center, int radius, Color centerColor, Color borderColor) 创建圆:center为(1,1),radius为10,centerColor为(0,0,0),borderColor为(1,1,1) | getRadius()为10;area()为314.159;perimeter()为62.831;getCenter(). getX()为1;getCenter().getY()为1;centerColor的所有get方法返回值皆为0;borderColor的所有get方法返回值皆为1 |
10 | ColoredCircle类构造方法 | 调用ColoredCirle(Point center, int radius, Color centerColor, Color borderColor) 创建圆:center为null,radius为-10,centerColor为null,borderColor为null | getRadius()为0;area()为0;perimeter()为1;getCenter(). getX()为0;getCenter().getY()为0;centerColor的所有get方法返回值皆为0;borderColor的所有get方法返回值皆为0 |
11 | ColoredCircle类setter方法 | setCenterColor(null) | 创建ColoredCircle时的原始centerColor值不变 |
12 | ColoredCircle类setter方法 | setBorderColor(null) | 创建ColoredCircle时的原始borderColor值不变 |
13 | 子类中编写的方法覆盖 | 调用Circle()创建圆c1,调用ColoredCircle()创建圆c2,并调用c2.relation(c1) | 显示“同心圆” |
14 | 子类中编写的方法覆盖 | 调用ColoredCircle()创建圆c1,调用ColoredCircle()创建圆c2,并调用c2.relation(c1) | 显示“同一圆” |
15 | 子类中编写的方法覆盖 | 调用ColoredCircle(centerColor, borderColor)创建圆c1,其中centerColor为(1,1,1),borderColor为(1,1,1);调用ColoredCircle()创建圆c2,并调用c2.relation(c1) | 显示“同心圆” |
●思考题
- 您还可以设计哪些Circle类的子类?
- Color类所抽象的颜色种类总数是多少?
- 可以在Color类中定义如下代码所示类常量,这么做有什么意义和优点?
pubic static final Color RED=new Color(255,0,0);
pubic static final Color GREEN=new Color(0,255,0);
pubic static final Color BLUE=new Color(0,0,255);
pubic static final Color WHITE=new Color(255,255,255);
pubic static final Color BLACK=new Color(0,0,0);
pubic static final Color GRAY=new Color(128,128,128);
二、代码实现
Color类
package 实验三;
public class Color {
private int red;
private int green;
private int blue;
public int getRed() {
if (red < 0 || red > 255) {
this.red = 0;
}
return red;
}
public void setRed(int red) {
if (red < 0 || red > 255) {
return;
}
this.red = red;
}
public int getGreen() {
if (green < 0 || green > 255) {
this.green = 0;
}
return green;
}
public void setGreen(int green) {
if (green < 0 || green > 255) {
return;
}
this.green = green;
}
public int getBlue() {
if (blue < 0 || blue > 255) {
this.blue = 0;
}
return blue;
}
public void setBlue(int blue) {
if (blue < 0 || blue > 255) {
return;
}
this.blue = blue;
}
public Color() {
this(0, 0, 0);
}
public Color(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
}
ColoredCircle类
package 实验三;
import 实验二.Circle;
import 实验二.Point;
public class ColoredCircle extends Circle {
private Color borderColor;
private Color centerColor;
public Color getBorderColor() {
if (borderColor == null) {
borderColor = new Color(0, 0, 0);
}
return borderColor;
}
public void setBorderColor(Color borderColor) {
if (borderColor == null) {
return;
}
this.borderColor = borderColor;
}
public Color getCenterColor() {
if (centerColor == null) {
centerColor = new Color(0, 0, 0);
}
return centerColor;
}
public void setCenterColor(Color centerColor) {
if (centerColor == null) {
return;
}
this.centerColor = centerColor;
}
public ColoredCircle() {
super();
centerColor = new Color(0, 0, 0);
borderColor = new Color(0, 0, 0);
}
public ColoredCircle(Point center, int radius) {
super(center, radius);
centerColor = new Color(0, 0, 0);
borderColor = new Color(0, 0, 0);
}
public ColoredCircle(Color centerColor, Color borderColor) {
super();
this.centerColor = centerColor;
this.borderColor = borderColor;
}
public ColoredCircle(Point center, int radius, Color centerColor, Color borderColor) {
super(center, radius);
this.centerColor = centerColor;
this.borderColor = borderColor;
}
@Override
public int relation(Circle c) {
boolean v = c instanceof ColoredCircle;
if (!v) {
int m = super.relation(c);
if (m == 0) {
return 1;
}
}
else {
int n = super.relation(c);
if (n == 0) {
if ((getBorderColor().getRed() == ((ColoredCircle) c).getBorderColor().getRed()
&& getBorderColor().getGreen() == ((ColoredCircle) c).getBorderColor().getGreen()
&& getBorderColor().getBlue() == ((ColoredCircle) c).getBorderColor().getBlue())
&& (getCenterColor().getRed() == ((ColoredCircle) c).getCenterColor().getRed()
&& getCenterColor().getGreen() == ((ColoredCircle) c).getCenterColor().getGreen()
&& getCenterColor().getBlue() == ((ColoredCircle) c).getCenterColor().getBlue()))
return 0;
else {
return 1;
}
}
}
return super.relation(c);
}
}
Test类
package 实验三;
import 实验二.Circle;
import 实验二.Point;
import java.text.DecimalFormat;
public class Test {
public static void main(String[] args) {
DecimalFormat dec = new DecimalFormat("0.000");
//测试1
/*
Point p=new Point();
Circle ci=new Circle();
*/
//测试2
Color co1 = new Color();
System.out.println(co1.getRed() + " " + co1.getGreen() + " " + co1.getBlue());
//测试3
Color co2 = new Color(255, 255, 255);
System.out.println(co2.getRed() + " " + co2.getGreen() + " " + co2.getBlue());
//测试4
Color co3 = new Color(355, 355, 355);
System.out.println(co3.getRed() + " " + co3.getGreen() + " " + co3.getBlue());
//测试5
Color co4 = new Color();
co4.setRed(355);
co4.setGreen(355);
co4.setBlue(355);
System.out.println(co4.getRed() + " " + co4.getGreen() + " " + co4.getBlue());
ColoredCircle cc = new ColoredCircle();
//测试6
cc = new ColoredCircle(null, -1);
cc = new ColoredCircle(null, null);
System.out.println(cc.getRadius() + " " + dec.format(cc.computerArea()) + " " + dec.format(cc.computerPerimeter()));
System.out.println(cc.getCenter().getX() + " " + cc.getCenter().getY() + " " + cc.getBorderColor().getRed() + " " + cc.getCenterColor().getRed());
//测试7
cc = new ColoredCircle( new Point(0,0),10);
System.out.println(cc.getRadius() + " " + dec.format(cc.computerArea()) + " " + dec.format(cc.computerPerimeter()));
System.out.println(cc.getCenter().getX() + " " + cc.getCenter().getY() + " " + cc.getBorderColor().getRed() + " " + cc.getCenterColor().getRed());
//测试8
cc = new ColoredCircle(new Color(0, 0, 0), new Color(1, 1, 1));
System.out.println(cc.getCenter().getX() + " " + cc.getCenter().getY() + " " + cc.getRadius() + " " + dec.format(cc.computerArea()) + " " + dec.format(cc.computerPerimeter()));
System.out.println(cc.getCenterColor().getRed() + " " + cc.getCenterColor().getGreen() + " " + cc.getCenterColor().getBlue());
System.out.println(cc.getBorderColor().getRed() + " " + cc.getBorderColor().getGreen() + " " + cc.getBorderColor().getBlue());
//测试9
cc = new ColoredCircle(new Point(1, 1), 10, new Color(0, 0, 0), new Color(1, 1, 1));
System.out.println(cc.getCenter().getX() + " " + cc.getCenter().getY() + " " + cc.getRadius() + " " + dec.format(cc.computerArea()) + " " +dec.format(cc.computerPerimeter()) );
System.out.println(cc.getCenterColor().getRed() + " " + cc.getCenterColor().getGreen() + " " + cc.getCenterColor().getBlue());
System.out.println(cc.getBorderColor().getRed() + " " + cc.getBorderColor().getGreen() + " " + cc.getBorderColor().getBlue());
//测试10
cc = new ColoredCircle(null,-10,null,null);
System.out.println(cc.getCenter().getX() + " " + cc.getCenter().getY() + " " + cc.getRadius() + " " + dec.format(cc.computerArea()) + " " + dec.format(cc.computerPerimeter()));
System.out.println(cc.getCenterColor().getRed() + " " + cc.getCenterColor().getGreen() + " " + cc.getCenterColor().getBlue());
System.out.println(cc.getBorderColor().getRed() + " " + cc.getBorderColor().getGreen() + " " + cc.getBorderColor().getBlue());
//测试11
cc.setCenterColor(null);
System.out.println(cc.getCenterColor().getRed() + " " + cc.getCenterColor().getGreen() + " " + cc.getCenterColor().getBlue());
//测试12
cc.setBorderColor(null);
System.out.println(cc.getBorderColor().getRed() + " " + cc.getBorderColor().getGreen() + " " + cc.getBorderColor().getBlue());
//测试13
Circle a1=new Circle();
ColoredCircle b1=new ColoredCircle();
int n1=b1.relation(a1);
if (n1==1){
System.out.println("这两个圆是同心圆");
}
//测试14
ColoredCircle a2=new ColoredCircle();
ColoredCircle b2=new ColoredCircle();
int n2 = b2.relation(a2);
if (n2==0){
System.out.println("这两个圆是同一圆");
}
else
System.out.println("这两个圆是同心圆");
//测试15
ColoredCircle a3=new ColoredCircle(new Color(1,1,1),new Color(1,1,1));
ColoredCircle a4=new ColoredCircle();
int n3 = a4.relation(a3);
if (n3==0){
System.out.println("这两个圆是同一圆");
}
else
System.out.println("这两个圆是同心圆");
}
}
实验4 Object类
一、实验要求
●实验目的:
- 掌握编写不同包的子类时设置成员访问权限;
- 掌握Object类与其他类的关系以及toString()、equals(Object o)方法的作用;
- 掌握方法覆盖时必须保证方法原型的一致性;
- 掌握类型上转型(上溯)和下转型(下溯)的应用;
●参考学时:2学时
●基本要求:
- 在“实验4”包中编写“实验2.Point”和“实验2.Circle”类的子类“实验4.Point”、“实验4.Circle”,并编写新的构造方法,重写equals()方法和toString()方法;
- 在“实验4”包中编写测试类实验4.Test,并测试上述方法的正确性。
●实验提示:
- equals方法的参数类型必须是Object,访问修饰符必须是public,返回类型必须是boolean,否则该方法不是对从父类继承的public boolean equals(Object obj)方法的覆盖,至多算是该方法的重载。
- Circle类的equals方法不能写成如下代码:
public boolean equals(Object obj) {
if(obj instanceof Circle){
Circle c=(Circle)obj;
return c.getRadius()==getRadius() &&
c.getCenter().equals(getCenter());
}
return false;
}
以上代码试图通过c.getCenter().equals(getCenter())语句调用实验4.Point类的equals方法,从而判断圆心是否为同一点。这种写法的错误之处在于实验4.Circle的center变量是实验2.Point类的对象,而实验2.Point类的equals方法并没有判断XY坐标值是否相同。
●评分标准:满分10分
- 按要求正确完成所有任务(Point和Circle各3分,Test 2分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表4.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 成员的private、public修饰 | 构造方法中直接访问Point类的x、y或Circle类的center、radius | 提示语法错误 |
2 | 子类的构造方法 | 创建实验4.Point()对象p1和实验4.Point(0,10)对象p2 | p1.getX()结果为0,p1.getY()结果为0,p1.distance(p2)结果为10 |
3 | 子类的构造方法 | 创建实验4.Circle()对象c1和实验4.Circle(0,0,10)对象c2 | c1.getCenter().getX()结果为0,c1.getCenter().getY()结果为0,c1.area()为0,c1.perimeter()结果为0,调用c1.relation(c2)结果为“同心圆” |
4 | toString()方法 | 调用p1.toString() | 返回“(0,0)” |
5 | toString()方法 | 调用c1.toString() | 返回“(0,0),0” |
6 | equals()方法 | 创建实验4.Point()对象p1和实验4.Point(0,10)对象p2 | 调用p1.equals(p2)时返回false |
7 | equals()方法 | 创建实验4.Point()对象p1和实验4.Point(0, 0)对象p2 | 调用p1.equals(p2)时返回true |
8 | equals()方法 | 创建实验4.Point()对象p1和实验2.Point(0, 0)对象p2 | 调用p1.equals(p2)时返回true |
9 | equals()方法 | 创建实验4.Point()对象p1和实验2.Point(0, 0)对象p2 | 调用p2.equals(p1)时返回false(因为实验2.Point还没有重写equals方法) |
10 | equals()方法 | 创建实验4.Circle()对象c1和实验4.Circle(0,0,10)对象c2 | 调用c1.equals(c2)时返回false |
11 | equals()方法 | 创建实验4.Circle()对象c1和实验4.Circle(0,0,0)对象c2 | 调用c1.equals(c2)时返回true |
12 | equals()方法 | 创建实验4.Circle()对象c1、实验4.Point(0,0)对象p和实验4.Circle(p,0)对象c2 | 调用c1.equals(c2)时返回true 调用c1.equals(p)和p.equals(c1)皆返回false |
13 | equals()方法 | 创建实验4.Circle()对象c1、实验2.Point(0,0)对象p和实验4.Circle(p,0)对象c2 | 调用c1.equals(c2)时返回true |
14 | equals()方法 | 创建实验4.Circle()对象c1和实验2.Circle(0,0,0)对象c2 | 调用c1.equals(c2)时返回true,调用c2.equals(c1)时返回false |
●思考题
- 为什么必须要重写子类的构造方法?
- 为什么equals()方法和toString()方法的访问类型必须为public?
- 在本实验中何处体现了面向对象技术的“上转型(上溯)”、“下转型(下溯)”概念?
二、代码实现
Point类
package 实验四;
public class Point extends 实验二.Point {
public Point() {
super(0,0);
// this(0, 0);
}
public Point(int x, int y) {
super(x, y);
//super.setX(x);
// super.setY(y);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
else if (obj instanceof 实验二.Point) {
实验二.Point p = (实验二.Point) obj;
return this.getX() == p.getX() && this.getY() == p.getY();
}
else
return false;
}
@Override
public String toString() {
return "(" + this.getX() + "," + this.getY() + ")";
}
}
Circle类
package 实验四;
import 实验二.Point;
public class Circle extends 实验二.Circle {
public Circle() {
super(0,0,0);
//this(0, 0, 0);
}
public Circle(Point center, int radius) {
super(center, radius);
//super.setCenter(center);
// super.setRadius(radius);
}
public Circle(int a, int b, int radius) {
super(a, b, radius);
//super.setCenter(new Point(a, b));
//super.setRadius(radius);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
else if (obj instanceof 实验二.Circle) {
实验二.Circle c = (实验二.Circle) obj;
return this.getRadius() == c.getRadius() && this.getCenter().getX() == c.getCenter().getX() && this.getCenter().getY() == c.getCenter().getY();
}
else {
return false;
}
}
@Override
public String toString() {
return "(" + this.getCenter().getX() + "," + this.getCenter().getY() + ")," + this.getRadius();
}
}
Test类
package 实验四;
public class Test {
public static void main(String[] args) {
//测试1
/*
System.out.println("**测试1的结果:**");
Point p = new Point();
Circle c = new Circle();
System.out.println(p.x + " " + p.y + " (" + c.center.x + "," + c.center.y + ") " + c.radius);
*/
//测试2
System.out.println("**测试2的结果:**");
Point p1 = new Point();
Point p2 = new Point(0, 10);
System.out.println(p1.getX() + " " + p1.getY() + " " + (int) p1.distance(p2));
//测试3
System.out.println("**测试3的结果:**");
Circle c1 = new Circle();
Circle c2 = new Circle(0, 0, 10);
System.out.println(c1.getRadius() + ",(" + c1.getCenter().getX() + "," + c1.getCenter().getY() + ")");
System.out.println((int) c1.computerArea() + " " + (int) c1.computerPerimeter() + " " + c1.relation(c2));
//测试4
System.out.println("**测试4的结果:**");
System.out.println(p1.toString());
//测试5
System.out.println("**测试5的结果:**");
System.out.println(c1.toString());
//测试6
System.out.println("**测试6的结果:**");
Point p3 = new Point();
Point p4 = new Point(0, 10);
System.out.println(p3.equals(p4));
//测试7
System.out.println("**测试7的结果:**");
Point p5 = new Point();
Point p6 = new Point(0, 0);
System.out.println(p5.equals(p6));
//测试8
System.out.println("**测试8的结果:**");
Point p7 = new Point();
实验二.Point p8 = new 实验二.Point(0, 0);
System.out.println(p7.equals(p8));
//测试9
System.out.println("**测试9的结果:**");
Point q1 = new Point();
实验二.Point q2 = new 实验二.Point(0, 0);
System.out.println(q2.equals(q1));
//测试10
System.out.println("**测试10的结果:**");
Circle c3 = new Circle();
Circle c4 = new Circle(0, 0, 10);
System.out.println(c3.equals(c4));
//测试11
System.out.println("**测试11的结果:**");
Circle c5 = new Circle();
Circle c6 = new Circle(0, 0, 0);
System.out.println(c5.equals(c6));
//测试12
System.out.println("**测试12的结果:**");
Circle c7 = new Circle();
Point q3 = new Point(0, 0);
Circle c8 = new Circle(q3, 0);
System.out.println(c7.equals(c8));
System.out.println(c7.equals(q3));
System.out.println(q3.equals(c7));
//测试13
System.out.println("**测试13的结果:**");
Circle q5 = new Circle();
实验二.Point p9 = new 实验二.Point(0, 0);
Circle q6 = new Circle(p9, 0);
System.out.println(q5.equals(q6));
//测试14
System.out.println("**测试14的结果:**");
Circle q7 = new Circle();
实验二.Circle q8 = new 实验二.Circle(0, 0, 0);
System.out.println(q7.equals(q8));
System.out.println(q8.equals(q7));
}
}
实验5 数组与异常处理
一、实验要求
●实验目的:
- 掌握一维数组和二维数组的定义和初始化步骤;
- 进一步掌握toString()方法的应用;
- 掌握异常类的定义以及异常对象的实例化(new)、抛出(throw)、声明抛出(throws)、捕获(try…catch)等内容。
●参考学时:2学时
●基本要求:
- 编写实现一维数组的反转类“实验5.ArrayReverser”。该类包含一个int[]数组data、构造方法ArrayReverser(int[] data)、获取data数组的方法int[] getData()、设置data数组的方法void setData(int[] data)、将data数组内容转换为字符串的toString()方法以及用于反转data数组内容(即第一个元素和最后一个元素互换、第二个元素和倒数第二个元素互换…)的方法void reverse()。
- 编写ArrayReverser类的测试类实验5.ArrayTest。该类首先创建一个一维数组,并将其内容用随机数填充;其后利用上述一维数组创建ArrayReverser对象,并通过toString()方法查看初始数据;最后调用reverse()方法后再次调用toString()查看反转后的数据。
- 编写矩阵类实验5.Matrix。该类包括矩阵数据数组double data[][],构造方法Matrix(int rows,int cols)、Matrix(double data[][]),获取某元素值的方法double getData(int row,int col),设置某元素值的方法void setData(int row,int col,double value),获取矩阵行数方法int getRows(),获取矩阵列数方法int getCols(),计算两个矩阵的乘积的方法Matrix multiply(Matrix m)以及equals()、toString()等内容。
- 编写测试类实验5.MatrixTest,测试Matrix类的正确性。
- 编写如下三个异常类,这些类只需要包含toString方法即可:
-
- 矩阵行数或列数非法异常类IllegalArgumentException
- 矩阵行号或列号非法异常类IllegalIndexException
- 矩阵无法相乘异常类MatrixMultiplicationException
-
- 完善Matrix类的相关方法,使其在不正确的调用情况下抛出适当的异常对象。
- 完善MatrixTest类,以测试异常类的定义和Matrix对异常类的应用是否有效。
●实验提示:
- ArrayReverser(int[] data)构造方法和setData(int[] data)中尽量申请新的this.data数组,并把data形参内容复制到新数组中。
- Matrix类是对现实生活中矩阵的抽象,而不是对二维数组的抽象。通过实例化该类对象,并赋与适当数据值,得到所需矩阵。
- 注意矩阵相乘方法的原型是Matrix multiply(Matrix m),因为两个矩阵相乘的结果仍然是矩阵,而不是二维数组。
- data不为空的前提下,矩阵行数是data.length值,列数是data[0].length值。
- getData(row,col)方法的功能是得到矩阵的row行col列值。setData(row,col,value)方法的功能恰好相反,将矩阵的row行col列设置为value值。如getData(1,2)返回矩阵的1行2列值(一个double值),setData(2,3,1.5)将矩阵的2行3列值赋为1.5。
- 在Matrix类的所有构造方法中,如果行数或列数值小于1,或形参data为空,则抛出IllegalArgumentException异常;
- 在getData(…)和setData(…)方法中,如果行号或列号大于等于矩阵行数或列数,或小于0,则抛出IllegalIndexException异常;
- 在multiply(…)方法中,如果形参为空对象,或两个矩阵的行列数不满足矩阵相乘规则,则抛出MatrixMultiplicationException异常。
- MatrixTest类中,通过预先设定的常数或键盘输入的值确定所要创建的两个矩阵的行列数。实例化矩阵对象之后,可生成一组随机数,或从键盘读入一组数,并循环调用setData(…)方法为矩阵某行某列赋值。建议采用随机数方式,键盘输入方式效率低,不利于程序的调试。
- 初始化矩阵对象之后,计算矩阵的乘积,并把结果通过toString方法的返回值输出到屏幕上,如某一2*3矩阵的输出结果为:
0,2,3
2,1,1
输出两个矩阵的乘积语句应该是:
System.out.println(m.multiply(n));
●评分标准:满分10分
- 按要求正确完成所有任务(ArrayReverser类1分,Matrix类3分,每个异常类各1分,两个Test类 1分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表5.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | ArrayReverser构造方法 | new ArrayReverser(null) | 创建data数组长度为0的对象 |
2 | ArrayReverser构造方法 getData方法 | new ArrayReverser(data) | 利用data数组内容创建ArrayReverser, getData()返回的数组内容应与原始data内容一样 |
3 | ArrayReverser类setData方法 | ar.setData(null) | ar对象data内容不变 |
4 | ArrayReverser类setData方法 | ar.setData(data) | ar对象data内容被形参data内容替换(数组长度也可能会发生变化) |
5 | ArrayReverser类reverse()方法 | ar.reverse() | ar对象data内容被反转 |
6 | ArrayReverser类toString()方法 | ar.toString() | 返回“3 6 1 5 7 34 65”形式字符串 |
7 | Matrix类构造方法 | new Matrix(2,3) | 创建2*3矩阵,每项数据值全为0(数组被new后的默认值) |
8 | Matrix类构造方法 | new Matrix(data) | 创建以data数组为内容的矩阵对象 |
9 | Matrix类构造方法 | new Matrix(0,3) | 抛出IllegalArgumentException异常(行数列数必须大于0) |
10 | Matrix类构造方法 | new Matrix(null) | 抛出IllegalArgumentException异常(参数不能为空) |
11 | 获得列数 | m.getCols() | 返回列数(>0) |
12 | 获得行数 | m.getRows() | 返回行数(>0) |
13 | getter方法 | m.getData(0,0) | 返回0行0列double值 |
14 | setter方法 | m.setData(0,0,10) | 将0行0列值改为10 |
15 | getter方法 | m.getData(-1,0) m.getData(m.getRows(),0) | 抛出IllegalIndexException异常(行号<0,行号>=行数) |
16 | setter方法 | m.setData(0,-1) m.setData(0,m.getCols()) | 抛出IllegalIndexException异常(列号<0,列号>=列数) |
17 | multiply方法 | m1.multiply(m2) | 返回一个新矩阵,值为m1*m2 |
18 | multiply方法 | m1.multiply(null) | 抛出MatrixMultiplicationException异常 |
19 | multiply方法 | m1.multiply(m2),同时m1的列数不等于m2的行数 | 抛出MatrixMultiplicationException异常 |
20 | toString方法 | m1.toString() | 返回“1 2\n3 4\n”形式的字符串 |
●思考题
- 还可以为Matrix类添加哪些方法?
- 为什么在Matrix类的构造方法中不能用随机数直接将二维数组赋值?
二、代码实现
ArrayReverser类
package 实验五;
import java.util.Arrays;
public class ArrayReverser {
private int[] data;
public int[] getData() {
if (data == null) {
this.data = new int[0];
}
return data;
}
public void setData(int[] data) {
if (data == null) {
return;
}
this.data = data;
}
public ArrayReverser() {
this.data = new int[0];
}
public ArrayReverser(int[] data) {
if (data == null) {
this.data = new int[0];
}else
this.data = data;
}
public void reverse() {
for (int i = 0; i < data.length / 2; i++) {
int g = data[i];
data[i] = data[data.length - i - 1];
data[data.length - i - 1] = g;
}
}
@Override
public String toString() {
return Arrays.toString(data);
}
}
ArrayTest 类
package 实验五;
import java.util.Random;
public class ArrayTest {
public static void main(String[] args) {
Random ran = new Random();
int[] a = new int[7];
for (int i = 0; i < a.length; i++) {
a[i] = ran.nextInt(100) + 1;
}
ArrayReverser ar = new ArrayReverser(a);
System.out.println(ar.toString());
ar.reverse();
System.out.println(ar.toString());
}
}
Matrix类
package 实验五;
import java.util.Arrays;
public class Matrix {
private int rows;
private int cols;
private double[][] data;
public int getRows() {
return rows;
}
public int getCols() {
return cols;
}
public double getData(int row, int col) throws IllegalIndexException {
if (row < rows && row >= 0 && col < cols && col >= 0) {
return data[row][col];
}
else {
throw (new IllegalIndexException());
}
}
public void setData(int row, int col, double value) throws IllegalIndexException {
if (row < rows && row >= 0 && col < cols && col >= 0) {
this.data[row][col] = value;
}
else {
throw (new IllegalIndexException());
}
}
public Matrix(){
this.data=new double[0][0];
}
public Matrix(int rows, int cols) throws IllegalArgumentException {
if (rows >= 1 && cols >= 1) {
this.rows = rows;
this.cols = cols;
data=new double[rows][cols];
}
else {
throw (new IllegalArgumentException());
}
}
public Matrix(double[][] data) throws IllegalArgumentException {
if (data == null) {
throw (new IllegalArgumentException());
}
else {
this.data = data;
}
}
public Matrix multiply(Matrix m) throws MatrixMultiplicationException {
if (m == null || data[0].length != m.data.length) {
throw (new MatrixMultiplicationException());
}
int s = this.data.length;
int t = m.data[0].length;
Matrix mat = new Matrix();
mat.data = new double[s][t];
for (int i = 0; i < s; i++) {
for (int j = 0; j < t; j++) {
for (int k = 0; k < m.data.length; k++) {
mat.data[i][j] += this.data[i][k] * m.data[k][j];
}
}
}
return mat;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
else if (o instanceof Matrix) {
Matrix mar = (Matrix) o;
return getRows() == mar.getRows() && getCols() == mar.getCols() && Arrays.equals(data, mar.data);
}
else {
return false;
}
}
@Override
public String toString() {
return Arrays.deepToString(data);
}
}
MatrixTest类
package 实验五;
public class MatrixTest {
public static void main(String[] args) throws MatrixMultiplicationException,IllegalArgumentException {
double [][]a1={{1,2},{2,3}};
Matrix mat1=new Matrix(a1);
double [][]a2={{1,3},{2,3}};
Matrix mat2=new Matrix(a2);
Matrix multiply = mat1.multiply(mat2);
System.out.println(multiply);
}
}
IllegalArgumentException类
package 实验五;
public class IllegalArgumentException extends Throwable {
@Override
public String toString() {
return "IllegalArgumentException";
}
}
IllegalIndexException类
package 实验五;
public class IllegalIndexException extends Throwable {
@Override
public String toString() {
return "IllegalIndexException";
}
}
MatrixMultiplicationException类
package 实验五;
public class MatrixMultiplicationException extends Throwable {
@Override
public String toString(){
return "MatrixMultiplicationException";
}
}
Test类
package 实验五;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws IllegalIndexException, MatrixMultiplicationException, java.lang.IllegalArgumentException, IllegalArgumentException {
Scanner sc=new Scanner(System.in);
Random r=new Random();
System.out.println("第一个测试");
ArrayReverser arr = new ArrayReverser(null);
System.out.println(arr.getData().length);
System.out.println("第二个测试");
int[] data = {25, 36, 87};
ArrayReverser arr1 = new ArrayReverser(data);
int[] data1 = arr1.getData();
System.out.println("ArrayReverser的数组内容是:" + arr1);
System.out.println("getData()的数组内容是:" + Arrays.toString(data1));
System.out.println("第三个测试");
arr1.setData(null);
System.out.println(arr1 + "内容不变");
System.out.println("第四个测试");
int[] data2 = {68, 45, 79, 64};
arr1.setData(data2);
System.out.println(arr1 + "已被替换" + " 数组长度为" + arr1.getData().length);
System.out.println("第五个测试");
arr1.reverse();
System.out.println(arr1 + "已被反转");
System.out.println("第六个测试");
System.out.println(arr1.toString());
System.out.println("第七个测试");
System.out.println("矩阵为");
Matrix matrix = new Matrix(2, 3);
System.out.println(matrix);
System.out.println("第八个测试***********");
System.out.println("矩阵为:");
double[][] data3 = {{4, 8, 9, 7}, {6, 3, 2, 1}, {7, 9, 6, 4}, {3, 7, 9, 2}};
matrix = new Matrix(data3);
System.out.println(matrix);
/*System.out.println("第九个测试");
matrix=new Matrix(0,3);
System.out.println("第十个测试");
Matrix matrix1=new Matrix(null);*/
System.out.println("第十一,十二个测试");
Matrix matrix2=new Matrix(2,1);
System.out.println("行数为"+matrix2.getRows()+" 列数为"+matrix2.getCols());
System.out.println("第十三个测试");
System.out.println(matrix.getData(0,0));
System.out.println("第十四个测试");
matrix.setData(0,0,10);
System.out.println(matrix.getData(0,0));
/*System.out.println("第十五个测试");
Matrix matrix3=new Matrix(0,0);
System.out.println(matrix3.getData(matrix3.getRows(),0));
System.out.println(matrix3.getData(-1,0));*/
/*System.out.println("第十六个测试");
Matrix matrix3=new Matrix(3,3);
int i = r.nextInt(10) + 1;
matrix3.setData(0,-1,i);*/
System.out.println("第十七个测试");
double [][]arr2={{1,2},{2,3}};
Matrix m1=new Matrix(arr2);
double [][]arr3={{1,3},{2,3}};
Matrix m2=new Matrix(arr3);
Matrix multiply = m1.multiply(m2);
System.out.println("m1*m2=");
System.out.println(multiply);
/*System.out.println("第十九个测试");
double[][]arr4={{1,3},{2,5},{7,8}};
m2=new Matrix(arr4);
m1.multiply(m2);*/
/* System.out.println("第十八个测试");
m1.multiply(null);*/
System.out.println("第二十个测试");
System.out.println(m1);
}
}
实验6-1 链表类的实现
一、实验要求
● 实验目的:
- 进一步掌握Object类的主要特点;
- 进一步掌握上转型、下转型的含义;
- 掌握equals方法的具体应用;
- 掌握开发一个链表类的关键步骤。
● 参考学时:2学时
● 基本要求:
- 定义实验61.Node类和实验61.LinkedList类,并通过测试类测试上述类的正确性。LinkedList类组合了Node类,Node类与LinkList类之间的关系如图6.1所示。图中空心菱形的含义是LinkedList类聚合(Aggregation)了Node类对象,聚合是组合关系的一种。
- 注意,Java语言已经提供了一个java.util.LinkedList类,请不要在本实验中导入并使用该类,而是重新定义“实验61.LinkedList”。
图6-1 Node类和LinkedList类之间的关系
- Node类:
- 要求Node类拥有如下2个私有属性:
- data:用于存放节点的数据。为了能够实现常见数据类型的链表,data类型定义为Object,即可以在链表中保存任何类型的对象。如果要保存int、double等基本数据类型,需要用到Java的自动“装箱”和“拆箱”机制。
- next:用于保存本节点的下一个节点的引用。
- 要求Node类里有一个构造方法Node(Object data),此外还拥有设置属性和获取属性的getter、setter方法以及简单的toString()方法(该方法将data转换为String并返回该值)。
- LinkedList类
- 要求LinkedList类拥有1个private属性head,用于存储链表第一个节点的引用。
- 要求为LinkedList实现7个public方法,关于这7个方法的要求如表6.1所示。
- 实现search()方法和remove()方法时需要用到链表中节点的数据所属类提供的equals()方法,因为这两个方法都涉及到比较链表中某一Node的getData().equals(所要查找的data),而不是getData()= = data。
表6.1 LinkList类的方法
编号 | 方法名称 | 描述 |
1 | void add(Object data) | 将值为data的节点插入到链表尾。 |
2 | boolean remove(Object data) | 在链表中删除值为data的节点。若删除成功,返回true;否则,返回false。 |
3 | Node search(Object data) | 在链表中查找值为data的节点。若找到,返回该节点的引用,若没找到,返回null。 |
4 | boolean insertAfter(Node previous, Object data) | 在引用previous指向的节点后插入一个值为data的节点。若插入成功,返回true;否则,返回false。 |
5 | Node get(int index) | 在链表中找第index个节点。若找到,返回第index节点的引用,若找不到,返回null。 |
6 | boolean set(Node node, Object data) | 将node引用节点的内容改为data。若修改成功,返回true;否则,返回false。 |
7 | int size() | 返回链表长度。 |
一个初步的具有add方法的LinkedList代码如下:
public class LinkedList {
private Node head;
public void add(Object data) {
Node node=new Node(data); //创建包含data值的Node节点
if(head==null) //链表为空,则当前节点就是第一个节点
head=node;
else { //链表不为空,需要找到最后一个节点
Node tmp=head; //利用tmp节点遍历
while(tmp.getNext()!=null) //tmp节点不是最后一个节点
tmp=tmp.getNext(); //tmp指向下一个节点
//循环结束时tmp为最后一个节点
tmp.setNext(node); //把当前节点链接在最后一个节点之后
}
}
}
- 实验61.Test类(测试类,其中含主方法)
- 要求在测试类的主方法中,测试LinkedList类的方法,可按如下步骤执行:
① 调用无参构造方法LinkedList(),创建一个空链表。
② 连续三次调用add()方法,在链表中插入三个Integer对象,如add(1); add(2); add(3)(此处Java自动将1、2、3等int值装箱为Integer对象)。
③ 调用search()方法,查找值为2的节点,将该方法返回的节点的引用赋给Node型变量p;
④ 调用set()方法,将p指向的节点的内容修改为22。
⑤ 调用insertAfter()方法,在p指向的节点后面插入一个值为23的节点。
⑥ 调用remove()方法,删除值为22的节点。
⑦ 调用size()方法,获得链表长度赋给整型变量n。
⑧ 循环n次,每一轮通过调用get(i)方法得到第i个节点的引用,并将其内容输出。
● 评分标准:满分14分
- 按要求正确完成所有任务(LinkedList类8分,Node类2分,Test类 2分,合计12分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表6.2 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 构造方法 | list=new LinkedList() | list.size()返回0; list.get(0)返回null; list.search(1)返回null; list.remove(1)返回false |
2 | add方法 | list.add(1); list.add(2); list.add(3); | list.size()返回3 |
3 | search方法 | Node p= list.search(22); | System.out.println(p)显示null |
4 | search方法 | Node p= list.search(2); | System.out.println(p)显示2 |
5 | set方法 | list.set(null,"22"); | set方法返回false |
6 | set方法 | list.set(p,"22"); | System.out.println(p)显示22 |
7 | insertAfter方法 | list.insertAfter(p,23); | list.size()返回4 |
8 | insertAfter方法 | list.insertAfter(null,23); | insertAfter方法返回null |
9 | remove方法 | list.remove(12) | 返回false |
10 | remove方法 | list.remove(22) | 返回true |
11 | get方法 | Node p=list.get(0); | System.out.println(p)显示1 |
12 | get方法 | Node p= list.get(-1); | System.out.println(p)显示null |
13 | get方法 | Node p= list.get(100); | System.out.println(p)显示null |
● 思考题
- Node类中重写toString()有什么特殊意义?
- 在本实验中Node中的data类型不是Object而是Integer可不可行?为什么?
- 假如链表中保存任意一个自定义类型的对象,上述Node和LinkedList的功能是否依然正确?如果不正确,需要进行哪些补充?
二、代码实现
Node类
package 实验六一;
public class Node {
Object date;//用于存放节点数据
Node next;//用于保存本节点的下一节点的引用
public Node(Object date) {
this.date = date;
}
public Object getDate() {
return date;
}
public void setDate(Object date) {
this.date = date;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return date + "";
}
}
LinkedList类
package 实验六一;
public class LinkedList {
private Node head;
//将值为data的节点插入到链表尾
public void add(Object date) {
Node node = new Node(date);//创建包含data值的Node节点
if (head == null) {//链表为空,则当前节点就是第一个节点
head = node;
} else { //链表不为空,需要找到最后一个节点
Node temp = head; //利用tmp节点遍历
while (temp.getNext() != null) {//tmp节点不是最后一个节点
temp = temp.getNext(); //tmp指向下一个节点
//循环结束时tmp为最后一个节点
temp.setNext(node);//把当前节点链接在最后一个节点之后
}
}
}
//在链表中删除值为data的节点。若删除成功,返回true;否则,返回false
public boolean remove(Object date) {
if (date != null) {
Node temp = head;
Node node = head;
if (temp == null) {
return false;
}
while (temp != null) {
if (temp.getDate().equals(date)) {
if (temp == head) {
head = head.getNext();
return true;
}
node.setNext(temp.getNext());
return true;
}
node = temp;
temp = temp.getNext();
}
return false;
}
return false;
}
//在链表中查找值为data的节点。若找到,返回该节点的引用,若没找到,返回null
public Node search(Object date) {
Node temp = head;
int i = 0;
if (head == null) {
return null;
} else {
while (temp != null) {
if (temp.getDate().equals(date)) {
i++;
break;
}
temp = temp.getNext();
}
if (i == 0) {
return null;
} else {
return temp;
}
}
}
//在引用previous指向的节点后插入一个值为data的节点。若插入成功,返回true;否则,返回false
public boolean insertAfter(Node previous, Object date) {
Node temp = head;
Node temp2;
Node temp3;
int i = 0;
if (head == null) {
return false;
} else {
while (temp.getNext() != null) {
if (temp.equals(previous)) {
temp2 = temp.getNext();
temp3 = new Node(date);
temp3.setNext(temp2);
temp.setNext(temp3);
i++;
break;
}
temp = temp.getNext();
}
}
if (i == 0) {
return false;
} else {
return true;
}
}
//在链表中找第index个节点。若找到,返回第index节点的引用,若找不到,返回null
public Node get(int index) {
if (head == null) {
return null;
} else {
int i = 0;
Node temp = head;
while (temp.getNext() != null) {
if (i == index) {
return temp;
}
i++;
temp = temp.getNext();
}
if (i == index) {
return temp;
}
return null;
}
}
//将node引用节点的内容改为data。若修改成功,返回true;否则,返回false
public boolean set(Node node, Object date) {
Node temp = head;
if (head == null) {
return false;
} else {
int f = 0;
while (temp != null) {
if (temp.equals(node)) {
temp.setDate(date);
f++;
break;
}
temp = temp.getNext();
}
if (f == 0) return false;
else return true;
}
}
//返回链表长度
public int size() {
Node node = head;
int i = 0;
while (node != null) {
i++;
node = node.getNext();
}
return i;
}
}
Test类
package 实验六一;
public class Test {
public static void main(String[] args) {
boolean boo;
//1
LinkedList list = new LinkedList();
System.out.println(list.size() + " " + list.get(0) + " " + list.search(1) + " " + list.remove(1));
//2
list.add(1);
list.add(2);
list.add(3);
System.out.println(list.size());
//3
Node p = list.search(22);
System.out.println(p);
//4
p = list.search(2);
System.out.println(p);
//5
boo = list.set(null,22);
System.out.println(boo);
//6
list.set(p,22);
System.out.println(p);
//7
boo = list.insertAfter(p,23);
System.out.println(boo);
System.out.println(list.size());
//8
boo = list.insertAfter(null,23);
System.out.println(boo);
//9
boo = list.remove(12);
System.out.println(boo);
//10
System.out.println(list.remove(22));
//11
p = list.get(0);
System.out.println(p);
//12
p = list.get(-1);
System.out.println(p);
//13
p = list.get(100);
System.out.println(p);
}
}
实验6-2 宠物商店
一、实验要求
● 实验目的:
- 掌握接口的概念和语法,并能够应用于实际。
- 利用实验61.LinkedList实现复杂应用。
● 参考学时:2学时
● 基本要求:
本题要求实现宠物商店的宠物上架、下架、查询等操作。要求用链表存储宠物商店中的宠物。因此,本选题需要在实验6-1中实现的链表类“实验61.LinkedList”和“实验61.Node”。在本程序中,最重要的是定义宠物标准(定义一个宠物接口Pet),进入宠物商店销售的所有宠物(如Cat类、Dog类、Fish类)都需要实现这个标准(即实现接口Pet)。根据以上原则,给出了图6.2所示的类图(图中的虚线三角形箭头表示实现接口,实线普通箭头表示关联(Association)关系)。
图6.2 实验相关的类、接口及其关系
- 定义宠物接口实验62.Pet
这个接口是宠物商店可处理的宠物标准。宠物商店并不关心具体的宠物是什么,只关心一点:只要是实现了标准接口的宠物,就可以进入宠物商店,并能够通过宠物商店进行各种操作。宠物接口Pet的定义如下:
package 实验62;
interface Pet {
public String getName();
public int getAge();
}
- 定义LinkedList类
具体要求见实验6-1。
- 定义宠物商店类实验62.PetShop
宠物商店所售卖的宠物数量不定,因此要用链表(LinkedList)对象保存多个宠物。同时宠物商店能够售卖的宠物必须是具有宠物特征(比如具有名字和年龄)和行为(比如告知名字或年龄)的一切动物,因此在上架(添加)、卖出(删除)宠物时,接收的参数都应是Pet接口类型。
表6.3 宠物商店类的方法
编号 | 方法名称 | 描述 |
1 | void add(Pet pet) | 将宠物pet上架,即添加到链表中。 |
2 | boolean delete(Pet pet) | 从宠物商店下架宠物pet,即从链表中查找并删除。若删除成功,返回true;否则,返回false。 |
3 | Pet get(int index) | 查找第index个宠物。若找到,返回该宠物的引用,若找不到,返回null。 |
4 | int size() | 返回宠物总数。 |
提示:在宠物商店类中,先定义一个LinkedList类变量pets,并实例化。
package 实验62;
import 实验61.*;
public class PetShop { // 一个宠物商店要保存多个宠物信息
private LinkedList pets = new LinkedList(); //用链表保存宠物信息
public void add(Pet pet) { //新宠物上架操作,以接口对象为参数!
pets.add(pet);
}
public boolean delete(Pet pet) { //宠物下架操作,以接口对象为参数!
…
}
public Pet get(int index){
//pets.get()返回Node节点,Node节点的getData()返回Object,该Object就是Pet
return (Pet)( pets.get(index).getData() );
}
…
}
- 定义宠物猫子类实验62.Cat
要求Cat类实现接口Pet。要求Cat类有两个私有属性name和age。Cat类中除了getName()和get getAge( ) 方法外,还要求为其实现3个public方法,关于这3个方法的要求如表6.4所示。Cat类的类图如图6.3所示。为了简洁起见,在图6.3所示的Cat类图中省略了getName()方法和getAge( ) 方法。
表6.4 Cat类的方法表
编号 | 方法名称 | 描述 |
1 | Cat(String name, int age) | 创建Cat类的实例,其名字为name,年龄为age。 |
2 | equals(Object obj) | 覆写Object中的equals()方法。判断猫类的两个对象值是否相等。 |
3 | toString() | 覆写Object中的方法。返回猫的完整信息。 |
- 定义宠物狗子类实验62.Dog
要求Dog类实现接口Pet。要求Dog类有两个私有属性name和age。Dog类中除了getName()和get getAge( ) 方法外,还要求为其要求实现3个public方法,对这3个方法的要求,与表6.4中对Cat类中方法的要求类似,因此不再赘述。Dog类的类图如图6.4所示。
图6.3 Cat类图 图6.4 Dog类图
- 编写测试类实验62.Test。要求在测试类的主方法中,完成如下动作:
① 定义一个宠物商店类变量shop,并实例化;
② 在宠物商店shop中添加1岁的“波斯猫”;
③ 在宠物商店shop中添加2岁的“橘猫”;
④ 在宠物商店shop中添加1岁的“折耳猫”;
⑤ 在宠物商店shop中添加1岁的“柯基犬”;
⑥ 在宠物商店shop中添加2岁的“波尔多”(注:波尔多是一种狗名字);
⑦ 显示宠物商店shop中所有宠物;
⑧ 删除宠物商店shop中2岁的“橘猫”;
⑨ 显示宠物商店shop中所有宠物;
通过运行结果可以发现,由于本程序是面向接口的编程,所以返回的结果中即包含了Cat类对象,也包含了Dog类对象。
● 评分标准:满分6分
- 按要求正确完成所有任务(PetShop2分,Dog和Cat类1分,Pet接口和Test类 1分,合计4分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表6.5 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 构造方法 size方法 get方法 delete方法 | shop=new PetShop() | shop.size()返回0; shop.get(0)返回null; shop.delete(null)返回false |
2 | add方法 get方法 toString方法 | shop.add(new Cat("波斯猫",1)); shop.add(new Cat("橘猫",2)); shop.add(new Cat("折耳猫",1)); shop.add(new Dog("柯基犬",1)); shop.add(new Dog("波尔多",2)); | shop.size()返回5 shop.get(0).toString()返回“波斯猫,1岁” |
3 | delete方法 | shop.delete(new Cat("橘猫",1)); | 返回false,因为商店中只有2岁橘猫,没有1岁橘猫 shop.size()返回5 |
4 | delete方法 | shop.delete(new Cat("波斯猫",1)); | 返回true shop.size()返回4,因为已经删除了一个波斯猫 |
● 思考题
- Cat、Dog类中重写equals()有什么特殊意义?不重写会有什么后果?
- 还可以为Pet接口或PetShop类添加哪些方法?
二、代码实现
宠物接口Pet
package 实验六二;
interface Pet {
public String getName();
public int getAge();
}
宠物商店类PetShop
package 实验六二;
import 实验六一.LinkedList;
public class PetShop {
private LinkedList pets = new LinkedList(); //用链表保存宠物信息
//将宠物pet上架,即添加到链表中
public void add(Pet pet) { //新宠物上架操作,以接口对象为参数
pets.add(pet);
}
//从宠物商店下架宠物pet,即从链表中查找并删除。若删除成功,返回true;否则,返回false
public boolean delete(Pet pet) { //宠物下架操作,以接口对象为参数
return pets.remove(pet);
}
//查找第index个宠物。若找到,返回该宠物的引用,若找不到,返回null
public Pet get(int index) {///pets.get()返回Node节点,Node节点的getData()返回Object,该Object就是Pet
if (pets.get(index) == null) {
return null;
} else {
return (Pet) (pets.get(index).getDate());
}
}
//返回宠物总数
public int size() {
return pets.size();
}
}
Cat类
package 实验六二;
public class Cat implements Pet {
private String name;
private int age;
//创建Cat类的实例,其名字为name,年龄为age
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
//覆写Object中的equals()方法。判断猫类的两个对象值是否相等
@Override
public boolean equals(Object obj) {
if (obj instanceof Cat) {
Cat cat = (Cat) obj;
return name.equals(cat.getName()) && age == cat.getAge();
}
return false;
}
//覆写Object中的方法。返回猫的完整信息
@Override
public String toString() {
return getName() + " " + getAge();
}
@Override
public String getName() {
return name;
}
@Override
public int getAge() {
return age;
}
}
Dog类
package 实验六二;
public class Dog implements Pet {
private String name;
private int age;
//创建Dog类的实例,其名字为name,年龄为age
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
//覆写Object中的equals()方法。判断猫类的两个对象值是否相等
@Override
public boolean equals(Object obj) {
if (obj instanceof Dog) {
Dog dog = (Dog) obj;
return name.equals(dog.getName()) && age == dog.getAge();
}
return false;
}
//覆写Object中的方法。返回狗的完整信息
@Override
public String toString() {
return getName() + " " + getAge();
}
@Override
public String getName() {
return name;
}
@Override
public int getAge() {
return age;
}
}
Test类
package 实验六二;
public class Test {
public static void main(String[] args) {
//1
// 定义一个宠物商店类变量shop,并实例化
PetShop shop = new PetShop();
System.out.println(shop.size());
System.out.println(shop.get(0));
System.out.println(shop.delete(null));
//2
//在宠物商店shop中添加1岁的“波斯猫”
//在宠物商店shop中添加2岁的“橘猫”
//在宠物商店shop中添加1岁的“折耳猫”
//在宠物商店shop中添加1岁的“柯基犬”
//在宠物商店shop中添加2岁的“波尔多”
shop.add(new Cat("波斯猫", 1));
shop.add(new Cat("橘猫", 2));
shop.add(new Cat("折耳猫", 1));
shop.add(new Dog("柯基犬", 1));
shop.add(new Dog("波尔多", 2));
System.out.println(shop.size());
System.out.println(shop.get(0).toString());
System.out.println(shop.get(1).toString());
System.out.println(shop.get(2).toString());
System.out.println(shop.get(3).toString());
System.out.println(shop.get(4).toString());
//3
//删除宠物商店shop中2岁的“橘猫”
System.out.println(shop.delete(new Cat("橘猫", 2)));
System.out.println(shop.size());
System.out.println(shop.get(1).toString());
//4
//删除宠物商店shop中1岁的“波斯猫”
System.out.println(shop.delete(new Cat("波斯猫", 1)));
System.out.println(shop.size());
System.out.println(shop.get(2).toString());
}
}
实验7 简易计算器
一、实验要求
●实验目的:
- 掌握图形界面程序的构造过程以及布局管理器的应用;
- 掌握常用组件对象的使用;
- 掌握Action事件和Window事件的处理过程。
●参考学时:2学时
●基本要求:
- 编写具有图形界面的简易计算器类实验7.SimpleCaculator,该类包含AWT界面构造代码。界面如图7.1所示:
图7.1简易计算器界面示意图
- 编写四个按钮的单击事件以及窗口关闭事件的处理类实验7.MyListener:
- 当按下“+”按钮时,两个数值文本框之间应显示“+”号,同时相加结果显示在第三个文本框内(如图7.2所示)。类似处理“-”、“*”和“/”按钮。
图7.2单击Plus按钮后的运行结果示意图
- 应处理除0异常和数值格式非法异常,如:
图7.3异常处理示意图
- 点击关闭按钮时,结束程序的运行。
- 编写计算器类的测试类实验7.Test。
●实验提示:
- 文本框(TextField类)的宽度可以用构造函数设置,如:
TextField tf1=new TextField(10);
- 出错提示窗口可以利用javax.swing.JOptionPane类的相应方法,如:
javax.swing.JOptionPane.showMessageDialog(null, "请输入两个整数!");
- 由于SimpleCaculator类和MyListener类分别是两个类,在SimpleCaculator类中应该把事件处理过程中需要访问的三个TextField对象以及第1个Label对象定义为成员变量(四个按钮也可以定义为成员变量),并实例化事件处理对象时将SimpleCaculator对象传给MyListener。此后MyListener就可以直接访问需要的对象(除非这几个成员变量为private)。这两个类和Test类的关系如以下代码:
public class Test{
public static void main(){
SimpleCaculator calc= new SimpleCaculator();
calc.go();
}
}
public class SimpleCaculator{
TextField tf1,tf2,tf3;
Label l1;
Button[4] button;
public void go(){
...
MyListener ml=new MyListener(this);//this即Test中的calc对象
for(inti=0;i<4;i++)
button[i].addActionListener(ml);
...
}
}
public class MyListener...{
SimpleCaculatorcalc;
public MyListener(SimpleCaculator o){
calc=o;
}
public void actionPerformed(ActionEvent e){
if(e.getSource()==calc.button[0]){//Plus按钮
Stringv1=calc.tf1.getText();
...
}
}
- 将界面类和事件处理类分开写的优点是程序结构清晰,易于维护,便于多人合作开发复杂软件。
●评分标准:满分10分
- 按要求正确完成所有任务(SimpleCaculator和MyListener类各4分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表7.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 布局管理器 | 改变窗口大小 | 各组件的相对位置基本保持不变 |
2 | Window事件 | 点击关闭按钮 | 窗口能够正确关闭 |
3 | 四则运算 | 输入10和0,点击加按钮 | 显示加号和10 |
4 | 四则运算 | 输入10和0,点击减按钮 | 显示减号和10 |
5 | 四则运算 | 输入10和0,点击乘按钮 | 显示乘号和0 |
6 | 四则运算 | 输入10和0,点击除按钮 | 显示“除数不能为零!” |
7 | 异常处理 | 什么也不输入,依次点击加减乘除按钮 | 提示“请输入两个整数!” |
8 | 异常处理 | 输入a和b,依次点击加减乘除按钮 | 提示“请输入两个整数!” |
●思考题
- 将事件处理代码直接写在SimpleCaculator有什么便利之处,有什么缺点?
- 在示例代码中,如果将MyListener ml=new MyListener(this)改为MyListener ml=new MyListener(new SimpleCaculator())会带来什么后果?
- 四个按钮的点击事件处理代码会有很多重复之处,如何减少这种冗余代码?
二、代码实现
SimpleCalculator类
package 实验七;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class SimpleCalculator extends JFrame {
JTextField t1 = new JTextField(12);
JTextField t2 = new JTextField(12);
JTextField t3 = new JTextField(12);
JLabel jLabel1 = new JLabel(" ");
JLabel jLabel2 = new JLabel("=");
JButton btn1 = new JButton("Plus");
JButton btn2 = new JButton("Minus");
JButton btn3 = new JButton("Times");
JButton btn4 = new JButton("Divide");
MyListener ml = new MyListener(this);
public SimpleCalculator() { //构造界面
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
setLayout(new GridLayout(2, 1));
add(panel1);
add(panel2);
panel1.add(t1);
panel1.add(jLabel1);
panel1.add(t2);
panel1.add(jLabel2);
panel1.add(t3);
panel2.add(btn1);
panel2.add(btn2);
panel2.add(btn3);
panel2.add(btn4);
btn1.addActionListener(ml);
btn2.addActionListener(ml);
btn3.addActionListener(ml);
btn4.addActionListener(ml);
setSize(480, 100);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
MyListener类
package 实验七;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
public class MyListener implements ActionListener{
SimpleCalculator s;
public MyListener(SimpleCalculator s) {
this.s = s;
}
public void actionPerformed(ActionEvent e) {
String s1 = s.t1.getText();
String s2 = s.t2.getText();
try {
int x = Integer.parseInt(s1);
int y = Integer.parseInt(s2);
if (e.getSource() == s.btn1) {
s.t3.setText(Integer.toString(x + y));
s.jLabel1.setText("+");
}
else if (e.getSource() == s.btn2) {
s.t3.setText(Integer.toString(x - y));
s.jLabel1.setText("-");
}
else if (e.getSource() == s.btn3) {
s.t3.setText(Integer.toString(x * y));
s.jLabel1.setText("*");
}
else {
s.t3.setText(Integer.toString(x / y));
s.jLabel1.setText("/");
}
} catch (NumberFormatException e1) {
JOptionPane.showMessageDialog(null,"请输入两个整数!");
} catch (ArithmeticException e2) {
JOptionPane.showMessageDialog(null, "除数不能为零!");
}
}
}
Test类
package 实验七;
public class Test {
public static void main(String[] args) {
new SimpleCalculator();
}
}
实验8 文本编辑器
一、实验要求
●实验目的:
- 掌握图形界面环境下的JTextArea组件和菜单组件的应用;
- 掌握JFileChooser类的使用方法;
- 掌握字符流文件的读写。
●参考学时:4学时
●基本要求:
- 编写简易编辑器图形界面类实验8.NotepadFrame,界面效果如图8.1所示:
图8.1 文本编辑器界面示意图
- 编写菜单事件响应类实验8.MyListener,并完成文本文件的编辑、保存功能。
- 要求完成从磁盘读入文本文件,或从新开始编辑全新内容,并将内容显示到JTextArea中。所编辑的内容可以保存到新文件、原文件或另存到其它文件中。
- 必须使用JFileChooser类来完成文件名称的输入或选取。
●实验提示:
(1)在JFrame中添加菜单可以用如下代码:
JMenuBar jMenuBar1 = new JMenuBar();
JMenu jMenu1 = new JMenu("文件");
JMenuItem jMenuItem1 = new JMenuItem("打开");
JMenuItem jMenuItem2 = new JMenuItem("保存");
JMenuItem jMenuItem3 = new JMenuItem("另存为");
JMenuItem jMenuItem4 = new JMenuItem("退出");
JSeparator jSeparator1 = new JSeparator();//分割线
jMenu1.add(jMenuItem1);
jMenu1.add(jMenuItem2);
jMenu1.add(jMenuItem3);
jMenu1.add(jSeparator1);
jMenu1.add(jMenuItem4);
jMenuBar1.add(jMenu1);
frame.setJMenuBar(jMenuBar1);
(2)默认情况下JtextArea没有滚动条,若要添加滚动条,需要用以下代码:
jScrollPane1 = new JScrollPane();
jTextArea1 = new JTextArea();
jScrollPane1.setViewportView(jTextArea1);
frame.add(jScrollPane1);
(3)应在程序中定义文件路径和文件名称变量,以便当打开某一文件,或将全新编辑的内容存储为某一文件时,保存文件路径和文件名称。
(4)当前内容已被编辑时,退出程序或打开新文件都应提示是否需要保存,并根据操作者的选择决定下一步处理。可通过JTextArea的键盘事件(KeyEvent)监听确定内容是否被修改,如:
public void keyPressed(KeyEvent e){
//edited为当前内容是否已编辑(不代表内容肯定被修改)标志
edited=true;
}
●评分标准:满分20分
- 按要求正确完成所有任务(NotepadFrame 2分,关闭按钮和退出菜单3分,打开菜单3分,保存菜单3分,另存菜单3分,文件读写4分,合计18分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)
●测试案例:
表8.1 测试案例(仅用于编码测试)
编号 | 测试目的 | 输入或测试数据 | 期望结果 |
1 | 布局管理器 | 改变窗口大小 | 各组件的相对位置基本保持不变 |
2 | Window事件 退出菜单 | 没有修改过JTextArea内容(内容空白,同时没有键入任何内容) | 关闭窗口时直接退出 |
3 | Window事件 退出菜单 | 键入某些内容(可将键入的内容全部删除) | 关闭窗口时弹出对话框提示“是否保存当前内容?”和“是”、“否”、“取消”三个按钮。选择“是”则弹出JFileChooser对话框,获取文件路径后保存内容并退出;选择“否”则直接退出;选择“取消”则不退出,返回编辑状态 |
4 | Window事件 退出菜单 | 打开一个文件后没做任何修改 | 关闭窗口时直接退出 |
5 | Window事件 退出菜单 | 打开一个文件后进行了修改 | 关闭窗口时弹出对话框提示“是否保存当前内容?”和“是”、“否”、“取消”三个按钮。选择“是”则覆盖原有文件并退出;选择“否”则直接退出;选择“取消”则不退出,返回编辑状态 |
6 | 打开菜单 | 没有修改过JTextArea内容 | 弹出JFileChooser对话框,获取文件路径后读入内容并显示在JTextArea中 |
7 | 打开菜单 | 修改过JTextArea内容,同时该内容不属于任何文件 | 弹出对话框提示“是否保存当前内容?”和“是”、“否”、“取消”三个按钮。选择“是”则弹出JFileChooser对话框,获取文件路径后保存内容,再次显示JFileChooser对话框,获取新文件路径后读入内容并显示在JTextArea中;选择“否”则直接显示JFileChooser对话框,获取文件路径后读入内容并覆盖JTextArea原内容;选择“取消”则返回编辑状态 |
8 | 打开菜单 | 修改过JTextArea内容,同时该内容属于某一文件 | 弹出对话框提示“是否保存当前内容?”和“是”、“否”、“取消”三个按钮。选择“是”则保存原有文件内容,再次显示JFileChooser对话框,获取新文件路径后读入内容并覆盖JTextArea内容;选择“否”则直接显示JFileChooser对话框,获取文件路径后读入内容并覆盖JTextArea原内容;选择“取消”则返回编辑状态 |
9 | 保存菜单 | JTextArea内容为全新内容 | 弹出JFileChooser对话框,获取文件路径后保存内容,最后返回编辑状态 |
10 | 保存菜单 | JTextArea内容为某一文件内容 | 直接覆盖源文件内容,最后返回编辑状态 |
11 | 另存为菜单 | JTextArea内容为全新内容 | 弹出JFileChooser对话框,获取文件路径后保存内容,最后返回编辑状态 |
12 | 另存为菜单 | JTextArea内容为某一文件内容 | 和测试案例11相同 |
●思考题
- 为了方便起见,通常将如前所述的文件路径、文件名称以及edited变量定义为事件监听类MyListener的成员变量。如果把上述变量定义为NotepadFrame的成员变量,有何特殊意义?
- 还可以添加哪些功能菜单?
二、代码实现
NotepadFrame类
package 实验八;
import javax.swing.*;
import java.io.*;
public class NotepadFrame extends JFrame {
Boolean haveChanged = false;
Boolean haveFile = false;
JMenuBar jMenuBar1 = new JMenuBar();
JMenu jMenu1 = new JMenu("文件");
JMenuItem jMenuItem1 = new JMenuItem("打开");
JMenuItem jMenuItem2 = new JMenuItem("保存");
JMenuItem jMenuItem3 = new JMenuItem("另存为");
JMenuItem jMenuItem4 = new JMenuItem("退出");
JScrollPane jScrollPane1;
JTextArea jTextArea1;
JFileChooser chooser;
File file;
MyListener l = new MyListener(this);
public NotepadFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jMenu1.add(jMenuItem1);
jMenu1.addSeparator();
jMenu1.add(jMenuItem2);
jMenu1.addSeparator();
jMenu1.add(jMenuItem3);
jMenu1.addSeparator();
jMenu1.add(jMenuItem4);
jMenuBar1.add(jMenu1);
jScrollPane1 = new JScrollPane();
jTextArea1 = new JTextArea();
jScrollPane1.setViewportView(jTextArea1);
setJMenuBar(jMenuBar1);
setSize(800, 600);
setLocationRelativeTo(null);//将窗口置于屏幕中央
setVisible(true);
add(jScrollPane1);
this.addWindowListener(l);
jMenuItem1.addActionListener(l);
jMenuItem2.addActionListener(l);
jMenuItem3.addActionListener(l);
jMenuItem4.addActionListener(l);
jTextArea1.getDocument().addDocumentListener(l);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
}
MyListener类
package 实验八;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.event.*;
import java.io.*;
public class MyListener extends WindowAdapter implements ActionListener, DocumentListener {
NotepadFrame notepadFrame;
MyListener(NotepadFrame notepadFrame) {
this.notepadFrame = notepadFrame;
}
@Override
public void windowClosing(WindowEvent e) {
Exit();
}
@Override
public void insertUpdate(DocumentEvent e) {
notepadFrame.haveChanged = true;
}
@Override
public void removeUpdate(DocumentEvent e) {
notepadFrame.haveChanged = true;
}
@Override
public void changedUpdate(DocumentEvent e) {
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == notepadFrame.jMenuItem1) {
//打开文件
if (Save()) {
Read();
notepadFrame.haveChanged = false;
}
}
else if (e.getSource() == notepadFrame.jMenuItem2) {
//保存文件
if (notepadFrame.haveFile && notepadFrame.haveChanged) {
SaveOpen();
notepadFrame.haveChanged = false;
}
else if (notepadFrame.haveChanged) {
SaveNew();
notepadFrame.haveChanged = false;
}
}
else if (e.getSource() == notepadFrame.jMenuItem3) {
//另存为
SaveNew();
notepadFrame.haveChanged = false;
}
else if (e.getSource() == notepadFrame.jMenuItem4) {
//退出
Exit();
}
}
public void Read() {
notepadFrame.chooser = new JFileChooser();
notepadFrame.chooser.setFileFilter(new FileNameExtensionFilter("txt文件", "txt"));
notepadFrame.chooser.showOpenDialog(null);
notepadFrame.file = notepadFrame.chooser.getSelectedFile();
if (notepadFrame.file == null)
return;
BufferedReader reader;
try {
reader = new BufferedReader(new FileReader(notepadFrame.file));
String line = "";
String ans = "";
while ((line = reader.readLine()) != null) {
ans = ans + line+" \n";
}
notepadFrame.jTextArea1.setText(ans);
reader.close();
notepadFrame.haveFile = true;
} catch (IOException e1) {
System.out.println("打开失败!");
e1.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因
}
notepadFrame.haveChanged = true;
}
public void SaveOpen() {
notepadFrame.file = notepadFrame.chooser.getSelectedFile();
if (notepadFrame.file == null)
return;
FileWriter writer;
try {
writer = new FileWriter(notepadFrame.file);
writer.write(notepadFrame.jTextArea1.getText());
writer.flush();
writer.close();
} catch (IOException e1) {
System.out.println("打开失败!");
}
}
public void SaveNew() {
notepadFrame.chooser = new JFileChooser();
notepadFrame.chooser.showOpenDialog(null);
notepadFrame.file = notepadFrame.chooser.getSelectedFile();
if (notepadFrame.file == null)
return;
FileWriter writer;
try {
writer = new FileWriter(notepadFrame.file);
writer.write(notepadFrame.jTextArea1.getText());
writer.flush();
writer.close();
} catch (IOException e1) {
System.out.println("打开失败");
}
}
public boolean Save() {
if (notepadFrame.haveFile && notepadFrame.haveChanged) {
int i = JOptionPane.showConfirmDialog(notepadFrame, "是否要保存已经修改的文本?", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
if (i == JOptionPane.YES_OPTION) {
SaveOpen();
return true;
}
else
return i != JOptionPane.CANCEL_OPTION;
}
else if (notepadFrame.haveChanged) {
int i = JOptionPane.showConfirmDialog(notepadFrame, "是否要保存已经修改的文本?", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
if (i == JOptionPane.YES_OPTION) {
SaveNew();
return true;
}
else
return i != JOptionPane.CANCEL_OPTION;
}
return true;
}
public void Exit() {
if (notepadFrame.haveFile && notepadFrame.haveChanged) {
int i = JOptionPane.showConfirmDialog(notepadFrame, "文本已修改,是否要保存?", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
if (i == JOptionPane.YES_OPTION) {
SaveOpen();
}
else if (i == JOptionPane.CANCEL_OPTION) {
return;
}
}
else if (notepadFrame.haveChanged) {
int i = JOptionPane.showConfirmDialog(notepadFrame, "文本已修改,是否要保存?", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
if (i == JOptionPane.YES_OPTION) {
SaveNew();
}
else if (i == JOptionPane.CANCEL_OPTION) {
return;
}
}
System.exit(0);
}
}
Test类
package 实验八;
public class Test {
public static void main(String[] args) {
new NotepadFrame();
}
}
实验9 世界时钟(选做)
一、实验要求
●实验目的:
- 掌握创建多线程应用;
- 进一步熟练掌握图形界面和事件处理。
●参考学时:2学时
●基本要求:
1.编写一个世界时钟程序界面类实验9.WorldClock,界面如图9.1所示:
图9.1 文世界时钟界面示意图
2.编写时钟线程类实验9.ClockThread,完成每隔1秒更新显示功能。
3.编写三个按钮的事件处理类实验9.MyListener,实现单击按钮时暂停或继续时钟显示。点击某一“暂停”按钮后,该按钮文字变成“继续”,再次点击该按钮(“继续”按钮)则又变成“暂停”按钮。
●实验提示:
1.设置Label字体的代码如下:
Font font=new Font("宋体",Font.BOLD,30);
labelBeiJing=new Label("北京时间");
labelBeiJing.setFont(font);
2.获取时间、换算时区以及转化为字符串的代码如下:
Calendar now = Calendar.getInstance();
//timeInterval为与北京时间相差的时数,如纽约为-12,伦敦为-7,东京为+2
now.add(Calendar.HOUR, timeInterval);
String sdf=new SimpleDateFormat("HH:mm:ss").format(now.getTime());
3.让某一线程t暂停执行的语句是:
t.suspend();//挂起,即暂停
4.让某一线程t继续执行的语句是:
t.resume();//恢复,即继续执行
●评分标准:满分10分
- 按要求正确完成所有任务(WorldClock 和MyListener 各3分,ClockThread2分,合计8分)
- 编码、调试操作熟练;输出正确,界面符合要求(1分)
- 实验报告撰写规范,内容完整;(1分)