南昌航空大学BLOG-1 (软件学院-22206123)

发布时间 2023-04-01 23:32:59作者: 薛之谦冒险历程cloud

一、前言

本学期开展了面向对象程序设计这门课程,目前,我们已经在PTA上完成了三次Java大作业。第一次大作业一共九题,第二次大作业一共四题,第三次大作业一共七题。

第一次作业主要是让我们熟悉并掌握基本的Java语法,如输入、输出的运用,import关键字的使用等,同时复习顺序结构、选择结构、循环结构等知识。题量虽然多,但难度较低,我在写这次作业的时候没有使用面向对象的思想。

第二次作业开始使用面向对象的思想。第一题涉及菜单计价系统中保存菜谱中所有菜品信息、保存订单上的一道菜品记录、保存用户点的所有菜的信息、计算一道菜品价格、计算订单的总价等功能。第二道题在此基础上增加了删除一条点菜记录的功能。对于我来说,比起第一次作业,第二次作业的难度大幅上升,超出了我的能力范围。主要原因是我在写这次作业之前没有及时了解类与对象的关系,成员方法的调用方法等,同时我的思路还停留在面向过程中,没有很好地理解各个类之间的关系,导致面对两题菜单计价程序时无从下手。后续我将对此次作业第一题、第二题进行分析。

第三次大作业第一题是菜单计价系列的延续,在此前两道题的基础上增加了桌号、代点菜和根据点单时间计算折扣等功能。第二到七题和第一次大作业一样没有使用面向对象的思想,主要是让我们掌握ArrayListHashSet、时间类LocalDateChronoUnit等、String类中split()等方法的用法。后续我将对此次作业第一题和第四题进行分析。

二、设计与分析

1、菜单计价程序-1(第二次大作业)

某饭店提供4种菜,每种菜品的基础价格如下:
西红柿炒蛋 15
清炒土豆丝 12
麻婆豆腐 12
油淋生菜 9

设计点菜计价程序,根据输入的订单,计算并输出总价格。
订单由一条或多条点菜记录组成,每条记录一行,最后以"end"结束
每条点菜记录包含:菜名、份额两个信息。
份额可选项包括:1、2、3,分别代表小、中、大份)

不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。

参考以下类的模板进行设计:
菜品类:对应菜谱上一道菜的信息。
Dish {
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
}

菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
}

点菜记录类:保存订单上的一道菜品记录
Record {
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
int getPrice()//计价,计算本条记录的价格
}

订单类:保存用户点的所有菜的信息。
Order {
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(String dishName,int portion)
//添加一条菜品信息到订单中。
}

输入格式:
每条点菜记录的格式:
菜名+空格(英文)+份额
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
最后一条记录以“end”结束。

输出格式:
订单上所有菜品的总价(整数数值),每份菜
如果订单中包含不能识别的菜名,则在总价之前输出“** does not exist”,**是不能识别的菜名

输入样例:
在这里给出一组输入。例如:

麻婆豆腐 2
西红柿炒蛋 3
end
输出样例:
在这里给出相应的输出。例如:

48
输入样例1:
订单中包含不存在的菜品记录。例如:

麻婆豆腐 2
炒脆肚 2
西红柿炒蛋 3
end
输出样例1:
在这里给出相应的输出。例如:

炒脆肚 does not exist
48
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

代码如下:

import java.util.Scanner;

public class Main {

public static void main(String[] args) {

Scanner input=new Scanner(System.in);

String dishName;

Menu m=new Menu();

int s = 0;

Order q=new Order();

Record r=new Record();

int i;

for(i=0;i<100;i++)

{

dishName=input.nextLine();

if(dishName.equals("end"))

{

System.out.println(s);

i=101;

}

String[] a=dishName.split(" ");

int portion=Integer.parseInt(a[1]);

r=q.addARecord(a[0], portion);

s=q.getTotalPrice();

}

}

}

class Dish{

String name;//菜品名称

int unit_price; //单价

public Dish() {

}

public Dish(String name, int unit_price) {

this.name = name;

this.unit_price = unit_price;

}

public void setName(String name) {

this.name = name;

}

public void setPrice(int price) {

this.unit_price = unit_price;

}

public String getName() {

return this.name;

}

public int getunit_price() {

return this.unit_price;

}

//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小//大份)

public int getPrice(int portion){

double price=0;

switch(portion)

{

case 1:price=unit_price;

break;

case 2:price=unit_price*1.5;

break;

case 3:price=unit_price*2;

break;

}

return (int)Math.round(price);

}

}

