OOP1~3训练心得体会
面向对象程序设计
前言
1.对于第一次java练习题目集,主要考查的是一些基础语法的使用,例如赋值,标识符,字符串,整形数字,数组的使用,其中也包含了一些对于后面好的工具进行使用的学习,例如String builder,String buffer中的方法的调用,使用时可以直接查找对应的字符进行删改。总体程度上体量较大,难度除第10题以外需要时间较少,第11题主要考察对于题目中有效信息进行筛选的能力,包括对于迷惑性条件的排查,筛选出实用型信息进行求解。
除以上内容以外因为是第一次学习java,相较于上一学期学习的c语言,两者格式上相差较大,语法相近,导致刚一上手有一种既熟悉又陌生的感觉,刚开始的学习如同无头苍蝇一般到处乱撞,导致在最开始的几天内难有进展,经过第一周的学习后更加认识到系统化学习的重要性,主要原因在于书上内容是相互串联在一块的,可以在学习新内容的同时联系到前面的内容,做到巩固的效果。
2.相较于第一次的练习题集相比,,题量减少,内容没有太大变化,除第7题中的二进制转换涉及到新的知识区以外,相较内容区分度不高。在通过题目7的给出条件的提示帮助后,调用二进制数转化的方法,完成题目的解答后,再在课本上找到其更多种方法还包括10,16进制的转换方法,可以说学习到的内容更加拓宽了。这次的题目集除了涉及以上转换,还包含了对于int,float类型的强制转化,这也是在了解到了数字型强制转换之间涉及到的精度损失问题,其中第6,8题就涉及到精度损失的问题,第七题涉及字符串的选择及判断,第9题的求下一天题也同时是第三次题目的前身,主要是做一个对于日期基础的判断以及下一天的求法。
3.相较于前两次题目集,第三次题目集更加符合程序面向对象这一概念,涉及到了类的定义,空方法的构造,带参构造创建对象,还涉及变量的作用域,类的调用(带参和无参),了解到了java在不创建空方法的前提下会自动创建。以及对象的存储位置,更是了解到了java中的引用本质上是指针,用于保存变量所在的地址、其中1,2两题比较简单,3,4题难度较大,以第四题为例,要在下手前先考虑到他的构造以及整体的一个框架后再下手,否则很容易造成边写又发现思路不对从而重写的发生,犯错虽然是一件好事,但犯的太多谁也受不了。所以,提前构建好思路很重要。
4.总共前三次的练习中,反应的问题主要是语法问题和逻辑问题。
设计与分析
训练题集(1)7-11
题目要求:输入定积分的上下限以及分割数。求:定积分的值并保留四位有效数字。
源码:
import java.util.*; public class Main{ public static void main(String[] args){ Scanner input=new Scanner(System.in); double num1=input.nextDouble(); double num2=input.nextDouble(); double num3=input.nextDouble(); double area=0; double tx=0; for(double i=0;i<num3;i++){ double a=Math.pow(num1+(num2-num1)/num3*i,2); double b=Math.pow(num1+(num2-num1)/num3*(i+1),2); tx=(a+b)/2.0000*(num2-num1)/num3; area+=tx; } System.out.printf("%.4f",area); } }
输出结果:
题目分析:
本题主要考察对于题目中有效信息的提取,在第一次做这道题是显示答案与要求具有一定的相差,在经过对题目的重新审题后发现原因是要对梯形进行计算而非题目中所描述的矩形,导致计算结果具有误差。之后便是使用math.pow方法进行平方计算(可以联想到和之前c语言学习中的调用math库),之后再格式化输出保留四位有效数字。求解成功。
训练题集(2)7-6
题目要求:输入两个浮点数,以空格为间隙,第一个是n,第二个是lastguess,输出其算术平方根结果,若输入非法,则输出Wrong Format
源码:
import java.util.*; public class Main{ public static void main(String[] args){ Scanner input = new Scanner(System.in); float n=input.nextFloat(); float lastguess=input.nextFloat(); float count=0; int flag=0; if(n<0||lastguess<=0){ flag=1; System.out.print("Wrong Format"); } while(flag==0){ count=(lastguess+n/lastguess)/2; if(Math.abs(count-lastguess)<0.00001){ //lastguess=count; break; } lastguess=count; } if(flag==0){ System.out.print((float)lastguess); } } }
输出结果:
题目分析:
这道题给出的信息很明确,包括条件,所以对于求解并非难事,但实际上因为浮点数之间的计算会存在精度损失,所以不能直接将两经过计算后的浮点数直接进行计算,而是计算其差值小于一定数值使其达到近似相等的效果才可得出原题结果,否则计算结果会更加精确一位,这显然违背了我们最初的意愿,还有一点就是要对计算结果进行float强制转化,否则结果依旧错误。
训练集题目(2)7-8
题目要求:输入三角形的三条边(实型数),判断其三角形的类型。(若给予数据不符合要求,则输出错误)。
源码:
import java.util.*; public class Main{ public static void main(String[] args){ Scanner input=new Scanner(System.in); double r1=input.nextDouble(); double r2=input.nextDouble(); double r3=input.nextDouble(); int flag=0; while(true){ while(true){ if(r1<1||r1>200||r2<1||r2>200||r3<1||r3>200){ flag=1; break; } if(!(r1+r3>r2&&r1+r2>r3&&r2+r3>r1)){ flag=2; break; }if(r1==r2&&r2==r3){ flag=3; break; }if(r1==r2&&r1*r1+r2*r2-r3*r3<0.1){ flag=4; break; }if(r1==r3&&r1*r1+r3*r3-r2*r2<0.1){ flag=4; break; }if(r3==r2&&r3*r3+r2*r2-r1*r1<0.1){ flag=4; break; }if(r1==r2||r2==r3||r3==r1){ flag=5; break; }if(r1*r1+r2*r2-r3*r3<0.1||r1*r1+r3*r3-r2*r2<0.1||r3*r3+r2*r2-r1*r1<0.1){ flag=6; break; } flag=7; break; } if(flag==1){ System.out.print("Wrong Format"); break; } if(flag==2){ System.out.print("Not a triangle"); break; } if(flag==3){ System.out.print("Equilateral triangle"); break; } if(flag==4){ System.out.print("Isosceles right-angled triangle"); break; } if(flag==5){ System.out.print("Isosceles triangle"); break; }if(flag==6){ System.out.print("Right-angled triangle"); break; }if(flag==7){ System.out.print("General triangle"); break; } } } }
输出结果:
题目分析:这道题目主要考察的是对于三角形判断各种类型先后顺序的考察,若顺序错误则会导致结果错误,还有就是类似于上一题的浮点数之间计算的精度损失问题要进行考虑。
训练题集(3)7-3
题目要求:输入一个日期时间,求其下一天的天数(若数据非法则直接输出wrong format)。
源码:
import java.util.*; public class Main{ public static void main(String[] args){ Scanner input = new Scanner(System.in); int Year=input.nextInt(); int Month=input.nextInt(); int Day=input.nextInt(); // int[]Sta=new int[]{31,28,31,30,31,30,31,31,30,31,30,31}; Date date=new Date(Year,Month,Day); if( date.checkinputValidity(Year,Month,Day)){ date.getNextDate(Year,Month,Day); }else{ System.out.println("Date Format is Wrong"); } } } class Date{ private int year; private int month; private int day; int[]Sta=new int[]{31,28,31,30,31,30,31,31,30,31,30,31}; Date(){ year=0; month=0; day=0; } public Date(int newYear,int newMonth,int newDay){ month=newMonth; year=newYear; day=newDay; } public int getYear(){ return year; } public void setYear(int newYear){ year=newYear; } public int getMonth(){ return month; } public void setMonth(int newMonth){ month=newMonth; } public int getDay(){ return day; } public void setDay(int newDay){ day=newDay; } public boolean isLeapYear(int year){ if((year%400==0)||(year%4==0&&year%100!=0)){ return true; }else{ return false; } } public boolean checkinputValidity(int newYear,int newMonth,int newDay){ if(year>=1900&&year<=2000){ if(month>=1&&month<=12){ if(day>=1&&day<=31){ if((year%400==0)||(year%4==0&&year%100!=0)){ Sta[1]=29; } if(Sta[month-1]>=day){ return true; }else return false; }else return false; }else return false; }else return false; } public void getNextDate(int newYear,int newMonth,int newDay){ if((year%400==0)||(year%4==0&&year%100!=0)){ Sta[1]=29; } if(Sta[newMonth-1]==newDay&&newMonth==12){ month=1; year++; day=1; System.out.print("Next day is:"+year+"-"+month+"-"+day); //break; } else if(Sta[newMonth-1]==newDay){ day=1; month++; System.out.print("Next day is:"+year+"-"+month+"-"+day); }else{ day++; System.out.print("Next day is:"+year+"-"+month+"-"+day); } } }
类图:
复杂度:
题目结果:
题目分析:对于这一题,主要考虑到的点就是对于日期属性的把握,其中最大的点就是对于平闰年的判断来判断日期,比如闰年的二月份要进行特殊考虑从而使代码更加的完善。
题目集(3)7-4
题目要求:
- 1 year month day n //测试输入日期的下n天
- 2 year month day n //测试输入日期的前n天
- 3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
源码:
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[]Sta=new int[]{31,28,31,30,31,30,31,31,30,31,30,31}; public DateUtil(){ year=0; month=0; day=0; } public DateUtil(int Year,int Month,int Day){ month=Month; year=Year; day=Day; } public int getYear(){ return year; } public void setYear(int Year){ year=Year; } public int getMonth(){ return month; } public void setMonth(int Month){ month=Month; } public int getDay(){ return day; } public void setDay(int Day){ day=Day; } public boolean checkInputValidity(){ if(year>=1820&&year<=2020){ if(month>=1&&month<=12){ if(day>=1&&day<=31){ if((year%400 == 0)||(year%4==0&&year%100!=0)){ Sta[1]=29; } if(Sta[month-1]>=day){ return true; } } } } return false; } public boolean isLeapYear(int Year){ if((Year%400==0)||(Year%4==0&&Year%100!=0)){ return true; }else{ return false; } } public String showDate(){ return year+"-"+month+"-"+day; } public DateUtil getNextNDays(int n){ int year1=year; int month1=0; int day1=0; int Count=0; int tm=365; if(isLeapYear(year1)){ Sta[1]=29; tm=366; }else{ Sta[1]=28; } day1=Sta[month-1]-day; //System.out.println(day1); for(int i=month;i<12;i++){ day1+=Sta[i]; } //System.out.println(day1); year1++; if(n>day1){ n-=day1; Count++; while(true){ if(isLeapYear(year1)){ n-=366; }else{ //System.out.println(year1); n-=365; } if(n>0){ Count++; year1++; }else{ break; } } //System.out.println(n); if(isLeapYear(year1)){ n+=366; Sta[1]=29; for(int i=0;n>0;i++){ n-=Sta[i]; if(n<Sta[i]){ month1=i+1; day1=n; } } }else{ Sta[1]=28; n+=365; for(int i=0;n>0;i++){ if(n<Sta[i]){ month1=i+1; day1=n; } n-=Sta[i]; } } }else{ n=n+(tm-day1); //System.out.println(n); year1--; for(int i=0;n>Sta[i];i++){ month1++; n=n-Sta[i]; //System.out.println(tm); } month1++; day1=n; } DateUtil Days=new DateUtil(year1,month1,day1); return Days; } public DateUtil getPreviousNDays(int n){ int year2=year; int month2=0; int day2=0; int Count=0; int tm2=365; if(isLeapYear(year2)){ Sta[1]=29; tm2=366; }else{ Sta[1]=28; } day2+=day; for(int i=month-2;i>=0;i--){ day2+=Sta[i]; } if(n<day2){ n=day2-n; for(int i=0;n>Sta[i];i++){ month2++; n=n-Sta[i]; } month2++; day2=n; }else{ year2--; n-=day2; while(true){ if(isLeapYear(year2)){ n-=366; }else{ n-=365; } if(n>0){ year2--; }else{ break; } } if(isLeapYear(year2)){ n=Math.abs(n); Sta[1]=29; for(int i = 0;n>Sta[i];i++){ month2=i+1; n-=Sta[i]; } month2++; day2=n; }else{ n=Math.abs(n); Sta[1]=28; for(int i = 0;n>Sta[i];i++){ month2=i+1; n-=Sta[i]; } month2++; day2=n; } } DateUtil Days=new DateUtil(year2,month2,day2); return Days; } public boolean compareDates(DateUtil date){ int year3=year; int month3=month; int day3=day; int yearx=date.year; int monthx=date.month; int dayx=date.day; if(year3>yearx||(year3==yearx&&month3>monthx)||(year3==year&&month3==monthx&&day3>dayx)){ return true; }else{ return false; } } public boolean equalTwoDates(DateUtil date){ int year3=year; int month3=month; int day3=day; int yearx=date.year; int monthx=date.month; int dayx=date.day; if(year3==yearx&&month3==monthx&&day3==dayx){ return true; }else{ return false; } } public int getDaysofDates(DateUtil date){ int year3=year; int month3=month; int day3=day; int yearx=date.year; int monthx=date.month; int dayx=date.day; int n=0; int n1=day3,n2=dayx; if(isLeapYear(year3)){ Sta[1]=29; } for(int i=month3-2;i>=0;i--){ n1+=Sta[i]; }Sta[1]=28; if(isLeapYear(yearx)){ Sta[1]=29; } for(int i=monthx-2;i>=0;i--){ n2+=Sta[i]; }Sta[2]=28; if(equalTwoDates(date)){ return 0; }else{ if(compareDates(date)){//前年大于后一年 if(year3!=yearx){ n=n1; }else{ n=n1-n2; return n; } year3--; while(true){ if(year3!=yearx){ if(isLeapYear(year3)){ n+=366; }else{ n+=365; } year3--; }else{ break; } } if(isLeapYear(yearx)){ n2=366-n2; n+=n2; }else{ n2=365-n2; n+=n2; } return n; }else{ if(year3!=yearx){ n=n2; }else{ n=n2-n1; return n; } yearx--; while(true){ if(year3!=yearx){ if(isLeapYear(yearx)){ n+=366; }else{ n+=365; } yearx--; }else{ break; } } if(isLeapYear(year3)){ n1=366-n1; n+=n1; }else{ n1=365-n1; n+=n1; } return n; } } } }
复杂度:
结果:
题目分析:这道题目是目前碰到过难度最大的题目,整道题目测试点全部通过需要很多的考虑,比如带参构造及无参构造,还有包括各种方法间对象的调用,以及各种方法中算法设计是否正确的考虑都是需要在内的。
在整个题目书写前,你需要考虑是一天一天计算或是跨年计算并考虑剩余时间(是否大于这一年的总天数),需要对这方面进行完善的考虑,但在试错后也会发现这一种算法会大大超出题目所限制的时间复杂度或是内存超限,之后再考虑这一种需要精简更多的算法,还有包括在构思以及实际码的过程中要步调一致,不要脑子在想这,手却在想那,这真的留下了血的教训(在构思第三个方法的算法时其中一个对象的传值错误导致整个题目需要进行调试,大大浪费了时间)。
踩坑心得
1.在写任何一个题目前,都需要对题目进行尽可能完整的构思,防止造成停滞不前的效率低下。
2.对于一些浮点数之间的计算,一定要考虑到他们之间的精度损失问题,这将判断是否能达成你这个是否能一遍过不浪费时间的目的,这也在题目集2的两道题目中出现过。
3.一定不要脑热,否则会造成需要大量调试找出问题的事情发生,造成效率的下降,时间补的局面。
4,考虑到越界问题,下标访问导致数组越界的问题屡见不鲜。
5.代码要尽可能的进行优化,降低其时间复杂度,空间复杂度的问题,在可以的情况下,可以考虑使用hashset,hashmap等可以大大提高时间效率的工具,做到事半功倍。
改进建议
主要是要熟悉对于各种基础语法的运用,这也急不来,需要频繁的练习来加强对于这些语句的熟悉度,除此以外,加强对于各种本地方法的调用来更加的熟能熟巧,遇见一些查改问题,可以优先选择hash表,或是Arraylist等工具方便。最重要也是最基础的一点就是代码风格的问题,需要尽可能的贴近流行的代码风格,例如驼峰命名,设置数组时不要写成c语言的形式(例子:String [ ] args而非String args[ ])两者虽然在功能上是一样的,但是严格的代码规范就使你不能特立独行,还有包括英文命名,不要用你那破abcd来命名变量,这样会使代码的维护更加困难,不仅别人看不懂你的代码,可能在写完之后没多久你自己也就看不懂自己的代码了。总的俩说,就是要在代码规范上要更下功夫。
总结
通过这三次的练习感触还是蛮大的,虽然每次都能写完过测试点,但时间上都特别的赶,这也会使得特别尴尬,所以还是要多多练习打牢基础,基础不牢,地动山摇可不是跟你开玩笑的。除此以外,学习任何一门语言都是需要时间的磨砺,即使是大牛也是一脚一脚迈上来的,没有人生来就会码代码,都需要不断地学习和钻研。言尽于此,继续敲码。