比起入门的题目集1-3,这阶段题目集4-6的学习感觉过的很快,又到了学习总结的时候了
一、前言
本次的java学习仍是 3 次题目集,学习重点在于加强正则表达式的使用能力
① 题目集四只有 3 道题,题量少,但难度不小,主要考察了以下内容:
• 对正则表达式的掌握。需要自行写出能够匹配正确年月日的正则表达式和对数据进行分割的正则表达式。
• java中类的继承和聚合的使用
② 题目集五适当降低了难度,题量适中,主要考察了以下内容:
• 字符串的分割,数组的合并与排序
• 3 种基本的排序算法(冒泡,选择,插入)
• list接口的创建和使用以及类的聚合关系
③ 题目集六共六道题,题目较前两次简单,主要考察了以下内容:
• 正则表达式的使用,字符的排序
• 类的继承、多态性及其使用方法
• 类接口的创建及接口的引用
二、题目分析及设计
(1) 日期问题面向对象设计(题目集4(7-2)、题目集5(7-5))
题目描述:设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1900,2050] ,month∈[1,12] ,day∈[1,31] 。
题目分析:DateUtil类中进行下n天,前n天和日期间隔天数的求取。
代码设计: 先根据题目所给的类图创建类和方法,再在方法中补充相应的算法,保证算法不出问题即可,核心代码如下
public DateUtil getNextNDays(int n)//取得year-month-day的下n天日期 { for (int i = 0; i < n; i++) { day.dayIncrement(); // Day dayy = new Day(year,month,day1); if (day.getValue() > day.getDayOfMonth()) { day.setValue(1); month.monthIncrement(); if (month.getValue() > 12) { month.resetMin();; year.yearIncrement(); } } } int year = year.getValue(); int month = month.getValue(); int day1 = day.getValue(); return new DateUtil(year, month, day1); } public DateUtil getPreviousNDays(int n)//取得year-month-day的前n天日期 { for (int i = 0; i < n; i++) { day.dayReduction(); while (day.getValue() < 1) { day.month.monthReduction(); if (month.getValue() < 1) { month.resetMax(); year.yearReduction(); } // dayyy = new Day(year,month,day1); day.setValue(day.getDayOfMonth()); } } int year = year.getValue(); int month = month.getValue(); int day1 = day.getValue(); return new DateUtil(year, month, day1); } public int getDaysofDates(DateUtil date) {//求当前日期与date之间相差的天数 String s = showDate(); String s1 = date.showDate(); DateTimeFormatter fmt = DateTimeFormatter.ofPattern("y-M-d"); LocalDate startDate = LocalDate.parse(s,fmt); LocalDate endDate = LocalDate.parse(s1,fmt); return Math.abs((int) startDate.until(endDate, ChronoUnit.DAYS)); }
题目集4(7-2)和题目集5(7-5)题目需求一样,都需要求出对应的日期,但是题目要求使用的类之间的关系不同,写出后类关系图如下
图1.1(题7-2) 图1.2(题7-5)
由图可以看出题7-2是依次聚合,题7-5是其他类与DateUtil类的聚合,做题期间只觉得7-4类关系更简单的,没有发现二者的区别,于是直接测圈复杂度
图1.3 (题7-2) 图1.4 (题7-5)
因为核心算法相同,所以两者圈复杂度相差不大。但使用上题7-5更简洁,所以相比起来还是用题7-4的聚合方法更好。
(2) 图形继承与多态(题目集4(7-3)、题目集6(7-5、7-6))
题目描述: 7-3 图形继承(编写程序,实现图形类的继承,并定义相应类对象并进行测试。),7-5 图形继承与多态(参考题目集 04 中 7-3 中图形类的继承层次结构,本次作业重点研究平面图形相关的处理方法。) ,7-6 实现图形接口及多态性(在Main类的主方法中分别定义一个圆类对象及矩形类对象(其属性值由键盘输入),使用接口的引用分别调用圆类对象及矩形类对象的求面积的方法,直接输出两个图形的面积值。(要求只保留两位小数))。
题目分析:根据题目需求写出相应的类与方法,注意类之间的关系。
代码设计: 7-3 图形继承只需按题目要求将所有的类创建出来并在其中补充相应的方法即可。主要运用类的继承,部分代码如下:
1 class Shape{ 2 public Shape() { 3 System.out.println("Constructing Shape"); 4 } 5 6 public double getArea() { 7 return 0.0 ; 8 } 9 } 10 11 class Circle extends Shape{ 12 13 14 public Circle(){ 15 System.out.println("Constructing Circle"); 16 } 17 18 private double radius ; 19 20 21 public Circle(double radius) { 22 super(); 23 this.radius = radius; 24 } 25 26 public double getRadius() { 27 return radius; 28 } 29 30 public void setRadius(double radius) { 31 this.radius = radius; 32 } 33 34 public double getArea() { 35 return radius * radius * Math.PI ; 36 } 37 38 }
7-5 图形继承与多态在题7-3基础上要求实现java的多态性,我们知道要实现java的多态性必须满足三个必要条件:继承、重写、向上转型。而在7-3中我们已经满足了继承,重写这两个条件,因此我们最需要再满足向上转型即可完成题目7-5,代码如下:
1 public class Main { 2 public static void main(String[] args) { 3 Scanner scan = new Scanner(System.in); 4 List<Shape> s = new ArrayList<Shape>(); //list接口 5 6 int a = scan.nextInt() ; 7 int b = scan.nextInt() ; 8 int c = scan.nextInt() ; 9 int d = a + b + c ; 10 int g = 0 ; 11 12 if( d < 0 ) { 13 System.out.println("Wrong Format"); 14 } 15 else { 16 17 while( a > 0 ) { //圆 18 a--; 19 Circle circle = new Circle(scan.nextDouble()); 20 if(circle.validate()) { 21 s.add(circle); 22 g++; 23 } 24 else{ 25 System.out.println("Wrong Format"); 26 break; 27 } 28 } 29 30 while( b > 0 ) { //矩形 31 b--; 32 Rectangle rectangle = new Rectangle(scan.nextDouble(),scan.nextDouble()); 33 if(rectangle.validate()) { 34 s.add(rectangle); 35 g++; 36 } 37 else{ 38 System.out.println("Wrong Format"); 39 break; 40 } 41 } 42 43 while( c > 0 ) { // 三角形 44 c--; 45 Triangle triangle = new Triangle(scan.nextDouble() , scan.nextDouble() , scan.nextDouble() ); 46 if(triangle.validate()) { 47 s.add(triangle); 48 g++; 49 } 50 else{ 51 System.out.println("Wrong Format"); 52 break; 53 } 54 } 55 } 56 57 if( g == d ) { 58 59 System.out.println("Original area:"); 60 double all = 0; 61 62 for (int i = 0; i < s.size(); i++) { // 计算面积总和并输出各个面积 63 System.out.printf(String.format("%.2f", s.get(i).getArea()) + " "); 64 all = all + s.get(i).getArea(); 65 } 66 System.out.println(""); 67 System.out.println("Sum of area:"+String.format("%.2f", all)); 68 69 Collections.sort(s, new Comparator<Shape>() { //对类进行排序 70 public int compare(Shape user1, Shape user2) { 71 Integer id1 = (int) user1.getArea(); 72 Integer id2 = (int) user2.getArea(); 73 return id2.compareTo(id1); 74 } 75 }); 76 77 System.out.println("Sorted area:"); 78 79 for (int i = s.size() - 1 ; i >= 0 ; i--) { // 输出排序后的各个面积 80 System.out.printf(String.format("%.2f", s.get(i).getArea()) + " "); 81 } 82 System.out.println(""); 83 System.out.println("Sum of area:"+String.format("%.2f", all)); 84 } 85 86 } 87 88 89 }
7-6 实现图形接口及多态性,此题需要在7-5的基础上使用类接口并调用即可完成题目要求,按照题目要求写出代码如下:
1 import java.util.ArrayList; 2 import java.util.List; 3 import java.util.Scanner; 4 5 6 public class Main { 7 public static void main(String[] args) { 8 Scanner scan = new Scanner(System.in); 9 10 double a = scan.nextDouble(); 11 double b = scan.nextDouble(); 12 double c = scan.nextDouble(); 13 14 if( a <= 0 || b <= 0 || c <= 0 ) { 15 System.out.println("Wrong Format"); 16 } 17 else { 18 GetArea getArea ; 19 getArea = new Circle(a); 20 System.out.println(String.format("%.2f", getArea.area())); 21 22 getArea = new Rectangle(b,c); 23 System.out.println(String.format("%.2f", getArea.area())); 24 25 } 26 } 27 28 29 public interface GetArea { 30 public double area(); 31 } 32 33 static class Circle implements GetArea{ 34 private double radius ; 35 36 public Circle(double radius) { 37 super(); 38 this.radius = radius; 39 } 40 41 public Circle() { 42 // TODO Auto-generated constructor stub 43 } 44 45 public double getRadius() { 46 return radius; 47 } 48 49 public void setRadius(double radius) { 50 this.radius = radius; 51 } 52 53 public boolean validate() { 54 if(this.radius <= 0 ){ 55 return false; 56 } 57 else 58 return true; 59 } 60 61 public double area() { 62 // TODO Auto-generated method stub 63 return radius * radius * Math.PI; 64 } 65 } 66 67 static class Rectangle implements GetArea{ 68 private double width ; 69 private double length; 70 71 public Rectangle(double width, double length) { 72 super(); 73 this.width = width; 74 this.length = length; 75 } 76 77 78 public double getWidth() { 79 return width; 80 } 81 82 public void setWidth(double width) { 83 this.width = width; 84 } 85 86 public double getLength() { 87 return length; 88 } 89 90 public void setLength(double length) { 91 this.length = length; 92 } 93 94 public boolean validate() { 95 if(this.length <= 0 || this.width <= 0 ){ 96 return false; 97 } 98 else 99 return true; 100 } 101 102 @Override 103 public double area() { 104 // TODO Auto-generated method stub 105 return width * length; 106 } 107 } 108 109 110 111 112 113 }
题7-5,7-6均实现了java的多态性,不同的是题7-5是基于继承实现的多态,题7-6是基于接口实现的多态。这两种方法的区别就在于接口和继承的区别:
• 继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。
(3)正则表达式的运用(题目集4(7-1)、题目集5(7-1、7-4)、 题目集6)
题目描述:题目集4(7-1 水文数据校验及处理 ),题目集5(7-1 找出最长的单词-hebust ,7-4 统计Java程序中关键词的出现次数 ),题目集6(7-1 正则表达式训练-QQ号校验,7-3 正则表达式训练-验证码校验,7-4 正则表达式训练-学号校验)。
题目分析:这些题目都需要我们对大量数据进行处理判断,而正则表达式可以提供强大的字符串处理能力,所以我们可以在解决数据存储后利用正则表达式判断数据的合法性。
代码设计: 数据存储利用了list读取,重点在于数据合法性的判断。先判断我们存储的数据形式才方便处理
题目集4(7-1): 2015/8/2 4:00|133.8400|133.070|1.11/1.21|75.780 2015/8/2 6:00|133.840|133.080|11.11/1.11|72.8a0 2015/8/2 8:00|133.830|133.070|1.11/1.11|73.890 2015/8/2 10:00|133.820|133.080|1.11/1.11|74.380 题目集5(7-2): an not need happy suggest 题目集5(7-4): //Test public method public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } 题目集6(7-1): 123456789O
像题目集4(7-1)中每行数据中包含了5种数据不好处理,所以需要对其进行分割处理,这里我运用了正则表达式中的split进行分割,分割效果和代码如下:
1 import java.time.LocalDateTime; 2 import java.util.ArrayList; 3 import java.util.List; 4 import java.util.Scanner; 5 6 7 public class Dail { 8 public static void main(String[] args) { 9 Scanner scan = new Scanner(System.in); 10 11 String str ; 12 List<String> s = new ArrayList<String>(); 13 str = scan.nextLine(); 14 String sub="exit"; 15 int cmp=str.compareTo(sub); 16 while(cmp!=0) 17 { 18 s.add(str); 19 str=scan.nextLine(); 20 cmp=str.compareTo(sub); 21 } 22 23 24 for (int i = 0; i < s.size(); i++) { 25 26 String[] a = s.get(i).split("\\|"); 27 28 String[] col45 = a[3].split("\\/"); 29 String data = "((([1-9]{1}|[1-9][0-9]{1}|[1-9][0-9]{2}|[1-9][0-9]{3})/((([13578]|1[02])/([1-9]|[12][0-9]|3[01]))|(([469]|11)/([1-9]|[12][0-9]|30))|(2/([1-9]|[1][0-9]|2[0-8]))))|((([1-9]{0,1}[0-9]{0,1})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))/2/29)) ([02468]|1[02468]|2[02]):00"; 30 31 // if(!a[0].matches(data)) { 32 // System.out.println("Row:"+(i+1)+",Column:1Wrong Format"); 33 // System.out.println("Data:"+s.get(i)); 34 for(String b : a ) { 35 System.out.println(); 36 } 37 38 } 39 40 41 } 42 43 44 45 }
数据像这样分割后就我们就可以开始数据的合法性判断了,首先是年月日的判断,写出一个匹配年月日的正则表达式来匹配我们数据中的年月日判断合法
其它数据也是一样处理,这样这道题就处理完了。而像题目集5(7-4)中有各种符号我们可以先将我们不需要的字符换成空格,这样就能方便我们切割单词完成题目。
所以合理的运用正则表达式可以帮助我们快速处理字符串,在这些题目中就体现了正则表达式的部分功能: a.匹配字符串某种模式 b.切割字符串 c.替换字符串 d.提取字符串
从这些题目中正则表达式所展现的功能中就可以感受到正则表达式强大的字符串处理能力。正则强!!!
(4)Java 集合框架的运用(题目集5(7-4))
题目描述:编写程序统计一个输入的Java源码中关键字(区分大小写)出现的次数,题目必须使用List、Set或Map中一种或多种,如完全未使用如上接口,不予评分。
题目分析:必须使用接口
代码设计: 题目已经在正则表达式的应用中进行阐述,在这里我们利用list接受存储数据。Collection集合主要有List和Set两大接口,List是元素有序并且可以重复的集合,
Set集合元素并且没有重复对象。
1 public class Dail { 2 public static void main(String[] args) { 3 Scanner scan = new Scanner(System.in); 4 5 String str ; 6 List<String> s = new ArrayList<String>(); 7 str = scan.nextLine(); 8 String sub="exit"; 9 int cmp=str.compareTo(sub); 10 while(cmp!=0) 11 { 12 s.add(str); 13 str=scan.nextLine(); 14 cmp=str.compareTo(sub); 15 } 16 }
ArrayList基于数组来实现集合的功能,其内部维护了一个可变长的对象数组,集合内所有对象存储于这个数组中,并实现该数组长度的动态伸缩。s.add()插入对象,s.get()返回指定位置的值。
三、踩坑心得
(1)string.replace()无效
发现这两者的区别是在一道题中用string.replace()想要替换一个字符串却始终不能成功,代码如下:
1 ss.replaceAll("(?<!:)\\/\\/.*|\\/\\*(\\s|.)*?\\*\\/", "");// 去除注释 2 ss.replaceAll("\"(.*?)\"", "");//去除匹配双引号之间的内容 3 ss.replaceAll("\\p{P}", ",");//去除非单词符号 4 ss.replace(" ", ",");
后改成这样就成功实现了字符串的替换:
1 ss = ss.replaceAll("(?<!:)\\/\\/.*|\\/\\*(\\s|.)*?\\*\\/", "");// 去除注释 2 ss = ss.replaceAll("\"(.*?)\"", "");//去除匹配双引号之间的内容 3 ss = ss.replaceAll("\\p{P}", ",");//去除非单词符号 4 ss = ss.replace(" ", ",");
这是因为replace(char oldChar,char newChar)返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 而生成的,而最开始时没有变量去接受他的返回值,所以才会导致replace的无效。
四、改进建议
(1)无意义变量的定义
水文数据一题中直接定义了一大堆的变量
1 public class Main { 2 public static void main(String[] args) { 3 Scanner scan = new Scanner(System.in); 4 5 LocalDateTime measurDatatime ; 6 double objectWaterlever ; 7 double actualWaterlever ; 8 double objectGateOpening ; 9 double actualGateOpening ; 10 double waterFlow ; 11 12 String str ; 13 List<String> s = new ArrayList<String>(); 14 str = scan.nextLine(); 15 String sub="exit"; 16 int cmp=str.compareTo(sub); 17 while(cmp!=0) 18 { 19 s.add(str); 20 str=scan.nextLine(); 21 cmp=str.compareTo(sub); 22 } 23 24 25 for (int i = 0; i < s.size(); i++) { 26 27 String[] a = s.get(i).split("\\|"); 28 29 String[] col45 = a[3].split("\\/"); 30 String data = "((([1-9]{1}|[1-9][0-9]{1}|[1-9][0-9]{2}|[1-9][0-9]{3})/((([13578]|1[02])/([1-9]|[12][0-9]|3[01]))|(([469]|11)/([1-9]|[12][0-9]|30))|(2/([1-9]|[1][0-9]|2[0-8]))))|((([1-9]{0,1}[0-9]{0,1})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))/2/29)) ([02468]|1[02468]|2[02]):00"; 31 32 if(!a[0].matches(data)) { 33 System.out.println("Row:"+(i+1)+",Column:1Wrong Format"); 34 System.out.println("Data:"+s.get(i)); 35 } 36 37 } 38 39 } 40 }
可以看到5-10行的变量是没有使用过的,定义大量变量最后还未使用,到最后受到干扰的还是自己,写到最后直接迷惑自己写的代码了。应该随写随用的。
(2)代码过于复杂
图形聚合一题中图方便直接定义了year,month,day,每次循环中再更改day类中数据,直接将问题复杂化了。
1 public DateUtil getNextNDays(int n)//取得year-month-day的下n天日期 2 { 3 int year = day.month.year.getValue(); 4 int month = day.month.getValue(); 5 int day1 = day.getValue(); 6 for (int i = 0; i < n; i++) { 7 day1++; 8 Day dayy = new Day(year,month,day1); 9 if (day1 > dayy.getDayOfMonth()) { 10 day1 = 1; 11 month++; 12 if (month > 12) { 13 month = 1; 14 year++; 15 } 16 } 17 } 18 return new DateUtil(year, month, day1); 19 } 20 21 public DateUtil getPreviousNDays(int n)//取得year-month-day的前n天日期 22 { 23 int year = day.month.year.getValue(); 24 int month = day.month.getValue(); 25 int day1 = day.getValue(); 26 for (int i = 0; i < n; i++) { 27 day1--; 28 Day dayyy = new Day(year,month,day1); 29 while (day1 < 1) { 30 month--; 31 if (month < 1) { 32 month = 12; 33 year--; 34 } 35 dayyy = new Day(year,month,day1); 36 day1 += dayyy.getDayOfMonth(); 37 } 38 } 39 return new DateUtil(year, month, day1); 40 } 41
更改后如下:
1 public DateUtil getNextNDays(int n)//取得year-month-day的下n天日期 2 { 3 4 for (int i = 0; i < n; i++) { 5 day.dayIncrement(); 6 // Day dayy = new Day(year,month,day1); 7 if (day.getValue() > day.getDayOfMonth()) { 8 day.setValue(1); 9 month.monthIncrement(); 10 if (month.getValue() > 12) { 11 month.resetMin();; 12 year.yearIncrement(); 13 } 14 } 15 } 16 int year = year.getValue(); 17 int month = month.getValue(); 18 int day1 = day.getValue(); 19 return new DateUtil(year, month, day1); 20 } 21 22 public DateUtil getPreviousNDays(int n)//取得year-month-day的前n天日期 23 { 24 25 for (int i = 0; i < n; i++) { 26 day.dayReduction(); 27 while (day.getValue() < 1) { 28 day.month.monthReduction(); 29 if (month.getValue() < 1) { 30 month.resetMax(); 31 year.yearReduction(); 32 } 33 // dayyy = new Day(year,month,day1); 34 day.setValue(day.getDayOfMonth()); 35 } 36 } 37 int year = year.getValue(); 38 int month = month.getValue(); 39 int day1 = day.getValue(); 40 return new DateUtil(year, month, day1); 41 }
不需要每次循环中传参,简便了代码
五、总结
第二阶段的学习到此结束,学习了正则表达式的使用,类的封装、继承、多态和接口,进一步加深了对这个面向对象课程的理解,但还是有一点蒙,题目会做,简单的封装、继承,多态接口原理也懂,但是如何用语言描述,无法做到,虽然说能写出来更重要,但是对于概念模棱两可,也不是太好,还是需要后期自己的多学习。最重要的是我在做题时想要实现什么功能,需要用到什么函数都是直接翻书,百度,这点需要我能更熟练掌握各类函数的操作方法。目前还是很满意现在上课的这种氛围,但是线上课感觉还是力不从心,一个原因是线上课程知识点多且复杂,二是时间上的冲突,在完成pta上的作业已经耗费了大量的时间,其他的线下课程也有一点的难度,有时不能分配好时间,会焦躁。老实说,每次mooc上的互评作业感觉大家都没有很认真的写,不仅没有提高自己的水平,反而像是一种负担(没有作业提醒,一不小心就错过)。希望我能在下一阶段的学习中能够完美分配时间并提高自己的效率。