前言:
在这三次次题目集里,我们通过共25道题初步了解了Java的一些基本的语法和格式的知识点,有对数据的处理和对字符串的操作,还有类的应用等。可以说是非常简单且基础的三次作业。但在学习的过程中也碰到了很多问题,在解决问题的过程中总结出了一些心得体会与方法等等。以下为总结作业详情:第一次作业:
第一次作业是Java的入门,是简单的输入(Scanner类)输出(println、printf、print等)数据处理以及字符串(String类)操作。简单讲讲不是很熟练的String类:
题目7-5 去掉重复字符:
我的代码:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); String s = in.next(); int sum = s.length(); int flag=0; for(int i=0;i<sum;i++) { for(int j=0;j<i;j++) { if(s.charAt(i)==s.charAt(j)) flag=1; } if(flag!=1) System.out.print(s.charAt(i)); flag=0; } } }
在设计时我思考出的方法是:从第一个字符开始,比较此字符之前是否有重复字符,若无则输出该字符。
Source Monitor:
可以看出我的代码平均复杂度较高,当时还没有学到String类的indexOf方法,以及StringBuffer类的append方法,所以用C语言的习惯使用了双层循环来检测重复字符的出现。
但在Java里我们完全可以用单层循环实现,这就节省了很多时间,平均复杂度也会降低很多。但其实思想也相差不大。
这是修改后的:
import java.util.Scanner;
public class test01 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
StringBuffer str = new StringBuffer();
for(int i=0;i<s.length();i++) {
char ch = s.charAt(i);
if(s.indexOf(ch) == i) {
str.append(ch);
}
}
System.out.print(str);
}
}
Source Monitor:
其实也可以用String类中的indexOf方法以及lastIndexOf来对比该字符在输入中的位置,这也是更加简单的方法之一,因为比较简单就不再附上代码了。
第二次作业:
第二次作业的主要训练内容是对数据的处理,以及帮助我们理解与认识到float与double两种不同类型浮点数的异同。double类的精度比float类高,同时对存储空间需求也比float高,所以我们可以在不同情况下做出不同的选择:在对精度要求高的情况下使用double,对精度要求不高或对存储空间要求较高或对前两种情况合取的情况下使用float。而题目7-8 判断三角形类型这题我自认为写得非常差,借此进行分析和修改。
题目7-8 判断三角形类型:
我的代码:
import java.util.*; public class Main { public static void main(String[] args) { Scanner sides = new Scanner(System.in); double side1 = sides.nextDouble(); double side2 = sides.nextDouble(); double side3 = sides.nextDouble(); side1 = (int)(side1 * 10000) / 10000.0; side2 = (int)(side2 * 10000) / 10000.0; side3 = (int)(side3 * 10000) / 10000.0; float s1 = (float)(side1 * side1); float s2 = (float)(side2 * side2); float s3 = (float)(side3 * side3); float ext; if(s1 > s2) { ext = s1; s1 = s2; s2 = ext; } if(s1 > s3) { ext = s1; s1 = s3; s3 = ext; } if(s2 > s3) { ext = s2; s2 = s3; s3 = ext; } if(side1 < 1 || side1 >200 ||side2 < 1 || side2 >200 ||side3 < 1 || side3 >200) { System.out.println("Wrong Format"); } else { if(side1 + side2 <= side3 || side1 + side3 <= side2 || side3 + side2 <= side1) { System.out.println("Not a triangle"); } else if(side1 == side2 && side2 == side3) { System.out.println("Equilateral triangle"); } else if(side1 == side2 || side1 == side3 || side2 == side3) { if(s1 + s2 - s3 < 0.0001) { System.out.println("Isosceles right-angled triangle"); } else { System.out.println("Isosceles triangle"); } } else if(s1 + s2 == s3 || s1 + s3 == s2 || s2 + s3 == s1) { System.out.println("Right-angled triangle"); } else { System.out.println("General triangle"); } } } }
可以看出非常屎山,很难想象出当时的精神状态...判断写太多,重复又无用...而且Source Monitor也是各种爆表:
Source Monitor:
其中需要改进的地方有:在输入后可以使用 Arrays.sort 进行三边的排序,可以有效避免在 if 里写太多的“ | | ”,在判断时会遇到有两种三角形类型叠加的情况,此时可以再分块进行判断,避免 if 嵌套增加复杂度。也可以创建和Triangle类对数据进行处理和输出。
第三次作业:
第三次作业的7-3 定义日期类与7-4 日期类设计是作业的重点,这里我们重点拿出分析。
题目7-3 定义日期类:
类图:
其中Date类里包含:
private类型的year,month,day,以及每月最大天数的数组。方法中setter与getter方法就不做解释。
boolean isLeapYear(int year)方法的作用则是检测所set的年份是否为闰年,是则返回true。
boolean checkInputValidity()的作用是检测输入日期是否合法,合法的范围包括年份在大于等于1900且小于等于2000,月份则在一月到十二月,天数则在每个月的最大天数范围内。因为闰年的二月有29天,所以我们在进行检测时,要注意先判断输入年份是否为闰年,如果是闰年则将二月最大天数改为29天。如果所有检测均合法,则返回true。
void getNextDate()的方法,执行此方法将令当前日期转为当前日期的下一天,这里我先让day + 1,如果day超过了当前月份的最大天数,则month + 1,并将day重新设置成1;同理,如果month超过了年份的最大月份,则year + 1,并将month重新设置成1。这样就可以实现跨月的下一天以及跨年的下一天。
我的代码:
import java.util.*; class Date { private int year; private int month; private int day; public int[] mon_maxnum = new int[] {0,31,28,31,30,31,30,31,31,30,31,30,31}; public Date() { year = 1900; month = 1; day = 1; } public Date(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; } int getDay() { return this.day; } public void setDay(int day) { this.day = day; } public boolean isLeapYear(int year) { if(year % 4 == 0 && year % 100 != 0) return true; if(year % 400 == 0) return true; return false; } public boolean checkInputValidity() { if(isLeapYear(year)) mon_maxnum[2] = 29; if(this.year < 1900 || this.year > 2000) return false; if(this.month < 1 || this.month > 12) return false; if(this.day < 1 || this.day > mon_maxnum[month]) return false; return true; } public void getNextData() { if(isLeapYear(year)) mon_maxnum[2] = 29; day += 1; if(day > mon_maxnum[month]) { month += 1; day = 1; } if(month == 13) { year += 1; month = 1; } setYear(year); setMonth(month); setDay(day); } } public class test01 { public static void main(String[] args) { Scanner date0 = new Scanner(System.in); int year = date0.nextInt(); int month = date0.nextInt(); int day = date0.nextInt(); Date date = new Date(year, month, day); if(date.checkInputValidity() == false) { System.out.print("Date Format is Wrong"); return ; } date.getNextData(); System.out.print("Next day is:" + date.getYear() + "-" + date.getMonth() + "-" + date.getDay()); } }
Source Monitor:
题目7-4 日期类设计
类图:
其中DateUtil类里要包含:
private类型的year,month,day,以及每月最大天数的数组。方法中setter与getter方法就不做解释。
boolean checkInputValidity () 的作用是检测输入日期是否合法,合法的范围包括年份在大于等于1820且小于等于2020,月份则在一月到十二月,天数则在每个月的最大天数范围内。因为闰年的二月有29天,所以我们在进行检测时,要注意先判断输入年份是否为闰年,如果是闰年则将二月最大天数改为29天。如果所有检测均合法,则返回true。
DateUtil getNextNDays (int n) 中,我们要实现将输入的日期按照下n天输出,这里有个难点,当n达到整数的极限(即2147483647)时,应该怎样输出。起初我并没有注意到这个点,所以我直接让day加上n值再进行while循环做月份的增加以及年份的增加,直到day的值不再溢出(即不大于当前月的最大天数)则循环停止。这样直接加的方法会导致当n达到int的最大值时报错。在修改时我想:既然day的值不会超过当前月的最大天数,那么我可以在n大于当前月最大天数时,先减去当前月的最大天数,用flag保存减去的值,再用减后的n值再加上day,这样最大也只能大到n减之前的数了。在减去之后进入循环,先执行一遍day的减法,执行完后再判断flag是否为0(即输入的n的值是否做过减法),否则加上减去的天数,加上后flag重新赋值为0。这样就解决了数值过大的问题。但是这种方法无疑会增加复杂度。在进行完第一次flag归零后,之后的每次都得判断一次flag 的值,这其实大无必要。这个if判断其实可以去掉,有没有都没关系。这是我觉得应该改进的点。
DateUtil getPreviousNDays(int n) 是输出当前日期的前n天的方法,这里直接做减法即可,与getNextNDays(int n)方法如出一辙。
boolean compareDates (DateUtil date) 则用于比较输入的两个日期的大小,若前者大于后者则返回true,否则返回false。此方法服务于获得两个日期间的天数的方法。
boolean equalTwoDates (DateUtil date) 是判断两个日期是否相等的方法,相等则返回true,否则返回false。此方法同样服务于获得两个日期间的天数的方法。
int getDaysofDates (DateUtil date) 是返回两个日期间隔天数的方法,是这题的另一个难点。但我想到:其实这个方法我已经写好了。我为什么不能使用上面的求当前日期的下n天与前n天的方法来求出两个日期的间隔呢?compareDates () 去判断该使用getNextNDays ( 1 ) 或getPreviousNDays ( 1 ) ,通过日期的不断加减,count计数也同步增加,并且使用equalTwoDates () 判断加减后的日期是否达到相同。相同后循环结束,返回count值,即两日期之间相隔天数。依靠计算机强大的算力,很快就能得出结果。
String showDate () 则用于按照一定格式输出日期。
我的代码:
import java.util.Scanner; class DateUtil { private int year; private int month; private int day; public int[] mon_maxnum = new int[] {0,31,28,31,30,31,30,31,31,30,31,30,31}; public DateUtil() { year = 1820; month = 1; day = 1; } public 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 checkInputValidity() {//检测输入的年、月、日是否合法 if(isLeapYear(year)) mon_maxnum[2] = 29; if(this.year < 1820 || this.year > 2020) return false; if(this.month < 1 || this.month > 12) return false; if(this.day < 1 || this.day > mon_maxnum[month]) return false; return true; } public boolean isLeapYear(int year) {//判断year是否为闰年 if(year % 4 == 0 && year % 100 != 0) return true; if(year % 400 == 0) return true; return false; } public DateUtil getNextNDays(int n) {//取得year-month-day的下n天日期 int flag = 0; if(n > mon_maxnum[this.month]) { n-=mon_maxnum[this.month]; flag = mon_maxnum[this.month]; } this.day += n; while(this.day > mon_maxnum[this.month]) { mon_maxnum[2] = 28; if(isLeapYear(this.year)) mon_maxnum[2] = 29; this.day -= mon_maxnum[this.month]; if(flag != 0) { this.day += flag; flag = 0; } this.month += 1; if(this.month == 13) { this.year += 1; this.month = 1; } } return this; } public DateUtil getPreviousNDays(int n) {//取得year-month-day的前n天日期 this.day -= n; while(this.day < 0) { mon_maxnum[2] = 28; if(isLeapYear(this.year)) mon_maxnum[2] = 29; this.month --; if(this.month == 0) { this.year -= 1; this.month = 12; } this.day += mon_maxnum[this.month]; } return this; } public boolean compareDates(DateUtil date) {//比较当前日期与date的大小(先后) if(date.getYear() < this.year) return false; if(date.getMonth() < this.month && date.getYear() == this.year) return false; if(date.getDay() < this.day && date.getYear() == this.year && date.getMonth() == this.month) return false; return true;// 如果是真,则表明date大于当前日期 } public boolean equalTwoDates(DateUtil date) {//判断两个日期是否相等 if(date.getYear() == this.year) if(date.getMonth() == this.month) if(date.getDay() == this.day) return true; return false; } public int getDaysofDates(DateUtil date) {//求当前日期与date之间相差的天数 int count = 0; while(!equalTwoDates(date)) { if(compareDates(date)) { getNextNDays(1); count ++; continue; } else { getPreviousNDays(1); count ++; } } return count; } public String showDate() {//以“year-month-day”格式返回日期值 String dates = Integer.toString(this.year) + '-' + Integer.toString(this.month) + '-' + Integer.toString(this.day); return dates; } } 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); } } }
Source Monitor:
可以看到,其实还有很大的改进空间。
总结:
这三次作业可以说都是比较基础但是有难点的题目,很好地锻炼了我们的代码能力。也让我更加清楚地认识到自己的进步空间还很大,要更加虚心地去学习。这三次的作业还让我明白,软件工程并不简单,我们在设计程序时要充分地分析与构造出一个软件的基本结构,并清晰地将它解释并写出。我在写代码时很少用注释,这是我今后应该改进的方向。老师说软件的一个很重要的点就是软件的可维护性,而可维护性不仅体现在低耦合与高内聚,还体现在清楚的注释中。现阶段的我们,在低耦合与高内聚的能力培养上还有一定距离,但是注释是我们每个人开始写代码时就必须开始学习的基本功。我们不仅要在编写代码上拥有扎实的基础,还要在表达上不断地提升自己,为自己的全面发展提供养分,追求全方面的卓越,精益求精。现阶段的自己,在代码能力上需要不断提升,在时间安排上也要有所进步,在表达上更是需要不断地锻炼。通过不断的练习,我相信,日后必有精进。