*本文是博主对Java各种实验的再整理与详解,除了代码部分和解析部分,一些题目还增加了拓展部分(⭐)。拓展部分不是实验报告中原有的内容,而是博主本人自己的补充,以方便大家额外学习、参考。
目录
一、实验目的
二、实验内容
1、编一个学生类(Student)
2、编写一个简单计算器类
3、设计一个日期类
4、设计一个分数类
⭐拓展:分数约分
迭代版
递归版
运用到该题
5、设计一个雇员类
6、设计一个电视机类
7、仿照超市购物的例子编写一个学生借书的程序。
三、实验总结
1、理解面向对象的相关基本概念,掌握类的声明、类的实例化和调用。
2、掌握方法及构造方法的定义;
3、掌握方法重载的用法,理解值传递和地址传递两种参数传递方式的区别;
4、掌握this关键字和static关键字的用法;
5、掌握访问控制符的使用,以实现类的封装和信息隐蔽;
6、能够初步进行类的设计,编写基本的面向对象的程序;
7、了解对象内存分配和垃圾回收机制的原理。
其中包含以下内容:
属性:学号studentNo,姓名studentName,性别studentGender,年龄studentAge。
方法:构造方法,显示学号方法showNo(),显示姓名方法showName(),显示性别方法showSex(),显示年龄方法showAge(),修改年龄方法modifyAge()。
主类(S3_1)包含:主方法main(),在其中创建两个学生对象s1和s2并初始化,两个对象的属性自行确定,然后分别显示这两个学生的学号、姓名、性别、年龄,然后修改s1的年龄并显示修改后的结果。
本题比较简单,按要求编程即可,注意构造方法的语法书写以及Student类的属性封装。
源代码:
import java.util.Scanner;
public class S3_1 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
Student s1 = new Student(1001,"小明","男",18);
Student s2 = new Student(2002,"小红","女",20);
System.out.println("学生1:");
s1.showNo();
s1.showName();
s1.showGender();
s1.showAge();
System.out.println("学生2:");
s2.showNo();
s2.showName();
s2.showGender();
s2.showAge();
System.out.println("修改学生2的年龄为:");
int age = reader.nextInt();
s2.modifyAge(age);
s2.showAge();
}
}
class Student {
//属性封装
private int studentNo;
private String studentName;
private String studentGender;
private int studentAge;
public Student(int studentNo, String studentName, String studentGender, int studentAge) {
this.studentNo = studentNo;
this.studentName = studentName;
this.studentGender = studentGender;
this.studentAge = studentAge;
}
public void showNo() {
System.out.println("学号是:" + this.studentNo);
}
public void showName() {
System.out.println("姓名是:" + this.studentName);
}
public void showGender() {
System.out.println("性别是:" + this.studentGender);
}
public void showAge() {
System.out.println("年龄是:" + this.studentAge);
}
public void modifyAge(int age) {
this.studentAge = age;
}
}
列出测试数据和实验结果截图:
拥有加减乘除等方法,每个方法都有2个操作数,同为double类型或同为整型,方法设计为重载,在测试类里通过键盘输入操作数,显示计算结果.
本题的重点是方法的重载。
方法的重载是指在一个类中可以有多个名字相同的方法,但这些方法的参数列表各不相同。参数列表指的是参数的个数、类型、顺序。如果两个方法的方法名与参数列表都完全相同,那么就不是重载,而是重复,程序会报错。重载与方法的返回值无关。
若要完善本题代码,有一些小细节需要注意,如double类型的运算结果需要保留几位小数,是否需要处理被0除的情况等。不过事实上,Java虚拟机会处理被0除的情况,那就是会抛出ArithmeticException异常。这个异常不需要我们手动定义,jvm也会处理。
源代码:
import java.util.Scanner;
public class S3_2 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
Calculator calculator = new Calculator();
System.out.println("请输入要运算的数据类型(0为int,1为double):");
int judge = reader.nextInt();
if(judge == 0){
System.out.println("请输入两个整数:");
int x = reader.nextInt();
int y = reader.nextInt();
System.out.println("请输入运算符(+ - * /)");
String operation = reader.next();
int ret = 0;
switch(operation){
case "+":
ret = calculator.add(x,y);
break;
case "-":
ret = calculator.sub(x,y);
break;
case "*":
ret = calculator.mul(x,y);
break;
case "/":
ret = calculator.div(x,y);
break;
}
System.out.println("计算结果是" + ret);
}else if(judge == 1){
System.out.println("请输入两个实数:");
double x = reader.nextDouble();
double y = reader.nextDouble();
System.out.println("请输入运算符(+ - * /)");
String operation = reader.next();
double ret = 0;
switch(operation){
case "+":
ret = calculator.add(x,y);
break;
case "-":
ret = calculator.sub(x,y);
break;
case "*":
ret = calculator.mul(x,y);
break;
case "/":
ret = calculator.div(x,y);
break;
}
System.out.printf("计算结果是%.2f",ret); //保留2位小数
}
}
}
class Calculator {
public int add(int x,int y) {
return x+y;
}
public int sub(int x,int y){
return x-y;
}
public int mul(int x,int y) {
return x*y;
}
public int div(int x,int y){
return x/y;
}
public double add(double x,double y) {
return x+y;
}
public double sub(double x,double y){
return x-y;
}
public double mul(double x,double y) {
return x*y;
}
public double div(double x,double y){
return x/y;
}
}
列出测试数据和实验结果截图:
若被0除,则抛出异常:
定义类的构造方法对日期进行初始化,在toString()中将其输出格式定为“月/日/年”。最后,编写一个测试程序来测试所定义的日期类能否实现预定的功能。
源代码:
public class Test2 {
public static void main(String[] args) {
MyDate date = new MyDate(2023,1,18);
System.out.println(date);
}
}
class MyDate{
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
//重写toString()方法
@Override
public String toString() {
return month+"/"+day+"/"+year;
}
}
列出测试数据和实验结果截图:
分数的分子和分母用两个整型数表示,类所拥有的方法包括对分数进行加、减、乘、除等运算,以及输出分数的方法,输出分数的格式应该是:分子/分母。
在测试类中定义分数类对象,运算并输出运算结果。
本题若不考虑分数的约分,难度会小一些。在该题的拓展部分介绍了约分的方式。
整体思路如下面源代码所示:
public Fraction add(Fraction x){
return new Fraction(this.mol*x.den + x.mol*this.den, this.den*x.den);
}
两个分数运算后得到的结果仍然是分数。因此,该成员方法的返回值为Fraction,传入的运算参数类型也为Fraction。确定了方法的返回值与参数后,那么问题就来了:如何让当前分数对象与传入的分数对象x做运算呢?
有些同学可能会想到一个思路,就是求出当前对象的分数值后再直接与x相加。即:
this.mol/this.den + x; //Error
但这种做法是完全行不通的。首先这一定会遇到类型转换的问题:this.mol/this.den是int类型,而x是Fraction类型,二者之间不能进行运算。这时又有人想到,可以通过构造方法把 this.mol/this.den 以似乎是Fraction类型的形式表现出来,即:
new Fraction(this.mol,this.den) + x; //Error
但事实上,这也是完全错误的。Fraction是引用类型,两个引用类型之间怎么能用+号进行运算呢?
出现这两种错误思路的根本原因在于:没有意识到到底是谁在进行运算。两个引用类型之间能直接进行四则运算吗?当然是不能的。而是引用类型的成员之间进行四则运算。就和两个引用类型进行比较一样,它们不能用大于号小于号等于号直接比较,而要用equals或Comparable接口实现compareTo()接口后,按照某种指定的规则进行比较。
同样地,当我们说两个分数进行相加,并不是“分数”这个概念进行的运算,而是分子和分母之间进行的运算。正确的做法是:通过构造方法将分子相加的结果、分母相加的结果得到一个新的分数,这个分数才是最终的结果。即:
new Fraction(this.mol*x.den + x.mol*this.den, this.den*x.den);
//括号中的内容即通分相加的过程
其余的三则运算同理。一个引用类型与它的成员是密不可分的。当要对某一个引用类型进行操作时,要顺带考虑一下是否关系到它的成员。
源代码:
class Fraction {
private int mol; //分子
private int den; //分母
public Fraction(int x, int y) {
if (y == 0){
throw new ArithmeticException("分母为0!");
}
this.mol = x;
this.den = y;
}
public Fraction add(Fraction x){
return new Fraction(this.mol * x.den + x.mol * this.den,this.den * x.den);
}
public Fraction sub(Fraction x){
return new Fraction(this.mol * x.den - x.mol * this.den,this.den * x.den);
}
public Fraction mul(Fraction x){
return new Fraction(this.mol * x.mol,this.den * x.den);
}
public Fraction div(Fraction x){
if(x.mol == 0) {
throw new ArithmeticException("除数为0!");
}
return new Fraction(this.mol * x.den,this.den * x.mol);
}
@Override
public String toString() {
return mol+"/"+den;
}
}
public class S3_4 {
public static void main(String[] args) {
Fraction fraction1 = new Fraction(4,5);
Fraction fraction2 = new Fraction(3,4);
System.out.println("分数1:" + fraction1 + " 分数2:" + fraction2);
System.out.println("add:" + fraction1.add(fraction2));
System.out.println("sub:" + fraction1.sub(fraction2));
System.out.println("mul:" + fraction1.mul(fraction2));
System.out.println("div:" + fraction1.div(fraction2));
}
}
列出测试数据和实验结果截图:
令fraction1的分母为0,则:
令fraction2的分子为0(即令fraction2为0),则除数为零,则:
分数约分的本质是找到分子mol与分母den的最大公约数。分子与分母同时除以该最大公约数的过程就是约分。本文在此拓展求最大公约数的方法:辗转相除法。
内容扩展:
迭乘法、辗转相除法、试除法:最大公约数与最小公倍数问题
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
//辗转相除法求最大公约数、最小公倍数
Scanner reader = new Scanner(System.in);
//输入两个数
int n = reader.nextInt();
int m = reader.nextInt();
/*因为求最大公约数和最小公倍数都需要用到m、n,且辗转相除的过程会改变n、m的值,
故再创建两个变量n2、m2,把m和n的值拷贝一份再做运算*/
int m2 = m;
int n2 = n;
int r = n2 % m2;
//最大公约数
while (r != 0) {
n2 = m2;
m2 = r; //注意:m2才是所求的最大公约数的结果,而不是r
r = n2 % m2;
}
System.out.println(m2);
}
}
public static int getComDivisor(int a , int b){
if(a % b == 0) {
return b;
}
return getComDivisor(b,a % b);
}
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
//输入两个数
int n = reader.nextInt();
int m = reader.nextInt();
System.out.println(getComDivisor(n,m));
}
源代码:
public class S3_4 {
public static void main(String[] args) {
Fraction fraction1 = new Fraction(4,5);
Fraction fraction2 = new Fraction(3,4);
System.out.println("分数1:" + fraction1 + " 分数2:" + fraction2);
System.out.println("add:" + fraction1.add(fraction2));
System.out.println("sub:" + fraction1.sub(fraction2));
System.out.println("mul:" + fraction1.mul(fraction2));
System.out.println("div:" + fraction1.div(fraction2));
}
}
class Fraction {
private int mol; //分子
private int den; //分母
//新增
private int getComDivisor(int a , int b){
if(a % b == 0) {
return b;
}
return getComDivisor(b,a % b);
}
public Fraction(int x, int y) {
if (y == 0){
throw new ArithmeticException("分母为0!");
}
this.mol = x;
this.den = y;
int tmp = getComDivisor(x,y);
this.mol = x/tmp;
this.den = y/tmp;
}
//
public Fraction add(Fraction x){
return new Fraction(this.mol * x.den + x.mol * this.den,this.den * x.den);
}
public Fraction sub(Fraction x){
return new Fraction(this.mol * x.den - x.mol * this.den,this.den * x.den);
}
public Fraction mul(Fraction x){
return new Fraction(this.mol * x.mol,this.den * x.den);
}
public Fraction div(Fraction x){
if(x.mol == 0) {
throw new ArithmeticException("除数为0!");
}
return new Fraction(this.mol * x.den,this.den * x.mol);
}
@Override
public String toString() {
return mol+"/"+den;
}
}
列出测试数据和实验结果截图:
(1)属性包括:编号、姓名、年龄、职务、部门,要求合理选定属性类型;该雇员类还拥有统计出勤人数的功能,可以考虑为雇员类设计一个静态属性;方法包括:构造方法、输出信息的方法、签到方法;
(2)创建雇员类对象,统计雇员的出勤人数。注意考虑属性和方法的访问权限,方法的功能,及main方法中如何实现要求统计的信息。
本题的思路如下:
1、成员属性编号、姓名、年龄、职务、部门封装,用private修饰,设计setter与getter方法。
2、静态属性attendance用于统计雇员出勤人数,用static修饰。默认初始化为0.
3、输出信息的方法通过重写toString()方法完成。
4、签到方法的功能是提示签到成功,并令attendance++,以达到统计出勤人数的目标。
源代码:
public class S3_5 {
public static void main(String[] args) {
//实例化雇员对象
Employee employee1 = new Employee(1001,"虹猫",20,"产品经理","开发部");
Employee employee2 = new Employee(1002,"蓝兔",19,"食堂队长","后勤部");
Employee employee3 = new Employee(1003,"大奔",21,"技术总监","技术部");
Employee employee4 = new Employee(1004,"莎莉",18,"销售总监","销售部");
//签到
Employee[] employees = {employee1,employee2,employee3,employee4};
for (Employee x : employees) {
System.out.println(x.toString());
Employee.signIn(x);
}
//公布出勤人数
System.out.println("共" + Employee.attendance + "人出勤!");
}
}
class Employee{
//出勤人数,默认初始化为0
public static int attendance;
//成员属性
private int ID;
private String name;
private int age;
private String post;
private String department;
//getter与setter
public int getID() {
return ID;
}
public void setID(int ID) {
this.ID = ID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post = post;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
//构造方法
public Employee(int ID, String name, int age, String post, String department) {
this.ID = ID;
this.name = name;
this.age = age;
this.post = post;
this.department = department;
}
//输出信息的方法:重写toString()
@Override
public String toString() {
return "ID=" + ID +
", name='" + name + '\'' +
", age=" + age +
", post='" + post + '\'' +
", department='" + department + '\'' +
'}';
}
//签到方法
public static void signIn(Employee x) {
System.out.println(x.name + "签到成功!");
Employee.attendance++; //用于统计
}
}
列出测试数据和实验结果截图:
属性包括商品编号、开关状态、音量、频道等,同时设计一些方法对电视机的状态进行控制。例如,方法应包括开/关电视机、更换频道、提高/减小音量等。要求商品编号自动生成(可以考虑为电视机类设置一个管理商品编号的静态成员变量,或者专门设置一个编号管理类)。
注意:有些成员变量需要定义为静态的(static),控制和操纵静态成员变量的方法应是静态的(static)。
本题思路如下:
1、设计静态变量num用于自动生成产品编号。每次构造一个电视机实例,num值加一。将num值赋给电视机编号itemNO即可实现自动生成。
2、开关电视机的成员方法tvSwitch()。用boolean变量status控制电视机的开关。该方法用于改变status的值,若为true,则改为false;若为false则改为true,以此实现电视机的开关。
3、调节音量的实现方式是令音量值向上增1或向下减1.要向上调节音量还是向下调节取决于用户传入的adjust变量值为true还是false。true则向上调节,false则向下调节。音量的调节范围是0~100.对临界情况的调节要予以考虑。
4、调节频道有两个重载的方法。可以通过和音量一样的调节方式调节,也可以由用户指定频道,直接跳转到目标频道。频道的调节范围是0~100.对临界情况的调节要予以考虑。
5、注意:在对电视机进行操作之前,必须确保电视机是开机状态,即status为true。否则不能进行相关操作。
6、main方法中设计了4个不同状态的电视机对象,对Television类进行测试。最终输出四个对象的相关信息,通过重写toString方法实现。
源代码:
public class S3_6 {
public static void main(String[] args) {
Television tv1 = new Television();
Television tv2 = new Television();
Television tv3 = new Television();
Television tv4 = new Television();
//打开tv1、tv2、tv3
tv1.tvSwitch();
tv2.tvSwitch();
tv3.tvSwitch();
System.out.println("电视机tv1、tv2、tv3已打开!");
//尝试将tv1的音量上调(true)、tv2的音量下调(false)
System.out.println("==========尝试将tv1的音量上调(true)、tv2的音量下调(false)==========");
tv1.adjustVolume(true);
tv2.adjustVolume(false);
//尝试将tv3的频道换成50、将tv1的频道换成100
System.out.println("==========尝试将tv3的频道换成50、将tv1的频道换成100==========");
tv3.changeChannel(50);
tv1.changeChannel(100);
//尝试将tv3向下调台、将tv1向上调台
System.out.println("==========尝试将tv3向下调台、将tv1向上调台==========");
tv3.changeChannel(false);
tv1.changeChannel(true);
//获取tv2当前的频道和音量
System.out.println("==========获取tv2当前的频道和音量==========");
System.out.println(tv2.getVolume());
System.out.println(tv2.getChannel());
//对未开机的tv4进行操作时:
System.out.println("==========对未开机的tv4进行操作==========");
tv4.changeChannel(50);
tv4.adjustVolume(true);
//输出各个电视机的信息
System.out.println("==========输出各个电视机的信息==========");
System.out.println(tv1.toString());
System.out.println(tv2.toString());
System.out.println(tv3.toString());
System.out.println(tv4.toString());
}
}
//电视机类
class Television{
private static int num; //管理商品编号的静态成员变量
private String itemNO; //商品编号
private boolean status; //开关状态
private int volume; //音量
private int channel; //频道
public int getVolume() {
if (this.status) {
return volume;
}else {
return -1;
}
}
public boolean isStatus() {
return status;
}
public int getChannel() {
if (this.status) {
return this.channel;
}else {
return -1;
}
}
//构造方法,自动生成产品编号
public Television() {
Television.num++;
this.itemNO = "tv000" + Television.num;
}
//开关电视机
public boolean tvSwitch() {
if(!this.status) {
this.status = true;
} else {
this.status = false;
}
return this.status;
}
//上调或下调频道
public void changeChannel(boolean change){
if(change) {
this.channel++;
if(channel > 100) {
System.out.println("已达最大频道,无法增加!");
this.channel--;
}
}else{
this.channel--;
if(channel < 0) {
System.out.println("已达最小频道,无法下降!");
this.channel++;
}
}
System.out.println(this.itemNO + "当前频道为:TV" + this.channel);
}
//重载:更换为指定频道
public void changeChannel(int newChannel) {
//判断电视机是否开启,若未开启,则直接返回
if(!this.status) {
System.out.println("电视机未开机!无法操作!");
return;
}
if(newChannel < 0 || newChannel > 100) {
System.out.println("该频道不存在!");
} else {
this.channel = newChannel;
}
System.out.println(this.itemNO + "当前频道为:TV" + this.channel);
}
//增减音量
public void adjustVolume(boolean adjust) {
//判断电视机是否开启,若未开启,则直接返回
if(!this.status) {
System.out.println("电视机未开机!无法操作!");
return;
}
if(adjust){
this.volume++;
if(this.volume > 100) {
System.out.println("已达音量最大值!无法提高音量!");
this.volume--;
}
} else {
this.volume--;
if(this.volume < 0) {
System.out.println("已达音量最小值!无法降低音量!");
this.volume++;
}
}
System.out.println(this.itemNO + "当前音量为:" + this.volume);
}
@Override
public String toString() {
return "Television{" +
"itemNO='" + itemNO + '\'' +
", status=" + status +
", volume=" + volume +
", channel=" + channel +
'}';
}
}
列出测试数据和实验结果截图:
提示:思考需要定义的类,例如:本程序需要用到学生、借书卡、书等对象,最后实现借书的过程,如果有指定的书,则输出“***借到了***书”,否则输出“****没有借到****书”。
还需要认真思考每个类中有哪些属性和方法,能够更好的完成这个程序。
本题相较于上面几题,稍微有些复杂。下面是思路:
1、学生类。学生类中需要有学生的基本信息,即学号、姓名。然后根据需求进行封装,提供相应的getter和setter接口;提供相应的构造方法;重写toString方法便于信息输出。
class Student{
private int stuID;
private String name;
public String getName() {
return name;
}
public Student(int stuID, String name) {
this.stuID = stuID;
this.name = name;
}
@Override
public String toString() {
return "stuID=" + stuID + ", name='" + name;
}
}
2、图书类。图书类中也要提供图书的相关信息,包括图书号、图书名两个成员属性,相应的构造方法和toString方法。注意,不同的是,这里必须重写equals方法。这在判断学生要借的图书是否存在时,有重大用处。
class Book{
private int bookNo;
private String name;
public Book(int bookNo, String name) {
this.bookNo = bookNo;
this.name = name;
}
@Override
public String toString() {
return "[" + bookNo +
", " + name +
']';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return bookNo == book.bookNo && name.equals(book.name);
}
@Override
public int hashCode() {
return Objects.hash(bookNo, name);
}
}
3、图书馆类。图书馆类相当于是一个书架。用ArrayList集合进行图书的预存放。直接调用集合中的各种方法对图书进行操作,非常方便。当然,也可以自定义一维数组完成相同的功能。展示书架上的图书这一方法中用到了迭代器,因为这样可以自定义打印出来的格式。如果不使用迭代器,也可以直接System.out.print(libBook);因为ArrayList中重写过toString方法。
class Library{
//书架
List libBook;
public Library() {
this.libBook = new ArrayList<>();
}
//设置书架上的图书
public void setBooks(Book book){
libBook.add(book);
}
//展示书架上的图书
public void showBooks() {
Iterator it = libBook.listIterator();
while(it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
}
}
4、借记卡类。借记卡的基本信息是卡号、学生、已借阅的图书与已借阅的图书数量这四个成员属性。其中,已借阅的图书也用ArrayList集合来存放。在构造方法中,只有学生的信息是需要外界传入的。卡号采取自动随机生成的方式赋值,采用了Random类来生成随机数。
在借书方法中,需要判断要借的书在书架上是否存在。采用的是ArrayList中的cntains方法进行判断。然而,在用contains将传入图书与已有图书比较的过程中,拿什么进行比较就很重要。java中ArrayList的contains方法,作用是用来判断元素是否在集合中,本质上使用的是比较对象的equals的方法来去比较。注意:若此时比较对象的equlas方法没有重写,则会进入Object的equlas方法比较地址,libBook中的任一元素与我们传入的book的地址肯定是不同的,因此一定会返回false。
而重写了equals方法,则能避免这些问题。若判断该书不存在,则抛出异常。
class DebitCard{
private int cardID;
private Student stu;
private List books;
private int bookNum;
//借记卡构造方法
public DebitCard(Student stu) {
Random random = new Random();
this.cardID = random.nextInt(900)+100;
this.stu = stu;
this.books = new ArrayList<>();
}
//借书 若书在图书馆中存在,则借记成功,若不存在,则抛出异常
public void borrowBooks(Book book,Library library) throws BookNotExistException{
if(library.libBook.contains(book)){
books.add(book);
bookNum = books.size();
}else{
throw new BookNotExistException("该书不存在!无法借记!");
}
}
@Override
public String toString() {
return "DebitCard{" +
"借记卡号:" + cardID +
", 已借阅图书:" + books +
", 已借阅图书数量:" + bookNum +
'}';
}
}
自定义的异常类:
public class BookNotExistException extends RuntimeException{
public BookNotExistException() {
}
public BookNotExistException(String message) {
super(message);
}
}
5、设计好各个类之后,在main方法中进行测试即可。分为如下几步:预存放书架上的书、创建学生对象、注册借记卡、借书。
源代码:
import java.util.*;
public class S3_7 {
public static void main(String[] args) {
//给图书馆书架上设置图书
Library library = new Library();
Book book1 = new Book(1111,"《Java程序设计》");
Book book2 = new Book(2222,"《C++程序设计》");
Book book3 = new Book(3333,"《Python程序设计》");
Book book4 = new Book(4444,"《Web程序设计》");
library.libBook.add(book1);
library.libBook.add(book2);
library.libBook.add(book3);
library.libBook.add(book4);
//展示书架上的图书
System.out.println("书架上现有图书:");
library.showBooks();
//创建学生对象
Scanner reader = new Scanner(System.in);
System.out.println("请输入学生学号:");
int stuID = reader.nextInt();
System.out.println("请输入学生姓名:");
String stuName = reader.next();
Student stu = new Student(stuID,stuName);
//申请借记卡
DebitCard debitCard = new DebitCard(stu);
//展示借记卡信息
System.out.println(debitCard);
//进行借书
System.out.println("请输入要借的图书编号:");
int bookNO = reader.nextInt();
System.out.println("请输入要借的图书名:");
String boName = reader.next();
try {
debitCard.borrowBooks(new Book(bookNO,"《" + boName +"》"),library);
}catch(BookNotExistException e){
System.out.println(stu.getName() + "没有借到" + boName);
e.printStackTrace();
System.out.println(debitCard);
System.exit(-1);
}
System.out.println(stu.getName() + "借到了" + boName);
System.out.println(debitCard);
}
}
class Student{
private int stuID;
private String name;
public String getName() {
return name;
}
public Student(int stuID, String name) {
this.stuID = stuID;
this.name = name;
}
@Override
public String toString() {
return "stuID=" + stuID + ", name='" + name;
}
}
class DebitCard{
private int cardID;
private Student stu;
private List books;
private int bookNum;
//借记卡构造方法
public DebitCard(Student stu) {
Random random = new Random();
this.cardID = random.nextInt(900)+100;
this.stu = stu;
this.books = new ArrayList<>();
}
//借书 若书在图书馆中存在,则借记成功,若不存在,则抛出异常
public void borrowBooks(Book book,Library library) throws BookNotExistException{
if(library.libBook.contains(book)){
books.add(book);
bookNum = books.size();
}else{
throw new BookNotExistException("该书不存在!无法借记!");
}
}
@Override
public String toString() {
return "DebitCard{" +
"借记卡号:" + cardID +
", 已借阅图书:" + books +
", 已借阅图书数量:" + bookNum +
'}';
}
}
class Book{
private int bookNo;
private String name;
public Book(int bookNo, String name) {
this.bookNo = bookNo;
this.name = name;
}
@Override
public String toString() {
return "[" + bookNo +
", " + name +
']';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return bookNo == book.bookNo && name.equals(book.name);
}
/* @Override
public int hashCode() {
return Objects.hash(bookNo, name);
}*/
}
class Library{
//书架
List libBook;
public Library() {
this.libBook = new ArrayList<>();
}
//设置书架上的图书
public void setBooks(Book book){
libBook.add(book);
}
//展示书架上的图书
public void showBooks() {
Iterator it = libBook.listIterator();
while(it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
}
}
public class BookNotExistException extends RuntimeException{
public BookNotExistException() {
}
public BookNotExistException(String message) {
super(message);
}
}
列出测试数据和实验结果截图:
借记成功:
借记失败:
1. 经过本次实验,我掌握了以下内容:
2、通过掌握以上内容,我能够初步进行类的设计,编写基本的面向对象的程序;
3. 遇到了一些语法上的问题,这是由于代码敲的不多,对语言不熟练。不会的地方通过翻书、查资料,基本能够解决问题。