class Menu{

//菜品数组,保存所有菜品信息

Dish[] dishs;

Menu(){

dishs=loadDishes();

}

Dish[] loadDishes() {

Dish[] load=new Dish[4];

load[0]=new Dish("西红柿炒蛋",15);

load[1]=new Dish("清炒土豆丝",12);

load[2]=new Dish("麻婆豆腐",12);

load[3]=new Dish("油淋生菜",9);

return load;

}

//根据菜名在菜谱中查找菜品信息,返回Dish对象。

Dish searthDish(String dishName){

for(int i=0;i<4;i++)

{

if(dishs[i].getName().equals(dishName))

{

return dishs[i];

}

}

return null;

}

}

//点菜记录类:保存订单上的一道菜品记录

class Record {

Dish d;//菜品

int portion;//份额(1/2/3代表小//大份)

public Record() {

}

public Record(Dish d,int portion) {

this.d=d;

this.portion=portion;

}

public int Price(){

if(d==null) {

return 0;

}

else {

return d.getPrice(portion);//计价,计算本条记录的价格

}

}

}

class Order {

Record[] records=new Record[100];//保存订单上每一道的记录

//添加一条菜品信息到订单中。

Record addARecord(String dishName,int portion){

Menu m=new Menu();

Dish s=m.searthDish(dishName);

// Record r=new Record(s,portion);

if(s==null)

{

System.out.println(dishName+" does not exist");

return null;

}

else {

for(int j=0;j<records.length;j++) {

if(records[j]==null) {

if(s!=null){

records[j]=new Record(s,portion);

return records[j];

}

}

}

}

return null;

}

//计算订单的总价

public int getTotalPrice() {

int sum=0;

for(int i=0;i<records.length;i++) {

if(records[i]!=null){

int p=records[i].Price();

if(p!=0) {

sum+=p;

}

}

}

return sum;

}

}

 

2、菜单计价程序-2(第二次大作业)

这道题因为时间不够,我没有写出完整代码。

 

3、菜单计价程序-3(第三次大作业)

这道题因为时间不够,我没有写出完整代码。

 

1、单词统计与排序(第三次大作业)

从键盘录入一段英文文本(句子之间的标点符号只包括“,”或“.”,单词之间、单词与标点之间都以" "分割。
要求:按照每个单词的长度由高到低输出各个单词(重复单词只输出一次),如果单词长度相同,则按照单词的首字母顺序(不区分大小写,首字母相同的比较第二个字母,以此类推)升序输出。

输入格式:
一段英文文本。

输出格式:
按照题目要求输出的各个单词(每个单词一行)。

输入样例:
Hello, I am a student from China.
输出样例:
student
China
Hello
from
am
a
I
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

 

代码如下:

import java.util.Arrays;
import java.util.Scanner;
import java.util.List;
import java.util.Set;
import java.util.HashSet;

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s = input.nextLine().replaceAll("[,.]", "");
String[] str = s.split(" ");

int i,j;
List list = Arrays.asList(str);
Set set = new HashSet(list);
String [] st=(String [])set.toArray(new String[0]);


String t;
String a="";
String b="";

for(j=0;j<st.length-1;j++) {
for(i = 0; i < st.length-1; i++) {

if(st[i].length()<st[i+1].length())
{
t=st[i];
st[i]=st[i+1];
st[i+1]=t;
}
if(st[i].length()==st[i+1].length())
{
for(int k=0;k<st[i].length();k++) {
//st[i]全部转换为小写字母
char ch=st[i].charAt(k);

if(ch>='A'&&ch<='Z')
{
ch=(char)((int)ch+32);
}
a+=ch;


//st[i+1]全部转换为小写字母
char ch1=st[i+1].charAt(k);

if(ch1>='A'&&ch1<='Z')
{

ch1=(char)((int)ch1+32);
}
b+=ch1;
}

if(a.compareTo(b)>0)
{
t=st[i];
st[i]=st[i+1];
st[i+1]=t;
a="";
b="";
}
else
{
a="";
b="";
}
}
}
}


for(i=0;i<st.length;i++) {
System.out.println(st[i]);
}

}
}

这题的难点主要在于判断条件太多,因为当时并没有学习正则关系,我使用了循环结构嵌套选择结构来写。题目里并没有写重复的单词只输出一遍,所以我的第一个测试点-较长文本一直过不去。最后我使用ListSet对输入文本先进行去重,再对单词长度和首字母顺序进行判断。

 

 

 

 

三、踩坑心得

 

1菜单计价程序-1

 

一开始,我不知道如何在Menu类中使用Dish类型的数组存入固定菜品,而且不知道如何检测菜品是否已经正确存入。我先尝试了这种写法:

 

Dish[] dishs=new Dish[4];

 

dishes[0]=new Dish("西红柿炒蛋",15);

 

dishes[1]=new Dish("清炒土豆丝",12);

 

dishes[2]=new Dish("麻婆豆腐",12);

 

dishes[3]=new Dish("油淋生菜",9);

 

