PTA作业1-3阶段性总结(1)
前言
从这学期开始,我学习了面向对象编程,也就是Java编程语言。相对于面向过程编程的C语言,Java有着面向对象的明显特点,刚开始学习时确实让人感到困惑和难以接受,但Java同时也摒弃了C语言的一些缺点并完善了C语言的一些不足,又因为有着上学期的学习经验,所以基础入门还是比较快的。
说回这三次的PTA作业,第一次作业个人主要是对基础编程能力的基础性考察,考察了对于Java编程基础结构的认知,方法的基本构建,对于简单输入输出语句结构的理解和运用,简单循环的基础运用以及选择语句的基本运用等,可能是为了综合考察所以题量较大,个人认为其中部分题目难度偏难。
第二次作业主要考察的是对类这一重要概念的基本认识和对代码细微之处(如数据输出类型和小数位数)的关注程度,这次作业的难度居中,部分题目虽然困难但并没到一点头绪都没有的程度。
第三次作业个人认为才是真正认识到了的Java的特性之一:封装性。本次作业主要考察的是简单的代码迭代和Java所特有的封装性(封装性体现了Java面向对象编程的特点)。这次作业前两题难度适中,后两题难度较之前相比有着很大的提升,主要还是因为引入了封装性以及类的概念。题目中也显示出了基本的代码迭代思想,通过不断优化算法使程序更加完善并能实现更多功能的同时还要有着便于修改和后续优化的特点。这也表现出了学习面向对象程序设计课程的目的并不是简单的学会Java语言编程,更要学会程序的结构设计和如何优化程序。
重点题目分析
1.题目集2 7-9 求下一天
本次是题目集中第一次出现日期类题目,按照题目中的具体要求编写方法实现目标,在本题中,本人对于不同的方法采取了有着细微差别的不同思路来解决问题。大体上都是通过不断排除特殊情况来实现对数据范围的限制,最后根据所得到的数据范围来分别实现功能。如在判断闰年(isLeapYear方法)的基础上,再进一步排除跨年和跨月的特殊情况来得到对于非特殊情况的统一运算方法,接着再补充对于特殊情况的运算方法。
类图:

本题目代码的不足之处是:
在编写代码时对于逻辑的判断不够清晰,会导致对于特殊情况的遗漏或者错误判断。同时本次代码也表现出了“垃圾代码“的特性:无用语句太多、部分语句可能会导致歧义或者错误。从现在看来,这次的代码纯粹是为了完成任务而一味的堆叠限制语句和方法,根本不符合Java编程的基础思想,不具有易维护性和迭代性。
2.题目集3 7-3 定义日期类
个人认为本题是对题目集2中7-9的进一步优化,题目要求从编写一个完整的程序变为了定义日期类。这意味着本次编写出来的代码是可以作为一个类插入其他的程序中了。程序变得更加灵活多变,也更利于优化和维护了。但与此同时带来的是本次题目对代码的要求会更严苛,代码逻辑也要更合理,更简洁。在本次编程过程中,本人选择了和上次题目相类似的逻辑思想,但同时也对其进行了优化,将判断条件进一步简洁化和合理化,一定程度上也增强了代码的可读性。并且本题有着很大的意义,是后续日期类题目的基础,后续日期类题目都是以本体为基础架构搭建的。
类图:

本题目代码的不足之处是:对类的结构认识不够清晰,并且由于是第一次接触封装性的原因,对于私有类型数据的处理也经常出错。经常遗漏对于私有类型数据的Getter和Setter方法的编写,导致程序无法正常运行,而且代码没有遵从单一职责原则,类中的方法杂乱且涉及到了多个功能,从长远看来这是不利于后期代码的进一步修改的。
3.题目集3 7-4 日期类设计
本体是上一题的迭代题目,为了降低难度,题目给出了main类的全部代码和架构,后续要做的就补充日期类中的具体方法和属性,由于测试用例的时间跨度太长,所以方法需要有着很高的稳定性和正确率,在重复多次运行和大量计算中也不能出现错误,这也是本题最大的困难所在。
在编写前期,曾试图通过不断减少所求的天数(所输入的整形属性 n)来将日期的变化限制在一年之内,再继续其他运算。但最后的代码结果出现错误,还出现了运行超时的现象,个人认为主要的错误原因是判断该年份是否是闰年的方法在过大的时间跨度下无法准确判断,进而无法对所剩余天数(整型属性 n)进行正确的运算,所以最后的程序出现了运算结果错误以及循环无法终止的错误。在后期通过思考决定将之前的思路全部推倒重来 ,试着将问题简化为对之前所写的日期类代码进行简单的更改和重复,判断条件也从对于所输入整型n的限制变为了判断两个日期是否相等,直到两个日期完全相等再返回所得结果,最后的运行结果证明这种方案是可行并且简洁的,截至目前为止个人还没有关于如何继续完善本次代码的任何想法。
类图:

