1.前言
自接触面向对象的程序设计这门课以来,已经完成了三次pta上的实验作业了,在这里对前面的练习做一些分析,同时也总结一些经验。
OK,首先,在难度上来说,这三次作业的难度是渐进式提升的,前面第一次第二次的难度都还好,但是后面的题目属实是难到我了,最后一题在要求的时间内甚至没做完,还是后面又做了半天才算能跑的。然后,如果从题量上来看的话,那第一次和第二次注重的是题目的量大但是难度小,后面注重的则是题目的量不多,而需要思考和调试的点明显增多了。可以说前两次作业是给我们练习基础的语法,而后面则是给我们提升自己对Java这门语言的熟练度。再说一说他们之间的联系,第一次作业训练的是数据的输入、处理和输出,字符串的查重删除增加这些基础的操作与语法,而第二次作业则是在实际情景下对上述语法的实际运用与设计,至于第三次作业,训练的是在更为复杂的情况下多个语法的嵌套与综合运用,所以他们之间的关系可以说是基础与提升,是循序渐进的关系。
2.设计与分析
那么,接下来我会对在前三次PTA作业中我个人认为比较重要的题目进行逐一的分析与解释,我会附上源码以及利用power designer所制作的图片来进行辅助说明(power designer用的不是很熟练,见谅),也会有一些我个人的心得与体会。
首先是作业二的第三题
这个题目是典型的输入数据进行处理后再输出,类似的还有银行存款问题,交税纳税问题一类的,就不一一赘述了,现在来分析一下这道题。
首先,我们有四个数据要输入,如下
double n=input.nextDouble();//第几次购房 double hm=input.nextDouble();//房款 double assess=input.nextDouble();//评估价 double s=input.nextDouble();//房屋面积
然后,在输出他们之前,我们还要进行一些计算,后面三个很好理解,就是直接计算然后输出即可,至于契税,要先进行判断,才能计算得出结果。这里利用powerdesigner制作图片可以便于理解代码。
if(n==1&&s<=90) qtax=assess*0.01*10000;//首次且小于90平 if(n==1&&s>90&&s<=144) qtax=assess*0.015*10000;//首次且在90-144平 if(n>1||s>144) qtax=assess*0.03*10000;//非首次或者首次但大于144平
这里简单来说就是通过购房次数与房屋大小来判断应该缴纳多少税,理解了之后也觉得没什么难点,只是初次见到的时候要思考一下而已。最后输出所要求的四个值即可。
System.out.print((float)qtax+" "); System.out.print((float)yfax+" "); System.out.print((float)jfee+" "); System.out.print((float)cfee);
这题不是难点,但是是很常见的一类题型,故在此分析。
接着我们来看一下这一题
这是一道考察我们条件语句的运用的题目,语法并不难,甚至可以说是很单一,仅仅考察了一个条件语句。但是做这种题主要考的是思路,因此我将结合图片来进行说明。
double a=input.nextDouble(); double b=input.nextDouble(); double c=input.nextDouble();
首先,当然是把三角形的三边输入进去,然后接下来是重点了,也就是对它是什么三角形进行判断的一环。为了方便判断,我们先让三条边abc从小到大进行重排。
if(a>b){ m=a;a=b;b=m; } if(a>c){ m=a;a=c;c=m; } if(b>c){ m=b;b=c;c=m; }
然后接下来我们就要开始通过三条边的关系来判断三角形的类型了。
if(a>=1&&a<=200&&b>=1&&b<=200&&c>=1&&c<=200){//判断输入是否符合条件 if(a+b>c){//判断能否组成三角形 if(a==b&&b==c)//判断是不是等边三角形 System.out.println("Equilateral triangle"); else if(a*a+b*b-c*c<0.000001&&a==b)//判断是不是等腰直角三角形 System.out.println("Isosceles right-angled triangle"); else if((a==b&&a!=c)||(a==c&&a!=b)||(b==c&&b!=a))//判断是不是等腰三角形 System.out.println("Isosceles triangle"); else if(a*a+b*b-c*c<0.000001)//判断是不是直角三角形 System.out.println("Right-angled triangle"); else System.out.println("General triangle");//如果不是上述三角形那只能是普通三角形 } else System.out.println("Not a triangle"); } else System.out.println("Wrong Format");
这道题也结束了,做这道题时我编程所花的时间其实没多少,主要还是前期理思路耗时比较多,所以在这里也再次总结一下巩固巩固。
然后就是折磨了我很久的后面两题了。
我们先来看倒数第二题。
刚拿到这道题的时候我就被它超长的题干吓住了,以至于考虑了很久都没有头绪,后来还是一边写一边理头绪的,现在回过头再来梳理一遍应该会好很多。
首先,不管做什么题目,我们先看它让我们要输入什么。这题是让我们在一行内输入年月日的值,因此,我们不管三七二十一先把这个处理好。
Scanner myscan = new Scanner(System.in); Date date = new Date(); date.Date(myscan.nextInt(),myscan.nextInt(),myscan.nextInt());//在一行内输入年月日的值
好,在完成这一步之后,继续看题,我们需要创建一个如上述图表所示的类。前面四个,年月日以及月份最大日期的数组很好定义,代码如下:
class Date{ private int year,month,day; int [] mon_maxnum={0,31,28,31,30,31,30,31,31,30,31,30,31}; public void Date(int year,int month,int day){ this.year=year;//定义年 this.day=day;//定义月 this.month=month;//定义日
}
接着我们需要得到输入的年月日了:
public int getYear(){ return year; } public void setYear(int year){ this.year=year;//得到年 }
public int getMonth(){ return month; } public void setMonth(int month){ this.month=month;//得到月 }
public int getDay(){ return day; } public void setDay(int day){ this.day=day;//得到日 }
接着看表格,我们在判断日期下一天是几号前,要先通过判断年份来确定是不是闰年,这也是我一开始错误的一个点,我看表格的时候没细看,导致忘记了判断是不是闰年,以至于开始的测试点一直过不了。判断闰年不难,只要看我们输入的年份是不是同时满足是四的倍数不是一百的倍数即可,但是不能忘记的一点是,四百的倍数也算闰年(我一开始就忘了)。
public boolean isLeapYear(int year){ if((year%400==0)||(year%100!=0&&year%4==0)){//进行上述判断,看是不是闰年 mon_maxnum[2]++; } public boolean checkInputValidity(){ if(this.isLeapYear(year)==true) { mon_maxnum[2]=29;//如果是闰年的话二月份29天 } else { mon_maxnum[2]=28;//如果不是闰年的话二月份28天 }
当然,输入数据如果不符合规范也是不行的,要对输入的数据判断合不合法。如题,输入的年份只能在1900-2000年,月份最多十二个月,日期最多31天。
if(this.year<1900||this.year>2000||this.month<1||this.month>12||mon_maxnum[this.month]<this.day||this.day<1)//对输入数据进行如上判断 { System.out.println("Date Format is Wrong");//输出“日期格式错误”
接下来就是最后的输出了,只要考虑两个边界即可,一个就是跨年,即当我们输入12月31日的时候,年份加一,输出一月一日,另外一个则是跨月,当我们输入这个月的最后一天的时候,即识别到和mon_maxnum[month]中对应的数据相同时,月份加一,日期直接输出1号。现在直接给出代码:
public void getNextDate(){ if(month==12&&day==31)//判断是不是今年最后一天 System.out.printf("Next day is:%d-%d-%d",++year,1,1);//年份加一,然后输出一月一日 else{ if(day==mon_maxnum[month]){//判断是不是本月最后一天 System.out.printf("Next day is:%d-%d-%d",year,++month,1);年份不动,月份加一,输出一日 } else{ System.out.printf("Next day is:%d-%d-%d",year,month,++day);//直接输出下一天 } } } }
那么倒数第二题也就到此为止了。
现在来对最后一题进行分析,这题比较麻烦,我会对代码的每行都进行解释,这样更容易看懂。
如果说上一道题是前菜的话,那这道题就是真的满汉全席了,它的题干看似把主方法给了你,减轻了压力,其实丝毫不影响这道题的难度。那么我们首先从主方法开始分析吧。主方法如下:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int year = 0;//定义年 int month = 0;//定义月 int day = 0;//定义日 int choice = input.nextInt();//输入选择:1.求下n天 2.求前n天 3.求两个日期相差的天数
if (choice == 1) { // test getNextNDays method int m = 0;//定义要求接下来几天 year = Integer.parseInt(input.next());//得到年 month = Integer.parseInt(input.next());//得到月 day = Integer.parseInt(input.next());//得到日 DateUtil date = new DateUtil(year, month, day); if (!date.checkInputValidity()) {//如果判断为非法日期的话 System.out.println("Wrong Format");//输出 System.exit(0); } m = input.nextInt();//输入要求接下来几天 if (m < 0) {//m的范围 System.out.println("Wrong Format"); System.exit(0); } System.out.print(date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " next " + m + " days is:");//输出 System.out.println(date.getNextNDays(m).showDate()); } else if (choice == 2) { // test getPreviousNDays method int n = 0;//输入要求之前几天,接下来代码和上面基本相同,不再赘述。 year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); DateUtil date = new DateUtil(year, month, day); if (!date.checkInputValidity()) { System.out.println("Wrong Format"); System.exit(0); } n = input.nextInt(); if (n < 0) { System.out.println("Wrong Format"); System.exit(0); } System.out.print( date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " previous " + n + " days is:"); System.out.println(date.getPreviousNDays(n).showDate()); } else if (choice == 3) { //test getDaysofDates method year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); int anotherYear = Integer.parseInt(input.next());//输入要求的第二年的各项数据 int anotherMonth = Integer.parseInt(input.next()); int anotherDay = Integer.parseInt(input.next()); DateUtil fromDate = new DateUtil(year, month, day);//开始年份 DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay);//结束年份 if (fromDate.checkInputValidity() && toDate.checkInputValidity()) {//判断两个年份的输入是否合法 System.out.println("The days between " + fromDate.showDate() + " and " + toDate.showDate() + " are:" + fromDate.getDaysofDates(toDate)); } else { System.out.println("Wrong Format"); System.exit(0); } } else{ System.out.println("Wrong Format"); System.exit(0); } } }
ok,上述代码中不好理解的地方我都加了注释,就不再多赘述了,继续看题,我们自己还需编写一些方法使得这个代码可以正常运行,首先是要检测输入的年、月、日是否合法:
public boolean checkInputValidity(){//检测输入的年、月、日是否合法 if(isLeapYear(year)) mon_maxday[2]=29; if(year<1820||year>2020||month>12||month<1||day<1||day>mon_maxday[month]){//year∈[1820,2020] ,month∈[1,12] ,day∈[1,31]
接着是判断year是否为闰年,这些代码在上一题就写过一样的了,在这里不多解释了。
public boolean isLeapYear(int year){//判断year是否为闰年 if((year%100!=0&&year%4==0)||(year%400==0)){ mon_maxday[2]=29; yearDay++;
取得year-month-day的下n天日期就开始有些复杂了,因为你要看的是接下来不知道多少天,所以要考虑月份和年份边界的问题,之前判断的是否为闰年在这里就可以用上了,因为我们不仅要判断这一年是不是闰年,还得看下一年是不是闰年。
public DateUtil getNextNDays(int n){//取得year-month-day的下n天日期 while (n > 365) { if (this.isLeapYear(year) == true && month <= 2) {//判断下一年是不是闰年 if (month == 2 && day == 29) {//判断二月二十九号下一天为三月一号 month = 3; day = 1; } year++; //年份加一 n =n-366; } else if(this.isLeapYear(year+1) && month > 2)//是闰年但不是二月 { year++; n=n-366; } else { year++; n=n-365; } } for (int i = 0; i < n; i++) { day++; if(this.isLeapYear(year) == true && month == 2) {//是闰年二月 if(day > 29) { month++; day = 1; } } else if (day > mon_maxday[month]) {//所要加的日期大于这个月的日期 month++; day = 1; if (month > 12) { year++; month = 1; } } } return this; }
取得year-month-day的前n天日期和上面的后n天基本相仿,也是要判断前一年是不是闰年。
public DateUtil getPreviousNDays(int n){ DateUtil x = new DateUtil(this.year,this.month,this.day); for (int i = 0; i < n; i++) { if(isLeapYear(d.getYear()))//判断闰年 mon_maxday[2] = 29; else mon_maxday[2] = 28; x.day--; if(d.day == 0){//日期被减到0的时候月份减一 x.month--; if(d.month == 0){//月份被减到0的时候年份减一 x.year--; x.month = 12; } x.day = mon_maxday[d.month]; } } return d; }
再然后,我们实现第三个功能,也就是判断两个日期之间相隔天数的时候,需要判断两个日期谁在前谁在后,也就是比较当前日期与date的大小(先后),如下所示:
public boolean compareDates(DateUtil date){//比较大小 if(this.year > date.getYear())//年份比较大小 return true; else if(this.year == date.year && this.month > date.month)//年份相同的情况下比较月份 return true; else { if(this.month>date.month) return true; else if(this.month<date.month) return false; else{//月份也相同的情况下比较日期 if(this.day>date.day) return true; else if(this.day<date.day) return false; } } return true; }
接下来的这个部分比较简单,就是判断两个日期是否相等,话不多说直接放代码:
public boolean equalTwoDates(DateUtil date){ return this.year == date.year && this.month == date.month && this.day == date.day;//判断年月日是否都相等 }
然后是我认为的这题的难点之一了,求当前日期与date之间相差的天数,不仅要判断闰年与否,也要用到上面判断两个日期谁大谁小谁先谁后,这里让我卡了快一个半小时,主要还是思路跟不上,后来写出来了又一直报错,调试了很久才改好。
public int getDaysofDates(DateUtil date){ int answer = 0;//相隔的日期 if(this.equalTwoDates(date))//判断两个日期是否相等 return 0; if(this.compareDates(date)){//比较两个日期的大小 while(true){//当确定大小之后,前面的年份大于后面的年份 if(isLeapYear(date.year))//判断是否为闰年 mon_maxday[2] = 29;//是闰年的话二月有二十九天 else mon_maxday[2] = 28;//非闰年 date.day++; if(date.day == mon_maxday[date.month]+1){//如果和这个月一共多少天相等 date.month++; date.day = 1; } if(date.month == 13){//月份等于十三的时候,年份就要进一,等于是十二进制 date.year++; date.month=1; } if(date.year == year && date.month == month && date.day == day){//直到两个减到同一天时停止 break; } answer++; } }else{//当后面的年份大于前面年份的时候 while(true){ if(isLeapYear(year))//判断闰年,同上 mon_maxday[2] = 29; else mon_maxday[2] = 28; day++; if(day == mon_maxday[month] + 1){ month++; day = 1; } if(month == 13){ year++; month = 1; } if(date.year == year && date.month == month && date.day == day){ break;
最后就是返回了,这里比较简单,这道难题也就到此为止了。
public String showDate(){ return this.year + "-" + this.month + "-" + this.day; } }
3.踩坑心得
毕竟是第一次做Java的作业,碰到的坑还是比较多的,接下来一一梳理一下。
首先是作业一,因为当时没仔细看上面的公告栏,导致我主方法的命名没用main,在pta上面一直跑不了,后来又看了一遍要求才发现这个问题。而且,因为不熟悉这门语言,导致我有的时候会忘记定义就直接使用这个变量,或者是不在这个类里面定义的就拿来用,导致了报错。另外还有一些符号忘记输入之类的错就不拿出来说了。如下图,第一个是没用定义,第二个是忘记输入“;”,第三个是忘记" } ".
然后是作业二,这里面需要我自己定义的数据就多了起来,使得我有的时候会忘记自己上面给这个变量起的什么名字,或者打的时候少了一个字母导致没法识别。这个问题在写那个三角形的时候就体现的很明显了,我输错了几个字母,导致和pta给的结果不同,测试点一直过不了。下图是输错字母导致和上面定义的不同的情况。
最后是作业三,这里我虽然也碰到了上面两种情况,但更多的是思路不好想或者数据不好处理。同时,有的时候,如果不用this.来指向所需要的类,程序就没办法正确运行,即使能跑也不能输出正确的答案。而在变量过多的时候也不能搞混自己定义的是哪个变量。
4.改进建议
要说优化,那就不得不提我的一个坏习惯了,一般导入包的时候也不管下面会用到啥,反正就直接import java.util.*;了,习惯用隐式导入虽然现在看不出什么麻烦的地方,但是从编译的角度来看,后面进行更复杂的编译的时候,直接显式导入显然要比隐式导入快,所以接下来还是克服懒惰继续显式导入吧。另外,在输出的时候是使用print还是println亦或者是printf也会让我犹豫,虽然大部分时候三个都试一试就知道该用哪个了,但终究还是有些耗时的,这也是该改进的地方。然后什么时候该用this.对我来说也是一个难点,甚至有的时候直到报错了或者结果不正确了我才意识到该用this.来指向所需的类。
5.总结
ok,那么这次对前三次pta作业的分析也就到此为止了,我来进行一下总结。
首先,先说说我得到了什么好处。在这三次写作业的过程中,我毫无疑问的更加熟悉Java基础语法的运用了,同时也对一些题目的解法有了自己的思路。而在面对稍难一些的题目时,虽然也会觉得很难入手,但终究还是能有一点思路,从而慢慢调试代码使其可以运行。
不足的地方也有很多,首先,如上面所说,我仍旧对Java中复杂的语句不怎么熟悉,这也导致了我在难题面前会停滞不前,再者,即使我有了思路,以我目前所掌握的语法有时并不足以让我完成这些代码,甚至别人或许直接用相关的类或者方法很快的解开了题目,我却因为对这些类的不熟悉导致需要编写更多更复杂的代码才能完成要求,甚至有可能就在这卡住了没办法继续写程序。要想克服这些困难,唯一的办法就是接下来更深入的去学习Java这门语言,此外别无他法。
总的来说的话,这三次作业对我的基础语法有了练习和提升,但我本人对更精深的知识还是了解甚少,以至于编写代码时还是会有无力感,得靠接下来更多的练习来学习与熟悉这门语言了。
标签:总结,int,System,PTA,month,实验,year,date,day From: https://www.cnblogs.com/mlz1106/p/17256117.html