这时出现了报错:Syntax error on token ";", { expected after this token

 

Syntax error, insert "}" to complete Block

 

我用{}dishes[0]=new Dish("西红柿炒蛋",15);

 

dishes[1]=new Dish("清炒土豆丝",12);

 

dishes[2]=new Dish("麻婆豆腐",12);

 

dishes[3]=new Dish("油淋生菜",9);

 

括起来,虽然不再报错,但我不知道这些信息是否存入了数组。经过多次修改,我决定使用

 

Dish[] dishs;

 

Menu(){

 

dishs=loadDishes();

 

}

 

Dish[] loadDishes() {

 

Dish[] load=new Dish[4];

 

load[0]=new Dish("西红柿炒蛋",15);

 

load[1]=new Dish("清炒土豆丝",12);

 

load[2]=new Dish("麻婆豆腐",12);

 

load[3]=new Dish("油淋生菜",9);

 

return load;

 

}

 

来存入固定菜品。

 

这之后,我的getTotalPrice方法出现了报错:Exception in thread mainjava.lang.NullPointerException

 

当时我的这段代码是这样写的:

 

public int getTotalPrice() {

 

int sum=0;

 

for(int i=0;i<records.length;i++) {

 

int p=records[i].Price();

 

sum+=p;

 

}

 

return sum;

 

}

 

用这种写法,当records[i]的值为null时,会出现空指针,这时不能调用records[i]的方法。

 

当时我在网上搜索这个报错的解决方案时,并没有理解“空指针”指的是records[i]的值为null这种情况。后来才发现此处需要加一个限制条件,只有当records[i]的值不为null时,才能调用records[i]的方法计算单价和总价。

 

解决完这个报错之后,我又因为运用结构不合理的for循环计算并输出总价,导致在程序还没读到end时,每读入一道点菜记录,就会输出一次当前订单的总价。然后我将System.out.println(s);

 

写在if(dishName.equals("end"))

 

里,但因为这时if(dishName.equals("end"))

 

没有写在for循环内,导致这个语句没有被执行。最后,我把if(dishName.equals("end"))

 

{

 

System.out.println(s);

 

i=101;

 

}

 

放在for循环内,终于避免了这个问题。

 

2、单词统计与排序

 

为了将长度相同的单词按字典顺序排序,我需要先把单词字母全部转化为小写(也可以全部转化为大写)再进行比较。一开始,我使用了这种写法:

 

if(st[i].length()==st[i+1].length())

 

            {

 

             for(int k=0;k<st[i].length();k++) {

 

             //st[i]全部转换为小写字母

 

         char ch=st[i].charAt(k);

 

        

 

         if(ch>='A'&&ch<='Z')

 

        {

 

         ch=(char)((int)ch+32);

 

         }

 

         a+=ch;

 

        

 

       

 

         //st[i+1]全部转换为小写字母

 

         char ch1=st[i+1].charAt(k);

 

        

 

         if(ch1>='A'&&ch1<='Z')

 

        {

 

        

 

        ch1=(char)((int)ch1+32);

 

      }

 

         b+=ch1;

 

             }

 

            

 

         if(a.compareTo(b)>0)

 

             {

 

             t=st[i];

 

                 st[i]=st[i+1];

 

                 st[i+1]=t;

 

             }

 

        

 

            }

 

当第二次出现长度相同的两个单词,字符串ab会带着上一次的单词继续往后增加字母,如输入Hello, I am a student from China.

 

时,a最后会等于ahelloahelloahelloahelloahelloa,

 

b最后会等于ichinaichinaichinaichinaichinai。为了解决这个问题,在每次比较完a和b的大小后,应该对a和b进行清零。

 

 

 

 

 

四、主要困难以及改进建议

 

主要困难:

 

1、我对类和对象,成员方法的调用等还很不熟练,也不能很好地理解类与类之间的关系。

 

2、我没有提前掌握HashSetArrayList、各种时间类的使用方法、Scanner的各种方法的用法,这导致我写代码的时间非常有限。

 

3、我不会使用Debug对程序进行调试,这导致我在程序报错的时候很难找到问题根源,也想不出解决方案。

 

改进建议:

 

希望以后的PTA大作业的测试点能写得再详细一点,若题目中需要用到没学过的知识,如第三次大作业的HashSetArrayList等,能在题目中给出提示。

 

五、总结

 

PTA作业和博客写作对我们来说是一很重要的学习、总结反思的机会。通过这三次作业我学了类的构造方法、成员方法的调用、构造函数、参数传递、对象的构造与使用等,并且熟悉了循环结构,了解了函数重载的定义,掌握了Java数据的输入与输出。同时,我对类的设计有了初步了解,虽然还不能完全独立地进行类的设计和后续代码的填充,但我一定会不断学习,不断完善自己的程序。