本题目暴露出来的不足之处是:在思考代码的基础架构时往往将问题复杂化,导致代码的复杂度过高,此时代码若出现错误,代码就会变得难以修改更正,这次编程对本人来说意义重大,让我认识到了代码的基础设计的重要性,错误的思路反而会导致问题的复杂度进一步加深。
踩坑心得
在完成这几次作业的过程中,我主要有以下几点心得:
- 编写代码时一定要遵循规范编写代码,严格限制代码格式规范
在这几次的编程过程中,最多出现的错误还是格式错误,如大括号未对齐,类的名称未大写,语句末端未附上分号,括号未对齐导致其他类被错误得算入主类内等。不规范的代码也增加了后期更改维护的难度



2.在编写代码时要谨慎使用循环和选择语句
在这几次题目集中,出现过不少运行超时的错误,这都是因为过于滥用循环语句导致的,限制条件的错误会导致代码陷入无限的循环或者根本不执行循环中的语句。选择也是同理,错误的选择语句会导致代码的检查变得困难,因为在检查时会分不清代码究竟进入了哪个循环分支。所以要慎用循环和选择,这能一定程度上提高代码的正确率和可读性。
3.对于代码的基础架构要认真思考,尽量想出最简单的解决方案
在编写题目集3 7-4的经历中,我认识到了思路的正确性是完全可以决定问题的难度的。不正确的思路会误导你向着错误的方向前进。所以在此之后,每次编写程序我都会先构想出来一个大概思路,并不断完善思路,再进一步构思来验证思路的可行性和实现该思路的困难程度,如果出现不可行的部分或实现太过困难,我就会重新构思全新的思路
下面的源代码能体现思路的重要性:
更改思路前:
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(); 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) { 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); } } } class DateUtil{ private int year; private int month; private int day; int []mon_maxnum=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; DateUtil(int year,int month,int day){ this.year=year; this.month=month; this.day=day; } public int getYear() { return this.year; } public void setYear(int year){ this.year=year; } public int getMonth() { return this.month; } public void setMonth(int month){ this.month=month; } public int getDay() { return this.day; } public void setDay(int day){ this.day=day; } public boolean isLeapYear(int year){ boolean isLeapYear=(year%4==0&&year%100!=0)||year%400==0; return isLeapYear; } public boolean checkInputValidity(){ boolean checkInputValidity=year<=2020&&year>=1820&&month>=1&&month<=12&&day<=mon_maxnum[month]&&day>=1; if(month==2&&isLeapYear(year)) { checkInputValidity=year<=2020&&year>=1820&&month>=1&&month<=12&&day<=29&&day>=1; } return checkInputValidity; } public DateUtil getNextNDays(int n){ while(n>365){ if(this.isLeapYear(year)&&month<= 2){ if(month==2&&day==29){ day = 1; month = 3; } year++; n-=366; }else if(this.isLeapYear(year+1)&&month>12){ year++; n-=366; }else { year++; n-=365; } }while(n<365&&n>mon_maxnum[month]){ if(this.isLeapYear(year)&&month==2){ n=n-(29-day); month=3; day=1; }else if(month==12){ n=n-(mon_maxnum[month]-day); year=year+1; month=1; day=1; }else{ n=n-(mon_maxnum[month]-day); day=1; month=month+1; } } while(n<this.mon_maxnum[month]){ for (int i = 0; i < n; i++){ day=day++; if(this.isLeapYear(year)&&month==2){ if(n>=29) month=month+1; day=1; }else if(day>=mon_maxnum[month]){ month++; day=1; if(month>12){ month=1; year=year+1; } } } } return this; } public DateUtil getPreviousNDays(int n){ while(n>365){ if(this.isLeapYear(year)&&month>2){ year=year-1; n=n-366; }else if(this.isLeapYear(year-1)&&month<=2){ year=year-1; n=n-366; }else { year=year-1; n=n-365; } }while(n<365&&n>mon_maxnum[month]){ if(this.isLeapYear(year)&&month==3){ n=n-day; month=2; day=29; }else if(month==1){ n=n-day; year=year-1; month=12; day=31; }else{ n=n-day; month=month+1; day=mon_maxnum[month]; } } while(n<this.mon_maxnum[month]){ for (int i = 0; i < n; i++){ day=day--; if(this.isLeapYear(year)&&month==3){ if(n==1) month=month-1; day=29; }else if(day==1){ month--; day=mon_maxnum[month]; if(month<1){ month=12; day=31; year=year-1; } } } } return this; } public boolean compareDates(DateUtil date){ boolean compareDates=true; if(this.year>date.getYear()) compareDates=true; else if(this.year==date.getYear()&&this.month>date.getMonth()) compareDates=true; else if(this.year==date.getYear()&&this.month==date.getMonth()&&this.day==date.getDay()) compareDates=true; else compareDates=false; return compareDates; } public int getDaysofDates(DateUtil date){ int n=0; if(!this.equalTwoDates(date)){ if(compareDates(date)){ while(this.year!=date.getYear()){ if(date.isLeapYear(date.getYear())&&date.getMonth()<=2){ date.setYear(date.getYear()+1); n+=366; }else if(date.isLeapYear(date.getYear()+1)&&date.getMonth()>2){ date.setYear(date.getYear()+1); n+=366; }else{ date.setYear(date.getYear()+1); n+=365; } } }else { while(this.year!=date.getYear()){ if(date.isLeapYear(date.getYear())&&date.getMonth()<=2){ this.year+=1; n+=366; }else if(date.isLeapYear(date.getYear()+1)&&date.getMonth()>2){ this.year+=1; n+=366; }else{ this.year+=1; n+=365; } } } if(this.isLeapYear(year)) mon_maxnum[2]=29; while(this.month!=date.getMonth()){ if(this.month<date.getMonth()){ this.month=this.month+1; n+=mon_maxnum[this.month-1]; }else if(this.month>date.getMonth()){ date.setMonth(date.getMonth()+1); n+=mon_maxnum[date.getMonth()-1]; } } while(this.day!=date.getDay()){ if(this.day<date.getDay()){ this.day+=1; n+=1; } if(this.day>date.getDay()){ date.setDay(date.getDay()+1); n+=1; } } } return n+1; } public boolean equalTwoDates(DateUtil date){ boolean equalTwoDates=false; if(this.year==date.getYear()&&this.month==date.getMonth()&&this.day==date.getDay()) equalTwoDates=true; return equalTwoDates; } public String showDate(){ String showDate=new String(""); showDate=year+"-"+month+"-" +day; return showDate; } }
运行结果:

