OO前三次作业总结
前言:
题目集1-3共有24道题目,主要用到的知识点是判断和循环还有数组,在第一次题目集的第5、6、8题里还用到了String类的相关知识点。第三次作业开始主要针对面向对象,知识点有如何设计对象并使用,封装,this关键词,构造方法等。我认为题目集2的难度最低,题目集1中用到String类的知识点的那几题对我来说有点麻烦,因为起初并不知道String类里会有这样快捷的方法。题目集3的话难度最大的就是最后一题了,要做到一次性通过所有的测试点那可不是一件容易的事情。
设计与分析:
题目集3的7-3是定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔,当输入数据非法及输入日期不存在时,输出“Date Format is Wrong”;当输入日期合法,输出下一天,格式如下:Next day is:年-月-日
下面是我的源码:
import java.util.Scanner; class Date{ //日期类 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}; //每月最大天数 public Date(){ } public Date(int year,int month,int day){ this.year=year; this.month =month; this.day =day; } 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%4==0&&year%100!=0||year%400==0){ return true; } else return false; } public boolean checkInputValidity(){ //判断输入是否符合规则 if(year>2000||year<1900||month>12||month<1||day>31||day<1) return false; if(isLeapYear(year)){ mon_maxnum[2]=29; //这里首先判断输入的年份是否是闰年,闰年的二月份有29天 if(day>mon_maxnum[month]){ return false ; } else return true; } else{ if(day>mon_maxnum[month]) return false; else return true ; } } public void getNextDate(){ //求下一天分为三种情况判断 //输入日期为该年的最后一天 if(month==12&&day==mon_maxnum[month]){ System.out.print("Next day is:"+(year+1)+"-1-1"); } //月份不为12月且日期为该月最大值 else if(month!=12&&day==mon_maxnum[month]){ System.out.print("Next day is:"+year+"-"+(month+1)+"-"+1); } //剩下的就是任意月份且日期不为该月天数的最大值 else{ System.out.print("Next day is:"+year+"-"+month+"-"+(day+1)); } } } public class Main { public static void main(String[] args) { Scanner in=new Scanner(System.in); Date data =new Date(in.nextInt() , in.nextInt(), in.nextInt()); if(data.checkInputValidity()==true){ data.getNextDate() ; } else{ System.out.print("Date Format is Wrong"); } } }
主要思路就是把输入的日期分为三种情况,12月的最后一天、除12月之外的月份的最后一天、任何月份的除最后一天之外的天数。根据这三种情况分别写出下一天的情况。这道题并不难,主要就是逻辑思路要清晰,写日期题目可能会复杂的点就在于闰年平年2月份天数不一样,这也可能是让编译结果答案错误的点。
上图是这道题在SourceMonitor上的生成报表内容,整体复杂度低,代码量少。
题目集3的7-4是参考题目3和日期相关的程序,设计一个类DateUtil,该类有三个私有属性year、month、day(均为整型数),其中,year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] ,并给定需要编写的方法:
public boolean checkInputValidity();//检测输入的年、月、日是否合法
public boolean isLeapYear(int year);//判断year是否为闰年
public DateUtil getNextNDays(int n);//取得year-month-day的下n天日期
public DateUtil getPreviousNDays(int n);//取得year-month-day的前n天日期
public boolean compareDates(DateUtil date);//比较当前日期与date的大小(先后)
public boolean equalTwoDates(DateUtil date);//判断两个日期是否相等
public int getDaysofDates(DateUtil date);//求当前日期与date之间相差的天数
public String showDate();//以“year-month-day”格式返回日期值
应用程序共测试三个功能:
-
求下n天
-
求前n天
-
求两个日期相差的天数
然后程序的主方法是直接给出来的,我的源码比较长,有近两百五十行吧,其中有些方法的思路是和题目7-3一样的,我这里就只分析DateUtil里的几个方法。
public DateUtil getNextNDays(int n){ for(int i=0;i<n;i++){ if(isLeapYear(year)) //判断闰年 mon_maxnum[2]=29; else mon_maxnum[2]=28; if(day==mon_maxnum[month]){ day=1; if(month==12){ month=1; year+=1; } else month+=1; } else day+=1; } DateUtil printdateUtil =new DateUtil(year ,month ,day ) ;//new一个DateUtill return printdateUtil; //返回循环后所得的日期 }
首先是求下n天,主要思路与7-3相同,分三种情况,通过循环从而得出下n天的日期
public DateUtil getPreviousNDays(int n){ for(int i=n;i>0;i--){ if(isLeapYear(year)) //判断闰年 mon_maxnum[2]=29; else mon_maxnum[2]=28; //同样分三种情况 //当循环到的日期为1月1日时 if(day==1){ if(month==1){ year-=1; month=12; day=mon_maxnum[month]; } // 当循环月份不为1,但日期仍为1日时 else{ month-=1; day=mon_maxnum[month]; } } //当循环月份为任意月份但日期不为1日时 else day-=1; } DateUtil printdateUtil =new DateUtil(year ,month ,day ) ; return printdateUtil; }
求前n天日期的思路与求下n天思路一样,只不过是倒着来循环
//比较两个日期的先后 public boolean compareDates(DateUtil date) { int key=1; if(!equalTwoDates(date)){ if(year==date.year){ if(month==date.month){ if(day>date.day) key=1; else key=0; } else if(month>date.month ) key=1; else key=0; } else if(year>date.year) key=1; else key=0; } if(key==1) return true; else return false; } //比较两个日期是否相等 public boolean equalTwoDates(DateUtil date){ if(year==date.year &&month==date.month &&day==date.day) return true; else return false ; } //求两个日期之间相差的天数 public int getDaysofDates(DateUtil date){ int count1=0; int count2=0; //分别计算出输入的两个日期与1820-1-1相差的天数count1和count2 for(int i=1820;i<this.year;i++){ if(isLeapYear(i)) //首先判断闰年 count1+=366; else count1+=365; } for(int j=1;j<this.month ;j++){ if(isLeapYear(this.year)){ mon_maxnum[2]=29; } else mon_maxnum[2]=28; count1+=mon_maxnum[j]; } count1+=this.day ; //同理可得 for(int i=1820;i<date.year;i++){ if(isLeapYear(i)) count2+=366; else count2+=365; } for(int j=1;j<date.month ;j++){ if(isLeapYear(date.year)){ mon_maxnum[2]=29; } else mon_maxnum[2]=28; count2+=mon_maxnum[j]; } count2+=date.day ; //返回count1和count2差的绝对值,即两日期间间隔天数 return Math.abs(count1-count2) ; }
求输入两个日期之前相差的天数,主要思路与前面的方法没有什么差别,只不过是通过间接的方法,分别求出输入的两个日期与1820-1-1间隔的天数,再求两个间隔天数差的绝对值,从而得到输入的两个日期间间隔的天数。
这道题与上一道题相比更加复杂,但思路都是一样的。不管是求前n天还是下n天又或者是两个日期间间隔的天数都是通过一天一天循环找到答案,思路简单逻辑清晰并且容易实现
踩坑心得:
首先说题目集1里的一道判断判断三角形类型的题目,在判断等腰直角三角形的时候出现了错误,我一开始是这样写的:
if(a==b||b==c||c==a){ if((a*a)==(b*b+c*c)||(b*b)==(a*a+c*c)||(c*c)==(a*a+b*b)){ System.out.print("Isosceles right-angled triangle"); } }
然后我的a,b,c都是double类型的,这样编译结果就是错误的
但是如果我把类型强制转换
if(a==b||b==c||c==a){ if((float)(a*a)==(float)(b*b+c*c)||(float)(b*b)==(float)(a*a+c*c)||(float)(c*c)==(float)(a*a+b*b)){ System.out.print("Isosceles right-angled triangle"); }
结果就变成正确的了
主要就是double和float的精度问题,这玩意我觉得挺那啥的
然后对于题目集3分析的两道题对于我来说比较困难的是7-4这一题,一开始看到题目的时候脑袋有点大,我不知道该怎么用编程语言去实现它,然后我就试着把它当做一道数学题,想知道如果是我去解决我会怎么去做,然后按我自己计算时的方法去编写代码,提交后基本上每个方法都有一个测试点没过。事实上我这样的做题思路是不可取的,编程之所以是编程,是因为那些难题都是由我们所写的语句通过计算机来实现的,换言之,我们只是提供方法,而剩下的是计算机来解决的。那么我们应该站在计算机的思路上去思考问题。计算机擅长运算,计算机不怕麻烦,所以用一遍一遍的循环找到答案是一个方法简单思路清晰的事情。不管是求前n天还是下n天,又或者是两个日期之间相差的天数,所用方法的内核都是一样的。我应该站在计算机的角度去找解决的办法。
同样是最后一道题,有同学pta提交的时候出现了内存超限的问题,起初我听到内存超限并不知道这具体是什么错误,然后上网搜了一下说是出现内存超限的问题的话需要对程序的空间复杂度进行优化,一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。存储算法本身所占用的存储空间:与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。算法的输入输出数据所占用的存储空间:是由要解决的问题(问题规模)决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。算法在运行过程中临时占用的存储空间:随算法的不同而异。(来自百度百科)解决办法就是更改算法以使用更小的内存。
改进建议:
命名风格和代码格式不规范,应多参照java开发手册。代码可读性低,应在适当位置注释。一个方法一个功能,便于调用。基础知识还是不够扎实,要多学多用。那道题目先构思再下手,防止不必要的删删改改。最重要的是逻辑一定要清晰,不要写着写着就不知道自己在写什么了。还有就是效率还是太低了,速度慢正确率一般。不过我觉得和同学交流然后比较自己和他们解决问题的办法从中找到更高效的方法是一件很有意思的事情吧。有时候看了别人的方法会有一种豁然开朗的感觉然后反思自己为什么没想到这样去做,那种感觉跟考完试看到答案感叹原来这题这么简单一样。
总结:
String类的常用方法有:
1.求字符串的长度 public in length()
2.求字符串某一位置的字符 public char charAt(int index) 其中第一个字符索引是0,最后一个是length-1
3.提取子串 public String substring(int beginIndex) 从beginIndex位置开始取剩余字符返回,如果是 public String substring(int beginIndex,int endIndex) 那就是从beginIndex位置起,从当前字符串取到endIndex-1位置的字符
定义类时:类名首字母建议大写,英文,有意义,满足驼峰模式,不能用关键字,满足标志符规定。一个代码文件中可以定义多个类,但只能一个类是public修饰的,public修饰的类名必须是java代码的文件名称。
封装:封装目的是正确设计对象的属性和方法,原则上对象代表什么就得封装对应的数据,并且提供数据对应的行为,封装让编程变得更简单,有什么事,找对象,调方法就行。
一个标准的javabean类:
1.类名需要见名知意
2.成员变量使用private修饰
3.提供至少两个构造方法(无参构造方法和带全部参数的构造方法)
4.成员方法(提供每一个成员变量对应的setXxx()/getXxx(),如果还有其他行为也需要写上)