一、前言:
通过面向对象程序设计这门课程开展了关于Java这门语言的学习。至今,已经完成了三次PTA 的大作业,这三次大作业主要围绕Java的结构,类的使用,还有一些函数的知识,但主要还是类的使用。三次作业的难度是循序渐进的,作业量也是逐渐增加。以下是对这三次PTA作业的概要分析。
二、设计与分析:
PTA第一次:
在该次作业中共有9道习题,但大多数都为一个C语言向Java过渡的历程,在学习了Java的基本结构都可以完成,因为其中一些知识的运用和C语言相差不大,因此得分也为100分。其中我将主要分析7-8巴比伦法求平方根近似值和7-7判断三角形类型。
巴比伦法求n的近似值可以用以下公式:
nextGuess = (lastGuess+n/lastGuess)/2
程序初始运行时lastGuess可赋予一个最初的猜测值。当由公式求得的nextGuess和lastGuess相差较大时,把nextGuess的值赋给lastGuess,继续以上过程,直至nextGuess和lastGuess几乎相同,此时lastGuess或者nextGuess就是平方根的近似值。
本题要求:nextGuess和lastGuess的差值小于0.00001时认为两者几乎相同
输入格式:
1、两个浮点数,以空格分隔,第一个是n,第二个是lastGuess最初的猜测值。例如:2 1。
2、若输入的两个数中包含负数或者lastGuess初始输入为0,认定为非法输入
输出格式:
1、输出n的平方根近似值:lastGuess。例如:1.4142157
2、非法输入时输出:"Wrong Format"
输入样例:
在这里给出一组输入。例如:
2 1
输出样例:
在这里给出相应的输出。例如:
1.4142157
输入样例1:
在这里给出一组输入1。例如:
2 -1
输出样例:
在这里给出相应的输出。例如:
Wrong Format
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); float n = input.nextFloat(); float lG = input.nextFloat(); float nG = (lG+n/lG)/2; float c = nG - lG; if(n<0||lG<=0) { System.out.print("Wrong Format"); return ; } while(Math.abs(c)>=0.00001) { lG = nG; nG = (lG+n/lG)/2; c = nG - lG; } System.out.print((float)(lG)); } }
分析:这道题我的错误点在与一个数据类型的运用。就是float和double之间的差别,这两者的精准度不同,我最开始用double测试多次,发现可能不是方法的错误,应该是定义时没有考虑到题目中这个0.00001的这个精准度,在我更换float型后,通过了测试。
输入三角形三条边,判断该三角形为什么类型的三角形。
输入格式:
在一行中输入三角形的三条边的值(实型数),可以用一个或多个空格或回车分隔,其中三条边的取值范围均为[1,200]。
输出格式:
(1)如果输入数据非法,则输出“Wrong Format”;
(2)如果输入数据合法,但三条边不能构成三角形,则输出“Not a triangle”;
(3)如果输入数据合法且能够成等边三角形,则输出“Equilateral triangle”;
(3)如果输入数据合法且能够成等腰直角三角形,则输出“Isosceles right-angled triangle”;
(5)如果输入数据合法且能够成等腰三角形,则输出“Isosceles triangle”;
(6)如果输入数据合法且能够成直角三角形,则输出“Right-angled triangle”;
(7)如果输入数据合法且能够成一般三角形,则输出“General triangle”。
输入样例1:
在这里给出一组输入。例如:
50 50 50.0
输出样例1:
在这里给出相应的输出。例如:
Equilateral triangle
输入样例2:
在这里给出一组输入。例如:
60.2 60.2 80.56
输出样例2:
在这里给出相应的输出。例如:
Isosceles triangle
输入样例3:
在这里给出一组输入。例如:
0.5 20.5 80
输出样例3:
在这里给出相应的输出。例如:
Wrong Format
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); float a = in.nextFloat(); float b = in.nextFloat(); float c = in.nextFloat(); if((a<1||b<1||c<1)||(a>200||b>200||c>200))//非法数值 { System.out.println("Wrong Format"); return ; } else if((a+b>c)&&(a+c>b)&&(c+b>a)) { if(a==b&&a==c) { System.out.println("Equilateral triangle"); return ; } else if((a==b)&&(Math.pow(a,2)+Math.pow(b,2)-Math.pow(c,2)<0.00001)) { System.out.println("Isosceles right-angled triangle"); return ; } else if((a==c)&&(Math.pow(a,2)+Math.pow(c,2)-Math.pow(b,2)<0.00001)) { System.out.println("Isosceles right-angled triangle"); return ; } else if((b==c)&&(Math.pow(b,2)+Math.pow(c,2)-Math.pow(a,2)<0.00001)) { System.out.println("Isosceles right-angled triangle"); return ; } else if(a==b||a==c||c==b) { System.out.println("Isosceles triangle"); return ; } else if(Math.pow(a,2)+Math.pow(c,2)-Math.pow(b,2)<0.00001) { System.out.println("Right-angled triangle"); return ; } else if(Math.pow(a,2)+Math.pow(b,2)-Math.pow(c,2)<0.00001) { System.out.println("Right-angled triangle"); return ; } else if(Math.pow(b,2)+Math.pow(c,2)-Math.pow(a,2)<0.00001) { System.out.println("Right-angled triangle"); return ; } System.out.println("General triangle"); return ; } else System.out.println("Not a triangle"); } }
分析:
这题主要是一个思路的清晰度,因为判断三角形类型包括先判断是否还是直接判断类型,这个思路理清楚就没有问题。当然这其中还有Math类的一些使用。
PTA第二次:
第二次中共有四道题目,虽然数量不达第一次的一半,但需要去真正的在作业中运用类、方法等的使用,难度也比第一次上升了一倍不止。但是在这次作业中,我对类、方法等这些的理解使用并不理想,因此本次的分数险过及格线。接下来我会比较详细分析7-1 菜单计价程序-1、7-2 菜单计价程序-2、7-3 jmu-java-日期类的基本使用。第四题主要是一个算法的计算,就不在这展开分析了。
- 给定一个日期,判定是否为合法日期。如果合法,判断该年是否闰年,该日期是当年第几天、当月第几天、当周第几天、。
- 给定起始日期与结束日期,判定日期是否合法且结束日期是否早于起始日期。如果均合法,输出结束日期与起始日期之间的相差的天数、月数、念书。
输入格式:
第一行输入一个日期字符串,格式为"YYYY-MM-dd"
第二行输入两个日期字符串,中间使用空格隔开。分别代表开始日期与结束日期。
输出格式:
如果第一行日期字符串非法,输出自定义的错误信息。
如果第一行日期有效,输出相关信息,如果是闰年要输出是闰年。
如果第二行两个日期,只要有一个无效。就输出相关错误信息。
如果第二行两个日期有效且结束日期不早于开始日期,输出相关信息。
输入样例1:
第一行日期非法、第二行有日期非法
2020-02-30
2020-02-30 2020-01-02
输出样例1:
2020-02-30无效!
2020-02-30或2020-01-02中有不合法的日期.
输入样例2:
均有效且合法
2021-02-28
2019-08-01 2020-01-02
输出样例2:
2021-02-28是当年第59天,当月第28天,当周第7天.
2020-01-02与2019-08-01之间相差154天,所在月份相差-7,所在年份相差1.
输入样例3:
日期均有效,但结束日期早于开始日期
2020-02-28
2020-02-02 2020-02-01
输出样例3:
2020-02-28是闰年.
2020-02-28是当年第59天,当月第28天,当周第5天.
2020-02-01早于2020-02-02,不合法!
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Scanner; public class Main {
public interface DateValidator { boolean isValid(String dateStr); } public static class DateValidatorUsingDateFormat implements DateValidator { private String dateFormat; public DateValidatorUsingDateFormat(String dateFormat) { this.dateFormat = dateFormat; } public boolean isValid(String dateStr) { DateFormat sdf = new SimpleDateFormat(this.dateFormat); sdf.setLenient(false); try { sdf.parse(dateStr);
} catch (ParseException e) { return false; } return true; } } public static void main(String[] args) { Scanner in = new Scanner(System.in); String inputDate = in.nextLine(); DateValidator validator = new DateValidatorUsingDateFormat("yyyy-MM-dd");、 if (validator.isValid(inputDate)) { LocalDate date = LocalDate.parse(inputDate, DateTimeFormatter.ISO_LOCAL_DATE); boolean isLeapYear = date.isLeapYear(); if (isLeapYear) { System.out.println(inputDate + "是闰年."); } int dayOfYear = date.getDayOfYear(); int dayOfMonth = date.getDayOfMonth(); int dayOfWeek = date.getDayOfWeek().getValue(); System.out.println(inputDate+"是当年第"+dayOfYear+"天,当月第"+dayOfMonth+"天,当周第"+dayOfWeek+"天."); } else { System.out.println(inputDate + "无效!"); } String inputDates = in.nextLine(); String[] dates = inputDates.split(" "); String startDateStr = dates[0]; String endDateStr = dates[1]; if (validator.isValid(startDateStr) && validator.isValid(endDateStr)) { LocalDate startDate = LocalDate.parse(startDateStr, DateTimeFormatter.ISO_LOCAL_DATE); LocalDate endDate = LocalDate.parse(endDateStr, DateTimeFormatter.ISO_LOCAL_DATE); if (endDate.isBefore(startDate)) { System.out.println(endDateStr + "早于" + startDateStr + ",不合法!"); return; } long days = ChronoUnit.DAYS.between(startDate, endDate); long months = ChronoUnit.MONTHS.between(startDate, endDate); long years = ChronoUnit.YEARS.between(startDate, endDate); System.out.println(endDateStr + "与" + startDateStr + "之间相差" + days + "天,所在月份相差" + (months-12) + ",所在年份相差" + years + "."); } else { System.out.println(startDateStr + "或" + endDateStr + "中有不合法的日期."); } in.close(); } }
分析:
首先先说明一下第三题。这道题我得了三分,测试是有答案错误,非零返回,运行超时这三个错误。因为在运行测试时能够获取到对应的输出,因此答案错误和运行超时出乎我意料的错误。运行超时在我这道题中的原因大概有两个,一个是:程序中存在死循环,无限递归这种;另外一个是:程序设计问题:程序设计不合理,算法复杂度过高,导致程序运行缓慢或超时。然后观察我的代码,就是简单的东西复杂化了,答案错误是由于部分方法的顺序出现了问题。非零返回:非零返回通常表示方法执行出错或异常,
- 方法执行出错,例如参数错误、空指针异常等;
- 方法执行过程中发生异常,例如数组下标越界、文件读取错误等;
- 方法执行被中断,例如线程被强制终止等;
- 方法返回非零值是为了表明某种特定的状态或结果,例如某些系统调用返回值为非零表示执行成功。
在根据原因去调试后。我在eclipse也运行出了答案。
某饭店提供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
import java.util.Scanner; public class Main { public static void main(String[] args) { Order order = new Order(); Scanner sc = new Scanner(System.in); String line = sc.nextLine(); while (!line.equals("end")) { String[] parts = line.split(" "); if (parts.length == 2) { String dishName = parts[0]; int portion = Integer.parseInt(parts[1]); order.addARecord(dishName, portion); } else { System.out.println("Invalid input"); } line = sc.nextLine(); } sc.close(); int totalPrice = order.getTotalPrice(); System.out.println(totalPrice); } } class Dish{ String name; int unit_price; public Dish(String name, int unit_price) { this.name = name; this.unit_price = unit_price; } int getPrice(int portion){ switch (portion){ case 1:{ return unit_price; } case 2:{ return (int) Math.round(unit_price * 1.5); } case 3:{ return unit_price*2; } } return 0; } } class Menu{ Dish[] dishs; public Menu() { dishs=new Dish[4]; Dish dish1 = new Dish("西红柿炒蛋", 15); Dish dish2 = new Dish("清炒土豆丝", 12); Dish dish3 = new Dish("麻婆豆腐", 12); Dish dish4 = new Dish("油淋生菜", 9); dishs[0]=dish1; dishs[1]=dish2; dishs[2]=dish3; dishs[3]=dish4; } public Dish searchDish(String dishName){ for (Dish dish : dishs) { if (dish.name.equals(dishName)) return dish; } return null; } } class Record{ Dish d; int portion; public Record(Dish d, int portion) { this.d = d; this.portion = portion; } public int getPrice(){ return d.getPrice(portion); } } class Order{ Record[] records; int size; public Order() { records=new Record[10]; size=0; } int getTotalPrice(){ int total=0; for (int i = 0; i < size; i++) { total+=records[i].getPrice(); } return total; } Record addARecord(String dishName,int portion){ Menu menu = new Menu(); Dish dish = menu.searchDish(dishName); if(dish != null){ Record record = new Record(dish,portion); records[size++] = record; return record; }else{ System.out.println(dishName + " does not exist"); return null; } } }
分析:最开始,因为类、方法这些我个人的能力问题,所以最开始使用了面向过程的方法(博客中展示的是后面改进的面向对象的代码),在我从面向过程到面向对象,主要就是类在代码中的运用,包括分成Dish{},Mune{},Record{},Order{}这些。我倾向于理解为拆分的一个过程,但其实有些主要的算法其实在这道题里差别不是很大,主要都是输入选择的判断和一个equals()的使用。在写出这个题目大概对类的使用更熟悉包括使用这个equals()这个函数的使用去了解这个函数的用法。
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:点菜记录和删除信息。每一类信息都可包含一条或多条记录,每条记录一行。
点菜记录包含:序号、菜名、份额、份数。
份额可选项包括:1、2、3,分别代表小、中、大份。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。
参考以下类的模板进行设计:
菜品类:对应菜谱上一道菜的信息。
Dish {
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份) }
菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
Dish addDish(String dishName,int unit_price)//添加一道菜品信息
}
点菜记录类:保存订单上的一道菜品记录
Record {
int orderNum;//序号\
Dish d;//菜品\
int portion;//份额(1/2/3代表小/中/大份)\
int getPrice()//计价,计算本条记录的价格\
}
订单类:保存用户点的所有菜的信息。
Order {
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(int orderNum,String dishName,int portion,int num)//添加一条菜品信息到订单中。
delARecordByOrderNum(int orderNum)//根据序号删除一条记录
findRecordByNum(int orderNum)//根据序号查找一条记录
}
输入格式:
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:
序号+英文空格+菜名+英文空格+份额+英文空格+份数
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
最后一条记录以“end”结束。
输出格式:
按顺序输出每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。
如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后输出订单上所有菜品的总价(整数数值),
本次题目不考虑其他错误情况,如:菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的下一次作业中会做要求。
输入样例:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
1 麻婆豆腐 2 2
2 油淋生菜 1 3
end
输出样例:
在这里给出相应的输出。例如:
1 麻婆豆腐 36
2 油淋生菜 27
63
输入样例1:
订单中包含删除记录。例如:
麻婆豆腐 12
油淋生菜 9
1 麻婆豆腐 2 2
2 油淋生菜 1 3
1 delete
end
输出样例1:
在这里给出相应的输出。例如:
1 麻婆豆腐 36
2 油淋生菜 27
27
输入样例2:
订单中包含不存在的菜品记录。例如:
麻婆豆腐 12
油淋生菜 9
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
end
输出样例2:
在这里给出相应的输出。例如:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
63
输入样例3:
订单中包含删除信息以及不存在的菜品记录。例如:
麻婆豆腐 12
油淋生菜 9
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
1 delete
7 delete
end
输出样例3:
在这里给出相应的输出。例如:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
delete error;
27
输入样例4:
订单中包含删除信息以及不存在的菜品记录。例如:
麻婆豆腐 12
油淋生菜 9
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
5 delete
7 delete
end
输出样例4:
在这里给出相应的输出。例如:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
delete error;
delete error;
63
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); Menu menu=new Menu(); Order order=new Order(); String line = sc.nextLine(); //读取一行输入 while (!line.equals("end")) { //如果输入不是"end" String[] parts = line.split(" "); //按空格分割输入 if(parts.length==4){ order.addARecord(Integer.parseInt(parts[0]),parts[1],Integer.parseInt(parts[2]),Integer.parseInt(parts[3]),menu); } if(parts.length==2){ if(Character.isDigit(parts[0].toCharArray()[0])){ order.delARecordByOrderNum(Integer.parseInt(parts[0])); }else { menu.addDish(parts[0],Integer.parseInt(parts[1])); } } line = sc.nextLine(); //读取下一行输入 } order.print(); int totalPrice = order.getTotalPrice(); //计算订单总价 System.out.println(totalPrice); //输出总价 } } class Dish { String name; int unit_price; public Dish(String name, int unit_price) { this.name = name; this.unit_price = unit_price; } public int getPrice(int portion) { if (portion == 1) return unit_price; else if (portion == 2) return (int) Math.round(unit_price * 1.5); else return unit_price * 2; } } class Menu { Dish[] dishs; int cnt = 0; public Menu() { dishs = new Dish[100]; } public Dish searchDish(String dishName) { for (int i = 0; i < cnt; i++) { if (dishs[i].name.equals(dishName)) return dishs[i]; } return null; } public void addDish(String dishName, int unit_price) { dishs[cnt++] = new Dish(dishName, unit_price); } } class Record { int orderNum; Dish d; int portion; int num; @Override public String toString() { return "Record{" + "orderNum=" + orderNum + ", d=" + d + ", portion=" + portion + ", num=" + num + '}'; } public Record(int orderNum, Dish d, int portion, int num) { this.orderNum = orderNum; this.d = d; this.portion = portion; this.num=num; } public int getPrice() { return d.getPrice(portion)*num; } } class Order { Record[] records = new Record[100]; int cnt = 0; Record addARecord(int orderNum,String dishName,int portion,int num,Menu menu){ Dish dish = menu.searchDish(dishName);//根据输入的dishName在Menu中查找对应的Dish对象 if(dish != null){//如果存在该Dish对象 Record record = new Record(orderNum,dish,portion,num);//创建一个新的Record对象 records[cnt++] = record;//将Record对象添加到records数组中,并更新size值 return record; }else{//如果不存在该Dish对象 System.out.println(dishName + " does not exist");//输出提示信息 return null; } } public void delARecordByOrderNum(int orderNum){ int flag=cnt; for(int i=0;i<cnt;i++){ if(records[i].orderNum==orderNum){ for (int j = i; j <records.length-1 ; j++) { records[i]=records[i+1]; } cnt--; break; } } if(flag==cnt){ System.out.println("delete error"); } } public Record findRecordByOrderNum(int orderNum){ for(int i=0;i<cnt;i++){ if(records[i].orderNum==orderNum)return records[i]; } return null; } public void print(){ for(int i=0;i<cnt;i++){ System.out.println(records[i].orderNum+" "+records[i].d.name+" "+records[i].getPrice()); } } public int getTotalPrice(){ int res=0; for(int i=0;i<cnt;i++){ res+=records[i].getPrice(); } return res; } }
分析:这题最开始我直接用一提交了一下,发现过了一个测试点----菜单那个一分。我提交的时候没能实现那个删除功能,这也还是类的一个使用,但是也需要去学会使用像split(),Integer.parseInt9()这些方法,比如说Integer.parseInt() 是一个 Java 方法,用于将字符串转换为整数如果字符串 str 无法转换为整数,会抛出 NumberFormatException 异常.这里提一下 @Override 这个的使用,
@Override是Java中的一个注解,在方法或类上使用,表示该方法或类是覆盖或实现父类或接口的方法或类。在使用@Override时,必须确保以下几点:
-
该方法或类必须是继承或实现自父类或接口中的方法或类。
-
方法名、参数列表和返回值类型必须与父类或接口中的方法或类相同。
-
方法的访问修饰符不能比父类中的方法更严格。
-
方法不能抛出比父类中方法更多的异常。
在使用@Override时,可以帮助我们更好地理解代码,减少代码的冗余和错误。同时,也可以增加代码的可读性和可维护性。
但是这个在菜单二中使用与否其实并没有很大差别(当时在论坛上看到了解了一下)。
然后就是这个删除,我考虑的是当读取到于前面已经点单的菜品序号相同时,如果后面的字符为delete,其实也就是根据序号删除,再选择结果是删除还是输出“delete error”。一和二就是一个慢慢往前爬的一个过程,包括功能的增加。
PTA第三次:
该次共有七道题,这里我只列举7-1 菜单计价程序-3和7-7 判断两个日期的先后,计算间隔天数、周数、7-6 GPS测绘中度分秒转换这三题。
其他四题就在这只用文字说明一下,首先是7-2 有重复的数据,这个最开始我使用一个循环的嵌套去实现检查重复,虽然运行测试没问题,但是提交就全错,运行超时,非零返回,(这两个原因前面讲过),改了很多次去精简,最后询问了蔡老师,说尝试减少循环,使用Set<Integer> set = new HashSet<>()
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:桌号标识、点菜记录和删除信息、代点菜信息。每一类信息都可包含一条或多条记录,每条记录一行或多行。
桌号标识独占一行,包含两个信息:桌号、时间。
桌号以下的所有记录都是本桌的记录,直至下一个桌号标识。
点菜记录包含:序号、菜名、份额、份数。份额可选项包括:1、2、3,分别代表小、中、大份。
不同份额菜价的计算方法:小份菜的价格=菜品的基础价格。中份菜的价格=菜品的基础价格1.5。小份菜的价格=菜品的基础价格2。如果计算出现小数,按四舍五入的规则进行处理。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
如果序号不对,输出"delete error"
代点菜信息包含:桌号 序号 菜品名称 份额 分数
代点菜是当前桌为另外一桌点菜,信息中的桌号是另一桌的桌号,带点菜的价格计算在当前这一桌。
程序最后按输入的先后顺序依次输出每一桌的总价(注意:由于有代点菜的功能,总价不一定等于当前桌上的菜的价格之和)。
每桌的总价等于那一桌所有菜的价格之和乘以折扣。如存在小数,按四舍五入规则计算,保留整数。
折扣的计算方法(注:以下时间段均按闭区间计算):
周一至周五营业时间与折扣:晚上(17:00-20:30)8折,周一至周五中午(10:30--14:30)6折,其余时间不营业。
周末全价,营业时间:9:30-21:30
如果下单时间不在营业范围内,输出"table " + t.tableNum + " out of opening hours"
参考以下类的模板进行设计:菜品类:对应菜谱上一道菜的信息。
Dish {
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份) }
菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
Dish\[\] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
Dish addDish(String dishName,int unit_price)//添加一道菜品信息
}
点菜记录类:保存订单上的一道菜品记录
Record {
int orderNum;//序号\\
Dish d;//菜品\\
int portion;//份额(1/2/3代表小/中/大份)\\
int getPrice()//计价,计算本条记录的价格\\
}
订单类:保存用户点的所有菜的信息。
Order {
Record\[\] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(int orderNum,String dishName,int portion,int num)//添加一条菜品信息到订单中。
delARecordByOrderNum(int orderNum)//根据序号删除一条记录
findRecordByNum(int orderNum)//根据序号查找一条记录
}
### 输入格式:
桌号标识格式:table + 序号 +英文空格+ 日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:序号+英文空格+菜名+英文空格+份额+英文空格+份数注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
代点菜信息包含:桌号+英文空格+序号+英文空格+菜品名称+英文空格+份额+英文空格+分数
最后一条记录以“end”结束。
### 输出格式:
按输入顺序输出每一桌的订单记录处理信息,包括:
1、桌号,格式:table+英文空格+桌号+”:”
2、按顺序输出当前这一桌每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品\*份数,序号是之前输入的订单记录的序号。如果订单中包含不能识别的菜名,则输出“\*\* does not exist”,\*\*是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后按输入顺序一次输出每一桌所有菜品的总价(整数数值)格式:table+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。
输入格式:
桌号标识格式:table + 序号 +英文空格+ 日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:序号+英文空格+菜名+英文空格+份额+英文空格+份数注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
代点菜信息包含:桌号+英文空格+序号+英文空格+菜品名称+英文空格+份额+英文空格+分数
最后一条记录以“end”结束。
输出格式:
按输入顺序输出每一桌的订单记录处理信息,包括:
1、桌号,格式:table+英文空格+桌号+“:”
2、按顺序输出当前这一桌每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品\*份数,序号是之前输入的订单记录的序号。如果订单中包含不能识别的菜名,则输出“\*\* does not exist”,\*\*是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后按输入顺序一次输出每一桌所有菜品的总价(整数数值)格式:table+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。
输入样例:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 12/2/3
1 麻婆豆腐 2 2
2 油淋生菜 1 3
end
输出样例:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
table 1: 38
输入样例1:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 17/0/0
1 麻婆豆腐 2 2
2 油淋生菜 1 3
1 delete
end
输出样例1:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
table 1: 22
输入样例2:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 16/59/59
1 麻婆豆腐 2 2
2 油淋生菜 1 3
1 delete
end
输出样例2:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
table 1 out of opening hours
输入样例3:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2022/12/5 15/03/02
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
5 delete
7 delete
table 2 2022/12/3 15/03/02
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
7 delete
end
输出样例3:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
delete error;
delete error;
table 2:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
delete error;
table 1 out of opening hours
table 2: 63
输入样例4:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2022/12/3 19/5/12
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
table 2 2022/12/3 15/03/02
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
1 4 麻婆豆腐 1 1
7 delete
end
输出样例4:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
table 2:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
4 table 2 pay for table 1 12
delete error;
table 1: 63
table 2: 75
在测绘中,获取经度和纬度信息的时候,可以是度分秒格式,也可以是小数点格式。例如一个北纬的纬度信息,30°41′52.37″ ,可以转换为 30.697881。
规则:
(1)度和分都是整数,秒可以含有小数。将用户输入的第一个整数,加上第二个整数除以60,再加上第三个浮点数除以3600,就是最终结果。
(2)在输出结果时,保留6位小数。
(3)题目假定用户输入的数据,一定是合法的数据。
输入格式:
三个数值,数之间用空格分开。
输出格式:
见输出样例。
输入样例:
两个整数后,跟上一个小数。数据之间用空格分开。三个数分别代表度、分、秒。
30 41 52.37
输出样例:
输出经纬度信息的小数点格式,保留6位小数。
注意等号的前后有且仅有一个空格,建议复制下面输出到你的代码中,再进行修改。
30°41′52.37″ = 30.697881
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); double a,b, c,s; a = in.nextDouble(); b = in.nextDouble(); c = in.nextDouble(); s=0; if(a==(int)a&&b==(int)b) { s=a+b/60+c/3600; System.out.println((int)a+"°"+(int)b+"′"+c+"″"+" = "+String.format("%.6f",s)); } } }
分析:这里主要提一下小数点的问题,使用double定义数据,最开始我使用print(),出现输出都是六位小数(但这个需要输入与输出一致),而后我又使用字符串转换来保证输入输出一致,但是增加复杂度,最后发现用format就可以了。
从键盘输入两个日期,格式如:2022-06-18。判断两个日期的先后,并输出它们之间间隔的天数、周数(不足一周按0计算)。
预备知识:通过查询Java API文档,了解Scanner类中nextLine()等方法、String类中split()等方法、Integer类中parseInt()等方法的用法,了解LocalDate类中of()、isAfter()、isBefore()、until()等方法的使用规则,了解ChronoUnit类中DAYS、WEEKS、MONTHS等单位的用法。
输入格式:
输入两行,每行输入一个日期,日期格式如:2022-06-18
输出格式:
第一行输出:第一个日期比第二个日期更早(晚)
第二行输出:两个日期间隔XX天
第三行输出:两个日期间隔XX周
输入样例1:
2000-02-18
2000-03-15
输出样例1:
第一个日期比第二个日期更早
两个日期间隔26天
两个日期间隔3周
输入样例2:
2022-6-18
2022-6-1
输出样例2:
第一个日期比第二个日期更晚
两个日期间隔17天
两个日期间隔2周
import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String dateStr1 = scanner.nextLine(); String dateStr2 = scanner.nextLine(); // 利用字符串的split方法将年月日分离出来 String[] dateArr1 = dateStr1.split("-"); String[] dateArr2 = dateStr2.split("-"); // 将分离出来的年月日转换为整数 int year1 = Integer.parseInt(dateArr1[0]); int month1 = Integer.parseInt(dateArr1[1]); int day1 = Integer.parseInt(dateArr1[2]); int year2 = Integer.parseInt(dateArr2[0]); int month2 = Integer.parseInt(dateArr2[1]); int day2 = Integer.parseInt(dateArr2[2]); // 利用LocalDate类创建日期对象 LocalDate date1 = LocalDate.of(year1, month1, day1); LocalDate date2 = LocalDate.of(year2, month2, day2); // 比较两个日期的先后关系,输出结果 if (date1.isBefore(date2)) { System.out.println("第一个日期比第二个日期更早"); } else if (date1.isAfter(date2)) { System.out.println("第一个日期比第二个日期更晚"); } else { System.out.println("两个日期相同"); } // 计算两个日期间隔的天数和周数 long days = date1.until(date2, ChronoUnit.DAYS); long weeks = date1.until(date2, ChronoUnit.WEEKS); System.out.printf("两个日期间隔%d天\n", Math.abs(days)); System.out.printf("两个日期间隔%d周\n", Math.abs(weeks)); } }
分析:最开始我最开始使用的,但是因为没有考虑到绝对值(Math.abs()),导致输出会出现负数,后面就参照论坛改了,
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String date1Str = in.nextLine();
String date2Str = in.nextLine();
LocalDate date1 = LocalDate.parse(date1Str);
LocalDate date2 = LocalDate.parse(date2Str);
if (date1.isBefore(date2)) {
System.out.println("第一个日期比第二个日期更早");
} else if (date1.isAfter(date2)) {
System.out.println("第一个日期比第二个日期更晚");
} else {
System.out.println("两个日期相同");
}
long days = date1.until(date2, ChronoUnit.DAYS);
System.out.println("两个日期间隔" + days + "天");
long weeks = date1.until(date2, ChronoUnit.WEEKS);
System.out.println("两个日期间隔" + weeks + "周");
}
}
这是快截止时使用的,但是存在一个致命问题,他无法正常读取第二个示例,只能正常读取YY-MM-DD,最后就没交上。
最后和同学邱桑沟通了一下,最后还是需要用数组,也就是我在此次博客中展示的那个,添加了绝对值并且删除了一些没有必要的选择分支,eclipse能够成功运行。
三:踩坑心得
1.数据的类型定义和小数点输出的细节问题,尤其是floath和double,在计算pta时,有次我把double换成float,计算结果就错误,包括有个输出使用Math.round就通过了,一些细节的问题非常有必要去解决的。
2.输入的一些差异,比如在作业中遇到的next.Line()和next()纯在的差异;
3.对类的使用,就好比日期输入读取的问题,因为最开始对非法示例就无法正常读取,内容再改也无济于事;
4.结构问题,方法的调用,(还有面向过程的这种思想)就是不是很清楚的话,就导致最后出来的结果无法输出,但可能只需要你换一下顺序,也许就能正常输出。
四:主要困难以及改进建议
1.对新的类、方法函数的理解和使用,就比如日期那个代码;
2.精简代码的这一个过程,包括嵌套循环但是会出现超时,并且可靠性也存在问题;
3.还是类的一些设计使用的问题,菜单问题就是比较典型错误处(我在这三次作业中);
4.加油。
五:总结
第一是写博客让我们去重新思考并且总结这个作业代码的一个机会;
第二是在这三次作业,我认为理解学会最深的还是Java它的结构和类的使用,还有就是自学能力,因为有一部分其实他和C语言没有大的差异。需要进一步学习的也还是类这个的学习,要逐步去降低这个代码的风险等这些。
第三是Java的学习是线上线下结合的,概念知识一般从线上获取,这保证了一个复习的便利性,线下一般更多的思考和一些课堂的运用,还是很认可这种模式的。暂时没有比较好的建议。
第四是对自己加油。