重新构思后
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(); 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) { 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); } } } class DateUtil{ private int year; private int month; private int day; int []mon_maxnum=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; DateUtil(int year,int month,int day){ this.year=year; this.month=month; this.day=day; } public int getYear() { return this.year; } public void setYear(int year){ this.year=year; } public int getMonth() { return this.month; } public void setMonth(int month){ this.month=month; } public int getDay() { return this.day; } public void setDay(int day){ this.day=day; } public boolean isLeapYear(int year){//判断是否是闰年 boolean isLeapYear=(year%4==0&&year%100!=0)||year%400==0; return isLeapYear; } public boolean checkInputValidity(){//检测输入的年、月、日是否合法 boolean checkInputValidity=year<=2020&&year>=1820&&month>=1&&month<=12&&day<=mon_maxnum[month]&&day>=1; if(month==2&&isLeapYear(year)) { checkInputValidity=year<=2020&&year>=1820&&month>=1&&month<=12&&day<=29&&day>=1; } return checkInputValidity; } public DateUtil getNextNDays(int n){//取得year-month-day的下n天日期 int i=0; while(i!=n){ if(this.isLeapYear(year)) mon_maxnum[2]=29; else mon_maxnum[2]=28; if(month==12) { if(day==mon_maxnum[month]){ year+=1; month=1; day=1;} else day++; i++; }else { if(day==mon_maxnum[month]) { month+=1; day=1; }else day++; i++; } } return this; } public DateUtil getPreviousNDays(int n){//取得year-month-day的前n天日期 int i=0; while(i!=n){ if(this.isLeapYear(year)) mon_maxnum[2]=29; else mon_maxnum[2]=28; if(month==1) { if(day==1){ year-=1; month=12; day=31;} else day--; i++; }else { if(day==1) { month-=1; day=mon_maxnum[month]; }else day--; i++; } } return this; } public boolean compareDates(DateUtil date){//比较当前日期与date的大小(先后) boolean compareDates=true; if(this.year>date.getYear()) compareDates=true; else if(this.year==date.getYear()&&this.month>date.getMonth()) compareDates=true; else if(this.year==date.getYear()&&this.month==date.getMonth()&&this.day==date.getDay()) compareDates=true; else compareDates=false; return compareDates; } public int getDaysofDates(DateUtil date){//求当前日期与date之间相差的天数 int n=0; if(compareDates(date)) { while(!this.equalTwoDates(date)){ date.getNextNDays(1); n++; } }else { while(!this.equalTwoDates(date)){ getNextNDays(1); n++; } } return n; } public boolean equalTwoDates(DateUtil date){//判断两个日期是否相等 boolean equalTwoDates=false; if(this.year==date.getYear()&&this.month==date.getMonth()&&this.day==date.getDay()) equalTwoDates=true; return equalTwoDates; } public String showDate(){//以“year-month-day”格式返回日期值 String showDate=new String(""); showDate=year+"-"+month+"-" +day; return showDate; } }
新代码的运行结果:

总结
在近期学习Java的过程中我得出这样的结论:学习中,要养成良好的习惯(写括号时要成对,字母大小写要区分,单词拼写要准确)。在学习的过程中,最好不是仅仅停留在java表层,对于每个代码都要用心琢磨,调试,完善。在学习的过程中一定要动手做、试着写代码,而不是抱一本书看看就行。很多东西和体会必须自己动手才能真正属于自己。在 Java 的学习过程中,可能会遇到形形色色的问题不容易解决,应多去专业论坛了解相关的知识,书本上的知识有限。要会从网上搜索有用的信息 加以整理,促进学习的深入和知识水平的提高。
浙公网安备 33010602011771号