面向对象程序设计第一次博客作业
前言(三次PTA题目集总结):
首先是第一次的题目集,通过此次作业我初步掌握了JAVA的输入输出方法、循环语句及选择语句的使用、字符串的使用及其中的方法、数组的创建与使用。其中在1~4题,重点学习了循环语句及选择语句的使用,如:for语句、if-else语句,这些内容与上学期学习的C语言基本一样,方便掌握。从第5题开始重点学习的就是字符串以及数组的创建及使用,题目相对来说难度有了提升,因为这些语法与C语言存在较大的差别,首先字符串的定义:String big = input.nextLine(),然后是数组的定义:elementType[] arrayRefVar = new elementType[arraySize]。并且在刷题过程我也学习了一些别的知识点,例如小数位数的保留可使用String.format()方法;排序方法可以使用Arrays.sort()方法进行快排,方便数据的查找与比对;同时也学习了数组与字符串间的转换方法:num[i]= str.charAt(i),当然这是String数据类型 间的转换。通过本次练习,也了解到JAVA语言的一些好处,有许多自带的方法,可以节省很多时间与空间。
现在再来说一说第二次作业的题目知识点。这一次的作业我认为是在第一次作业基础上的进一步学习,通过此次作业,我对于循环以及选择语句的使用、String的使用更加顺手,同时也学会了一些新的方法,跟重要的是此次作业锻炼了我的逻辑能力。这边写点我印象深刻的,第一个就是double、float数据类型的使用。float是单精度类型,精度是8位有效数字,取值范围是10的-38次方到10的38次方,float占用4个字节的存储空间double是双精度类型,精度是17位有效数字,取值范围是10的-308次方到10的308次方,double占用8个字节的存储空间。并且当你不声明的时候,默认小数都用double来表示,float是8位有效数字,第7位数字将会产生四舍五入,在数据类型确定时需要注意。其次是学习到的新方法,如public String substring(int beginIndex),这个方法截取的字符串是从索引beginIndex开始的,到整个字符串的末尾; public String substring(int beginIndex, int endIndex),这个方法截取的字符串从beginIndex开始,到字符串索引的(endIndex - 1)结束,即截取的字符串不包括endIndex这个索引对应的字符,在使用时需要注意越界问题。再然后就是第一次遇见的自己建议一个新的方法来完成程序,如最后一题求下一天,写了两个返回布尔值的方法来分别判断输入时间是否正确、该年是否是闰年,创建多个方法执行不同的功能使得程序更为明了,修改起来也更为方便。最后再来谈谈我认为这次作业最主要的一块--思维逻辑,在最后两题尤其重要。例如7-8判断三角形类型,就需要去仔细分析各种三角形类型之间是否存在包含关系,以便确定判断输出的先后顺序,在程序的设计过程中必须理清逻辑,不然在运行时很可能会出现逻辑错误,执行的顺序和自己设想的不同。
最后就是第三次题目集的总结了。此次作业由于写法与前几次有了较大的区别,对于我来说难度直接上了一个档次,从这次作业起,我开始接触类的定义与对象的创建。在第一题的编写过程中初步掌握了带参数构造方法、无参构造方法、属性的getter、setter方法,同时也了解了属性的私有与公有,当掌握这些以后下面的题写起来就会顺手许多。其余东西在这就不过多赘述,在接下来的设计与分析中再进行叙述。
总的来说这三次题目集是一个循序渐进的过程,第一次题目集看似量大,实则不难,是C语言与JAVA的一个过渡,第二次作业我认为老师开始锻炼我们的逻辑思维能力,并开始真正的学习JAVA语言,而第三次题目集看似题量少实则难度大增,很多都是未曾接触过的东西。这就需要我们自己结合书本与网页进行学习。
设计与分析:
训练集2(7-8)
题目要求:输入三角形三条边,判断该三角形为什么类型的三角形。
源码:
1 import java.util.*; 2 3 public class Main{ 4 public static void main(String[] args){ 5 Scanner input = new Scanner(System.in); 6 double a = input.nextDouble(); 7 double b = input.nextDouble(); 8 double c = input.nextDouble(); 9 10 if(a<1||a>200||b<1||b>200||c<1||c>200){//判断数据是否合法 11 System.out.print("Wrong Format"); 12 return; 13 } 14 if(a+b>c&&a+c>b&&b+c>a){//判断是否构成三角形,可以则进入if语句,否则跳转27行执行输出 15 if(a==b&&a==c)//先判断是否为等边三角形 16 System.out.print("Equilateral triangle"); 17 else if((a==b&&a*a+b*b-c*c<0.01)||(b==c&&b*b+c*c-a*a<0.01)||(a==c&&a*a+c*c-b*b<0.1))//判断是否为等腰直角三角形 18 System.out.print("Isosceles right-angled triangle"); 19 else if(a*a+b*b-c*c==0||b*b+c*c-a*a==0||a*a+c*c-b*b==0)//判断是否为直角三角形 20 System.out.print("Right-angled triangle"); 21 else if(a==b||a==c||b==c)//判断是否为等腰三角形 22 System.out.print("Isosceles triangle"); 23 else 24 System.out.print("General triangle");//以上都不是则为一般三角形 25 26 } 27 else 28 System.out.print("Not a triangle"); 29 } 30 }
分析:此题最主要的点就是要理清楚判断的先后顺序,先判断数据合法性在判断类型,判断类型时要注意若是先行判断了等腰三角形,则后面的等腰直角三角形、等边三角形无法进行判断;若是先判断了直角三角形,后面的等腰直角三角形同样无法判断。同时还有一点需要注意,就是判断是的精确度,如:a*a+b*b-c*c<0.01,这里之所以不直接等于零,是因为电脑储存浮点数不精确。下面可以试试判断顺序错误以后的结果。
这就是先判断等腰三角形再判断等腰直角三角形的结果,可以看出判断完等腰后直接输出,并未判断是否为等腰直角三角形。
训练集1(7-7)
题目要求:你要写一个程序来做这件事情,读入数据,检查是否有重复的数据。如果有,输出“YES
”这三个字母;如果没有,则输出“NO
”。
源码:
1 import java.util.Scanner; 2 import java.util.Arrays; 3 public class Main{ 4 public static void main(String[] args){ 5 Scanner input = new Scanner(System.in); 6 int n = input.nextInt(); 7 int num[] = new int[n]; 8 9 for(int i = 0;i<n;i++)//输入数组元素 10 num[i] = input.nextInt(); 11 12 Arrays.sort(num);//将数组内的数据快速排序 13 int flag=0; 14 for(int j = 1;j<n;j++){ 15 if(num[j]==num[j-1])//判断两个元素是否相等 16 flag++;//记录相等数据数量 17 } 18 if(flag>0) 19 System.out.print("YES"); 20 else 21 System.out.print("NO"); 22 23 } 24 }
分析:此题最难的地方在于查重方法,大多数的初学者第一想法应该是暴力查重,利用两个for循环进行比较,虽然可以做出题目,但是时间复杂度却上来了,效率不够高,因此我利用Arrays.sort()方法对数组元素进行快速排序后,有序数组只需要比较相邻元素就可以知道是否有重复数据了。这个题其实再进一步可以改为输出去重后的字符串,就可以直接利用Hashset来实现。
训练集3(7-3)
题目要求:定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。求输入日期的下一天。
源码:
1 import java.util.*; 2 public class Main{ 3 public static void main(String[] args){//主方法,输入数据 4 Scanner input = new Scanner(System.in); 5 int nian = input.nextInt(); 6 int yue = input.nextInt(); 7 int ri = input.nextInt(); 8 Date date1 = new Date(nian,yue,ri);//定义一个Date类的对象,传入输入的数据 9 if(!date1.checkInputValidity())//调用Date类的方法判断数据合法性 10 System.out.print("Date Format is Wrong"); 11 else 12 date1.getNextDate();//调用Date类方法进行计算与输出 13 } 14 } 15 class Date{//定义一个Date类 16 private int year; 17 private int month; 18 private int day;//定义私有属性对象 19 int []mon_maxnum = new int[]{31,28,31,30,31,30,31,31,30,31,30,31};//一般年份各月的天数 20 Date(){}//无参构造方法 21 Date(int year,int month,int day){//有参构造方法 22 this.year = year; 23 this.month = month; 24 this.day = day; 25 } 26 int getYear(){//主要用于属性内容的取得 27 return year; 28 } 29 void setYear(int year){//主要给成员变量做赋值和一定的保护 30 this.year = year; 31 } 32 int getMonth(){ 33 return month; 34 } 35 void setMonth(int month){ 36 this.month = month; 37 } 38 int getDay(){ 39 return day; 40 } 41 void setDay(int day){ 42 this.day = day; 43 } 44 boolean isLeapYear(int year){//判断是否是闰年,返回布尔值 45 if(year%400==0||(year%4==0&&year%100!=0)) 46 return true; 47 else 48 return false; 49 } 50 boolean checkInputValidity(){//判断数据合法性 51 if(year<1900||year>2000||month<1||month>12||day<1||day>31) 52 return false; 53 else{ 54 if((month==1||month==3||month==5||month==7||month==8||month==10||month==12)&&day<=31) 55 return true; 56 else if((month==4||month==6||month==9||month==11)&&day<=30) 57 return true; 58 else if(isLeapYear(year)&&month==2&&day<=29) 59 return true; 60 else if(!isLeapYear(year)&&month==2&&day<=28) 61 return true; 62 else 63 return false; 64 } 65 } 66 void getNextDate(){ 67 if(isLeapYear(year))//是闰年则2月为29天 68 mon_maxnum[1]=29; 69 if(day<mon_maxnum[month-1])//如果当前日期小于当前月份天数,则日期加1就好 70 day++; 71 else if(day==mon_maxnum[month-1]&&month==12){//如果日期等于当前月份且为12月,则年份加1,日期与月份变为1 72 year++; 73 month=1; 74 day=1; 75 } 76 else if(day==mon_maxnum[month-1]&&month!=12){//若日期与当前月份天数相等但不是12月,则日期变为1,月份加1 77 month++; 78 day=1; 79 } 80 System.out.print("Next day is:"+getYear()+"-"+getMonth()+"-"+getDay());//使用属性的getter方法进行输出 81 } 82 }
分析:这道题不算难,但是对于初学来说比较新颖,首先有私有类型属性,调用时需要用到getter方法,不可直接调用;其次就是计算下一天有情况要弄清楚,情况分6种,闰年与平年各3种相同方法,先确定是否是某月的最后一天,再确定是否是某年的最后一个月,接着就可以进行计算。当然在月份天数数组中可以在首元素位置加上一个0,这样再利用下标获得月份天数的时候更易懂。
训练集3(7-4)
题目要求:
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
源码:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String[] args) { 5 Scanner input = new Scanner(System.in); 6 int year = 0; 7 int month = 0; 8 int day = 0; 9 10 int choice = input.nextInt(); 11 //三次判断选择执行的功能 12 if (choice == 1) { // test getNextNDays method 13 int m = 0; 14 year = Integer.parseInt(input.next()); 15 month = Integer.parseInt(input.next()); 16 day = Integer.parseInt(input.next()); 17 18 DateUtil date = new DateUtil(year, month, day); 19 20 if (!date.checkInputValidity()) {//输入数据合法性判断 21 System.out.println("Wrong Format"); 22 System.exit(0);//结束程序 23 } 24 25 m = input.nextInt(); 26 27 if (m < 0) { 28 System.out.println("Wrong Format"); 29 System.exit(0); 30 } 31 32 System.out.print(date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " next " + m + " days is:"); 33 date.getNextNDays(m); 34 System.out.println(date.getNextNDays(m).showDate()); 35 } 36 else if (choice == 2) { // test getPreviousNDays method 37 int n = 0; 38 year = Integer.parseInt(input.next()); 39 month = Integer.parseInt(input.next()); 40 day = Integer.parseInt(input.next()); 41 42 DateUtil date = new DateUtil(year, month, day); 43 44 if (!date.checkInputValidity()) { 45 System.out.println("Wrong Format"); 46 System.exit(0); 47 } 48 49 n = input.nextInt(); 50 51 if (n < 0) { 52 System.out.println("Wrong Format"); 53 System.exit(0); 54 } 55 56 System.out.print( 57 date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " previous " + n + " days is:"); 58 System.out.println(date.getPreviousNDays(n).showDate()); 59 } 60 else if (choice == 3) { //test getDaysofDates method 61 year = Integer.parseInt(input.next()); 62 month = Integer.parseInt(input.next()); 63 day = Integer.parseInt(input.next()); 64 65 int anotherYear = Integer.parseInt(input.next()); 66 int anotherMonth = Integer.parseInt(input.next()); 67 int anotherDay = Integer.parseInt(input.next()); 68 69 DateUtil fromDate = new DateUtil(year, month, day); 70 DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay); 71 72 if (fromDate.checkInputValidity() && toDate.checkInputValidity()) { 73 System.out.println("The days between " + fromDate.showDate() + 74 " and " + toDate.showDate() + " are:" 75 + fromDate.getDaysofDates(toDate)); 76 } else { 77 System.out.println("Wrong Format"); 78 System.exit(0); 79 } 80 } 81 else { 82 System.out.println("Wrong Format"); 83 System.exit(0); 84 } 85 } 86 } 87 88 89 class DateUtil{//定义一个DateUtil类 90 private int year; 91 private int month; 92 private int day; 93 94 public DateUtil(){} 95 DateUtil(int year,int month,int day){ 96 this.year = year; 97 this.month = month; 98 this.day = day; 99 } 100 int getYear(){ 101 return year; 102 } 103 void setYear(int year){ 104 this.year = year; 105 } 106 int getMonth(){ 107 return month; 108 } 109 void setMonth(int month){ 110 this.month = month; 111 } 112 int getDay(){ 113 return day; 114 } 115 void setDay(int day){ 116 this.day = day; 117 } 118 119 public boolean isLeapYear(int year){ 120 if(year%400==0||(year%4==0&&year%100!=0)) 121 return true; 122 else 123 return false; 124 } 125 public boolean checkInputValidity(){ 126 if(year<1820||year>2020||month<1||month>12||day<1||day>31) 127 return false; 128 else{ 129 if((month==1||month==3||month==5||month==7||month==8||month==10||month==12)&&day<=31) 130 return true; 131 else if((month==4||month==6||month==9||month==11)&&day<=30) 132 return true; 133 else if(isLeapYear(year)&&month==2&&day<=29) 134 return true; 135 else if(!isLeapYear(year)&&month==2&&day<=28) 136 return true; 137 else 138 return false; 139 } 140 } 141 public DateUtil getNextNDays(int n){//获得下n天的方法 142 int a=this.year; 143 int b=this.month; 144 int c=this.day; 145 int []yue = new int[]{31,28,31,30,31,30,31,31,30,31,30,31}; 146 while(n>365){//判断n是否足够一年的天数,足够则执行,不足则跳转156行 147 if((isLeapYear(a)&&b<3)||(isLeapYear(a+1)&&b>=3)){//判断当前日期,若是闰年且月份小于三月,则n-366,因为这样调到下一年本日的话,会经过本年2月份,要多加了一天;若下一年为闰年且当前月份大于2月的话,跳到下一年本日会经过下一年的2月,有29天,要多加一天 148 a++; 149 n-=366; 150 } 151 else{ 152 a++; 153 n-=365; 154 } 155 } 156 if(isLeapYear(a)||isLeapYear(a+1))//n不足一年以后,只需要判断本年与下一年2月的天数 157 yue[1]=29; 158 while(n>yue[b-1]){//判断n是否大于本月天数,如果足够则可以跳转到下一月的本日,n减去本月天数,如果月份大于12的话,则变为下一年的一月 159 n=n-yue[b-1]; 160 b++; 161 if(b>12){ 162 b=1; 163 a++; 164 } 165 } 166 if(n>(yue[b-1]-c)){//判断n是否足够本月剩下的天数,如果足够则补本月,跳转到下月,剩下的n则为当前日期,如果月份大于12则会跳转到下一年一月 167 n=n+c-yue[b-1]; 168 b++; 169 c=n; 170 if(b>12){ 171 b=1; 172 a++; 173 } 174 } 175 else{//若是n不足本月剩余天数,则日期直接加n 176 c=c+n; 177 n=0; 178 } 179 DateUtil d =new DateUtil(a,b,c);//创建一个DateUtil类的对象,方便返还日期 180 return d; 181 } 182 public DateUtil getPreviousNDays(int n){//获得前n天的方法,与获得后n天的方法类似 183 184 int a=this.year; 185 int b=this.month; 186 int c=this.day; 187 int []yue = new int[]{31,28,31,30,31,30,31,31,30,31,30,31}; 188 while(n>365){ 189 if((isLeapYear(a)&&b>2)||(isLeapYear(a-1)&&b<3)){ 190 a--; 191 n-=366; 192 } 193 else{ 194 a--; 195 n-=365; 196 } 197 } 198 if(isLeapYear(a)||isLeapYear(a-1)) 199 yue[1]=29; 200 while(n>yue[b-1]){ 201 if(b==1){ 202 n=n-yue[11]; 203 b--; 204 } 205 else{ 206 n=n-yue[b-2]; 207 b--; 208 } 209 if(b==0){ 210 b=12; 211 a--; 212 } 213 } 214 215 216 if(n>c){ 217 b--; 218 c=yue[b-1]-n+c; 219 n=0; 220 if(b==0){ 221 b=12; 222 a--; 223 } 224 } 225 else{ 226 c=c-n; 227 n=0; 228 } 229 DateUtil f =new DateUtil(a,b,c); 230 return f; 231 } 232 public boolean compareDates(DateUtil date){//比较两个日期的先后顺序 233 if(this.year>date.year)//先判断年份再判断年份相同时的月份,接着再判断年份月份都相同时的日期;其余情况下都一样 234 return true; 235 else if(this.year==date.year&&this.month>date.month) 236 return true; 237 else if(this.year==date.year&&this.month==date.month&&this.day>date.day) 238 return true; 239 else 240 return false; 241 } 242 public boolean equalTwoDates(DateUtil date){//判断是否为同一天 243 if(this.year==date.year&&this.month==date.month&&this.day==date.day) 244 return true; 245 else 246 return false; 247 } 248 public int getDaysofDates(DateUtil date){//计算两个日期间隔的天数,我这边使用的方法是直接计算,分别计算两个日期从0年0月0日开始分别经过了多少天 249 int []a = new int[]{31,28,31,30,31,30,31,31,30,31,30,31}; 250 int count = 0; 251 int sum = 0; 252 int cha=0; 253 for(int i=0;i<this.year-1;i++){ 254 if(isLeapYear(i)) 255 count+=366; 256 else 257 count+=365; 258 } 259 for(int i=1;i<this.month;i++){ 260 if(isLeapYear(this.year)) 261 a[1]=29; 262 count=count+a[i-1]; 263 } 264 count=count+this.day; 265 for(int i=0;i<date.year-1;i++){ 266 if(isLeapYear(i)) 267 sum+=366; 268 else 269 sum+=365; 270 } 271 for(int i=1;i<date.month;i++){ 272 if(isLeapYear(date.year)) 273 a[1]=29; 274 sum=sum+a[i-1]; 275 } 276 sum=sum+date.day; 277 if(equalTwoDates(date))//若是同一天则间隔为0 278 cha=0; 279 else{//判断日期先后顺序确定是哪一天减去哪一天,以确保得到的天数为正数 280 if(compareDates(date)) 281 cha=count-sum; 282 else 283 cha=sum-count; 284 } 285 return cha; 286 } 287 public String showDate(){ 288 return (year+"-"+month+"-"+day); 289 } 290 } 291
分析:这道题目时这次习题集中最难得一次,因为它需要我们去思考日期怎么加减。我的思路是这样的,当天数大于一年时则先跳到下一年,在此过程中需要判断n是减366还是365,这就涉及到在跳转过程中经过的二月份是28天,还是29天,这些判断好了,剩余的就好做了。
踩坑心得
- 审要清楚,要搞明白输入与输出的数据类型,就如第二次习题集的1、3、6题的输出就需要是float类型
2.在程序中使用了数组的话要注意越界问题,就如第三次习题集的最后一题月份数组,如果小标增加到了13,你要先将它赋值为1在使用
3.当有数据加减以后要使用时需注意先后顺序,如最后一题,如果现加了b在计算n,那么利用下标获取的元素会出现错误
4.要理清楚判断的先后顺序,如第二次题目集的7-8,需要搞清楚三角形类型间是否有重合,一般来说先判断大项再判断小项
5.要注意设计出的程序的复杂度,一般来说是越低越好,更易读懂,如 题目集1的7-7,使用快排后再使用一个for循环比较会比两个for循环嵌套使用时间复杂度低
改进建议
我认为自己在数组、字符串的使用上可以进行优化,比如使用ArrayList,在求时间类方面在没有限制的前提下以后可以使用LocalDate类。在判断方面也可以进行简化。在程序架构方面在合适的情况下可以建几个方法去执行单一的行为,在变量名方面也可做出改进,在此次作业中有些地方变量名取得有点随意,使用了abc这样的,有时候可能连自己都不好读懂
总结
这三次的题目集对我来说就是一个循序渐进的过程,从JAVA语言基本的输入输出到字符串、数组的创建使用,再到类的定义与对象的创建,通过书本与网站的学习,所了解的也在逐步增加。学习Java是一个不断进步的过程,在此期间我们除了要掌握基本的语法,耕种奥的是锻炼自己的逻辑思维,知道怎么去构建一个程序。当然每写一道题都是一次进步,我们需要弄清楚每一次错误后i的原因,不断完善它。我认为自己目前在类的定义方面还需呀继续学习,对于其值的接受与返回还不是很熟练,然后对于各个类之间的关系也不是很清楚;别的也还有很多需要掌握,如JAVA自带的很多方法,需要去学习使用,可以让程序编写过程更加轻松。