OO第二次博客作业
一,前言:总结三次题目集的知识点、题量、难度等情况
这三次题目集重点考察:正则表达式(题目集四的水文数据校验及处理,题目集五的统计Java程序中关键字的出现次数,题目集六的1-4题),有关字符串的应用(题目集五的1-3题),有关继承,多态的应用(题目集四的日期问题面向对象设计(聚合一),图形继承,题目集五的日期问题面向对象设计(聚合二),题目集六的实现图形接口及多态性)。
总体来说题量并不是很大,难度的话,题目集四的水文数据校验及处理比较难,总体的代码思路比较难,对于数据的处理比较复杂,我也是花了三天时间才解决这道题目,其他的题在第一天就解决了,有两个知识点需要对正则表达式的灵活运用才能通过。题目集五也是只有统计Java程序中关键字的出现次数比较难,其中的一个难点我到题目集结束还没有发现,其实这是老师的问题,我感觉那个点并不是很考验我们对正则表达式的使用,当却要花很多时间,有点得不偿失,对这个题目集来说应该增加题量,减去那几个无情的测试点(个人见解)。
二,设计与分析:
1,题目集4(7-2)、题目集5(7-4)两种日期类聚合设计的优劣比较:
首先分析两者的类图:第一张图片为聚合一的类图,他的主要调用方法是通过在一个类中生成另一个类的对象,然后层层叠加,就可以实现调用各个类的方法了,就像聚 合一中,通过在DateUtil中生成Day的对象,在Day的类中生成Month的对象,在Month类中生成Year的对象,就可以做到在DateUtil类中调用各个类的方法了。
第二张图片为聚合二的类图,他的主要调用方法主要是通过在DateUtil类中生成Day类的对象,Month类的对象,Year类的对象。就可以实现对各 个类中方法的调用了。


其次再来分析这两个的圈复杂度:分析两者的圈复杂度可知,两者的圈复杂度可以说是一模一样,差距十分小,代码分析可知,我这两题的解题方式用的都是差不多方 法,再加上调用方式差距不是很大,所以才会出现这样的结果。

总体来说,我还是认为聚合二的方法比较好,因为Java的单一原则,一个类或者一个方法往往只实现一种或者一个功能,但聚合一的调用方法明显不符合这个原则,况且,在一个类出了问题之后,会牵连整个程序。但聚合二明显要好很多。
2,题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计的思路与技术运用(封装、继承、多态、接口等)
首先分析三者的类图:第一种图形继承采用逐层递进的方式来继承,先创建一个Shape类,其中有一个getArea方法,后面在写两个类Rectangle,Circle来继承Shape类,增加Shape类中没有的有关矩形和圈的属性,并把他们封装,创建有关的set和get方法以供调用。最后创建两个类Box和Ball继承Rectangle和Circle类,重写有关的方法,增加相应的属性。
第二种图形继承采用创建一个抽象类GetArea接口,其中只有一个getArea方法,用Circle类和Rectangle类去继承抽象类,根据需求增加相应的属性和重写getArea方法,封装其属性,直接计算面积,这题还是比较简单的。
第三种图形继承和第二种有些相似,采用创建一个抽象类Shape接口,无属性,有抽象方法。根据需求创建三个类Triangle,Circle,Rectangle,在三个类中分别增加相应的属性,重写计算面积的方法,增加其他相应的方法,供主函数调用。



如果类图所证明的不够全面, 再来分析他们的的圈复杂度:其实从类图也可以看出他们的复杂度大小,不过相差不大(这其中还包括了它们由于问题不同所带来的差距)。

3,对三次题目集中用到的正则表达式技术的分析总结:
根据三次题目集所总结正则表达式使用方法
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/* 这两个类是必要的 */
public class MatchesTest {
public static void main(String[] args) {
//matches()
Pattern pattern = Pattern.compile("在这里填入你需要的正则表达式");
Matcher matcher = pattern.matcher("在这里填入被匹配的字符串");
System.out.println(matcher.matches()); // 尝试将整个字符序列与该模式匹配
//find()
Matcher matcher = pattern.matcher("另一个字符串");
System.out.println(matcher.find()); // 查找匹配该模式的下一个子序列
//group()
while(matcher.find()){
System.out.println(matcher.group());
}
//group()2
while(matcher.find()){
System.out.println(matcher.group(0)); // 等价于matcher.group()
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
}
}
4,题目集5(7-4)中Java集合框架应用的分析总结
三个题目集中主要运用到的集合有List,Set,Map,
1,List接口:
常用方法
void add(int index, E element):向指定位置处添加元素,无返回值类型 get(int index):根据下标获取对应的元素,返回值类型为:Object indexOf(Object o) :返回指定元素对应的第一次出现下标,若没有返回-1,返回值类型为:int E remove(int index) :移除指定下标处对应的元素 E set(int index, E element):向指定位置处添加元素并且覆盖原来的元素,更改
ArrayList的增删改查操作:
import java.util.ArrayList; public class testArrayList { /** * 集合框架 * 集合:一个对象,用来储存其他多个对象 * ArrayList 能更好的代替数组 * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ArrayList<String> list = new ArrayList<String>(); //新增 list.add("今天周日"); list.add("明天周一"); list.add(0, "昨天周六"); //修改 list.set(1, "今天周日不上晚自习"); System.out.println(list); //查找 System.out.println(list.get(1)); //删除 System.out.println(list.remove(2)); //遍历 for (String string : list) { System.out.println(string); } } }
2,Set接口:
无序无下标,元素不可以重复
set接口的方法都是从Collection接口中继承而来,
add(E e) :向集合中添加一个元素,返回值类型为:boolean contains(Object o):判断集合中是否包含某个元素,返回值类型为:boolean remove(Object o): 移除某一个元素 ,返回值类型为:boolean size() :返回集合中的有效元素个数,返回值类型为:int
HashSet实现类的简单增删改查以及常用方法:
package test; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; public class test { /** * Set 元素无顺序 内容不可重复 * 方法全部继承自Collection接口 * 遍历:迭代遍历或者for-each遍历 */ public static void main(String[] args) { //set中判断元素不重复值的是元素内容不重复 //Set<String> set = new HashSet<>(); Set<String> set = new LinkedHashSet<>(); String s1 = new String("aaa"); String s2 = new String("bbb"); String s3 = new String("ccc"); String s4 = new String("aaa"); set.add(s1); set.add(s2); set.add(s3); set.add(s4); //forEach遍历 for (String string : set) { System.out.print(string+"\t"); } System.out.println(); //删除操作 set.remove(s3); //看set中是否包含一个对象 System.out.println(set.contains(s4)); //再次添加 System.out.println(set.add("aaa")); //迭代遍历 Iterator<String> it = set.iterator(); while(it.hasNext()){ System.out.print(it.next()+"\t"); /*Object o = it.next(); System.out.println(o);*/ } } }
3,Map集合
Map集合的一个元素是由一个键(key)和一个值(value)组成的
key不允许重复,value允许重复
Map集合常用方法:
package test; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class testMap { public static void main(String[] args) { // TODO Auto-generated method stub Map<String, Integer> map = new HashMap<String, Integer>(); //增 map.put("a", 1); map.put("b", 2); map.put("c", 3); map.put("d", 4); map.put("e", 5); //查 System.out.println(map.get("c")); //改 map.put("c", 33); System.out.println(map.get("c")); //删 map.remove("d"); //遍历 System.out.println(map); //查找map中是否包含指定的key System.out.println(map.containsKey("e")); //查找map中是否包含指定的value map.containsValue("5"); //长度 System.out.println(map.size()); System.out.println(); //遍历key System.out.println("遍历key"); Set<String> set = map.keySet(); for (String string : set) { Integer val = map.get(string); System.out.print(string+"\t"+val+"\n"); } System.out.println(); //遍历value System.out.println("遍历value:"); Collection<Integer> values = map.values(); for (Integer integer : values) { System.out.print(integer+"\t"); } System.out.println(); System.out.println("遍历键值对:"); Set<Entry<String,Integer>> set2 = map.entrySet(); for (Entry<String, Integer> entry : set2) { System.out.println(entry); } } }
(部分分析来源于csdn)
4,Java集合框架图

三,踩坑心得:
1,题目集四
只有第一道题有点坑,其他两道题还好。第一题的坑只要在两个测试点:
![]()
![]()
第一个测试点思考了很久,最后用正则表达式去测试才发现了这个点,匹配空字符,按照题目指导书上给的提示过了这个点。
if (str.matches("^\\s*|\\s*$")) { System.out.println("Max Actual Water Level:0.00"); System.out.println("Total Water Flow:0.00"); System.exit(0); }
第二个测试点则是我的不小心所造成的,没有看到题目指导书上的提示,忘记了加上Wrong Format,使我达到满分推迟了一天。
if (sign != 4) { System.out.println("Wrong Format"); System.out.println("Data:" + dates[i]); sign1++; continue; }
2,题目集五
唯一的坑就是这个,这个点在另外一个点中间,我整日苦思冥想,却终不得解,后面才听说有这样的一回事,满分的朋友说要在那两个点中不断的卡答案,也不知怎么就满分了。
![]()
3,题目集六我感觉是没有什么坑的,我虽然不是一遍过的,但也就提交了几次就满分了,就这个点卡了我一下,主要是没明白其中的关系,后面改进了就满分了。
![]()
四,改进建议:
1,题目集四的第一题
我感觉我的编码没有体现出类的单一原则,主要是实现方面有点困难,我在数据处理的那个类中就已经将结果计算出来了。类HydrologicalInfo几乎没有被使用,只是做简单的传输数据,并没有实现它应有的功能。在数据处理的那个类中,我的计算方法有些冗余,加入了太多C语言的元素进去,不符合Java的单一原则。应该在类HydrologicalInfo中完成数据的处理,在传输到DealData类中计算,这样程序才有更好的可读性和效率,并且在出错的时候也比较容易修改。
import java.time.LocalDateTime; import java.util.Scanner; import java.util.regex.Pattern; class HydrologicalInfo { LocalDateTime measureDateTime; private double objectWaterLevel; private double actualWaterLevel; private double objectGateOpening; private double actralGateOpening; private double waterFlow; public HydrologicalInfo() { } public HydrologicalInfo(double objectWaterLevel, double actualWaterLevel, double objectGateOpening, double actralGateOpening, double waterFlow) { this.objectWaterLevel = objectWaterLevel; this.actualWaterLevel = actualWaterLevel; this.objectGateOpening = objectGateOpening; this.actralGateOpening = actralGateOpening; this.waterFlow = waterFlow; } public double getObjectWaterLevel() { return objectWaterLevel; } public void setObjectWaterLevel(double objectWaterLevel) { this.objectWaterLevel = objectWaterLevel; } public double getActualWaterLevel() { return actualWaterLevel; } public void setActualWaterLevel(double actualWaterLevel) { this.actualWaterLevel = actualWaterLevel; } public double getObjectGateOpening() { return objectGateOpening; } public void setObjectGateOpening(double objectGateOpening) { this.objectGateOpening = objectGateOpening; } public double getActralGateOpening() { return actralGateOpening; } public void setActralGateOpening(double actralGateOpening) { this.actralGateOpening = actralGateOpening; } public double getWaterFlow() { return waterFlow; } public void setWaterFlow(double waterFlow) { this.waterFlow = waterFlow; } } class CheckData { private String data = new String(); private int row; int flag = 0, flag1 = 0, flag2 = 0, flag3 = 0,flag4 = 0; int [][] arr = new int[3][2]; HydrologicalInfo hydrologicalInfo = new HydrologicalInfo(); public void setDate(String data, int row) { this.data = data; this.row = row; } public CheckData(String data, int row) { this.data = data; this.row = row; } public CheckData() { } public int getRow() { return row; } public void setRow(int row) { this.row = row; } public String getData() { return data; } public void setData(String data) { this.data = data; } public boolean validateData(String str) { flag4 = 0; if (flag1 == 2 && flag2 == 2 && flag3 == 2&&flag == 1) { flag = flag1 = flag2 = flag3 = 0; return true; } System.out.println("Data:" + str); flag = flag1 = flag2 = flag3 = 0; return false; } public boolean validateMeasureDateTime(String measuredatetime, int i, int j) { int[] month = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (i == 1 && Pattern.matches("[1-9][0-9]{0,3}(\\-|\\/|.)([1-9])?(1[0-2])?\\1\\d{1,2}", measuredatetime)) { String s1 = measuredatetime.substring(measuredatetime.lastIndexOf("/") + 1); String s2 = measuredatetime.substring(measuredatetime.indexOf("/") + 1, measuredatetime.lastIndexOf("/")); String s3 = measuredatetime.substring(0, measuredatetime.indexOf("/")); if (Integer.parseInt(s3) % 4 == 0 && Integer.parseInt(s2) % 100 != 0 || Integer.parseInt(s2) % 400 == 0) { month[2] = 29; } if (Integer.parseInt(s1) <= month[Integer.parseInt(s2)]) { flag1++; return true; } }else if (i == 1 && Pattern.matches("[02468]:00|(1[02468]):00|2[02]:00", measuredatetime)) { flag1++; return true; } flag4 ++; if(flag4 == 1) { System.out.println("Row:" + j + ",Column:" + i + "Wrong Format"); } return false; } public boolean validateWaterLevel(String waterLevel, int i, int j) { if (i == 2 && Pattern.matches("[1-9][0-9]{0,2}(.[0-9]{1,3})?", waterLevel)) { flag2++; hydrologicalInfo.setObjectWaterLevel(Double.parseDouble(waterLevel)); return true; }else if (i == 3 && Pattern.matches("[1-9][0-9]{0,2}(.[0-9]{1,3})?", waterLevel)) { flag2++; hydrologicalInfo.setActualWaterLevel(Double.parseDouble(waterLevel)); return true; } System.out.println("Row:" + j + ",Column:" + i + "Wrong Format"); return false; } public boolean validateGateOpening(String gateOpening, int i, int j) { if (i == 4 && Pattern.matches("[1-9].[0-9][0-9]", gateOpening)) { flag3++; hydrologicalInfo.setObjectGateOpening(Double.parseDouble(gateOpening)); return true; } else if (i == 5 && Pattern.matches("[1-9].[0-9][0-9]", gateOpening)) { flag3++; hydrologicalInfo.setActralGateOpening(Double.parseDouble(gateOpening)); return true; } System.out.println("Row:" + j + ",Column:" + i + "Wrong Format"); return false; } public boolean validateWaterFlow(String WaterFlow, int i, int j) { if (Pattern.matches("[1-9][0-9]{0,2}(.[0-9]{1,3})?", WaterFlow)) { flag++; return true; } System.out.println("Row:" + j + ",Column:" + i + "Wrong Format"); return false; } } class DealData { private StringBuilder sb; CheckData find = new CheckData(); HydrologicalInfo data = new HydrologicalInfo(); public DealData(StringBuilder sb) { this.sb = sb; } public DealData() { // TODO 自动生成的构造函数存根 } public StringBuilder getSb() { return sb; } public void setSb(StringBuilder sb) { this.sb = sb; } public void computeDate(HydrologicalInfo hydrologicalInfo[]) { } public void getDealDataResult(String dates[], int n) { int num = 0, x = 1, sign1 = 0; double max = 0, amax = 0; for (int i = 1; i < n; i++) { int a = 0; dates[i] = dates[i].trim(); String arr = dates[i]; int sign = 0; for (int j = 0; j < dates[i].length(); j++) { if (arr.charAt(j) == '|') { sign++; } } if (sign != 4) { System.out.println("Wrong Format"); System.out.println("Data:" + dates[i]); sign1++; continue; } String[] str = dates[i].split("\\|"); String[] Time = str[0].split(" "); String[] Opening = str[3].split("\\/"); find.validateMeasureDateTime(Time[0].replaceAll("\\s+", ""), 1, i); find.validateMeasureDateTime(Time[1].replaceAll("\\s+", ""), 1, i); find.validateWaterLevel(str[1].replaceAll("\\s+", ""), 2, i); find.validateWaterLevel(str[2].replaceAll("\\s+", ""), 3, i); find.validateGateOpening(Opening[0].replaceAll("\\s+", ""), 4, i); find.validateGateOpening(Opening[1].replaceAll("\\s+", ""), 5, i); find.validateWaterFlow(str[4].replaceAll("\\s+", ""), 6, i); if (find.validateData(dates[i])) { if (Double.parseDouble(Opening[0]) < Double.parseDouble(Opening[1])) { System.out.println("Row:" + i + " GateOpening Warning"); } data.setWaterFlow(Double.parseDouble(str[4])); amax += Double.parseDouble(str[4]) * 7200; num++; if (max < Double.parseDouble(str[2])) { max = Double.parseDouble(str[2]); } } if (num + 1 == n && sign1 == 0) { System.out.println("Max Actual Water Level:" + String.format("%.2f", max)); System.out.println("Total Water Flow:" + String.format("%.2f", amax)); System.exit(0); } } } private char[] sizeof(String[] str) { return null; } } public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); StringBuilder sb = new StringBuilder(); String str = input.nextLine(); String s = "exit"; int n = 0; while (str.equals(s) == false) { sb.append("\n" + str); n++; str = input.nextLine(); } n++; str = sb.toString(); if (str.matches("^\\s*|\\s*$")) { System.out.println("Max Actual Water Level:0.00"); System.out.println("Total Water Flow:0.00"); System.exit(0); } String[] dates = str.split("\n"); DealData dD = new DealData(); dD.getDealDataResult(dates, n); } }
2,题目集五的第四题
我得到数据和储存数据用的都是结构体,到最后才将其转为map进行排序,输出。其实本来全部都是由结构体完成操作,但由于老师的要求不得不改成map,list,set中的一个,由于map的天然优势,拿来就用,所以选择了map,改进的点在于数据的得到与储存都可以用map来操作。
import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; class S { String str = ""; int num = 0; // 这里有必要可以加入get、set方法 } public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); S[] jiamap = new S[53]; for (int i = 0; i < 53; i++) { jiamap[i] = new S(); } // 使数据有序 LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>(); StringBuilder str = new StringBuilder(); for (int i = 0;; i++) { String ss = input.nextLine(); ss = ss.replaceAll("\"(.*)\"", " aaa "); if (ss.equals("exit")) { break; } if (ss.matches("(.*)//(.*)")) {// 注释不能有 String[] data = ss.split("//"); str.append(data[0] + " "); } else { str.append(ss); } } String s = str.toString(); if (s.equals("")) { System.out.print("Wrong Format"); System.exit(0); } s = s.replaceAll("/\\**(.*?)/", " "); s = s.replaceAll("=", "a"); s = s.replaceAll("\"(.*)\"", " aaa "); s = s.replaceAll("[^a-zA-Z]", " "); //s = s.replaceAll("(\\s)?\\W(\\s)?", " "); //System.out.println(s); String[] s2 = { "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while" }; String[] s1 = s.split(" "); int num = 0; for (int i = 0; i < s2.length; i++) { for (int j = 0; j < s1.length; j++) { if (s2[i].equals(s1[j])) { jiamap[i].str = s2[i]; jiamap[i].num++; } } } for (int i = 0; i < 53; i++) { if (jiamap[i].num == 0) { continue; } map.put(jiamap[i].str, jiamap[i].num); } // 先转成ArrayList集合 ArrayList<Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); // 从小到大排序(从大到小将o1与o2交换即可) Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { return (o1.getKey().compareTo(o2.getKey())); } }); // 新建一个LinkedHashMap(有序),把排序后的List放入 LinkedHashMap<String, Integer> map2 = new LinkedHashMap<>(); for (Map.Entry<String, Integer> entry : list) { map2.put(entry.getKey(), entry.getValue()); } // 遍历输出 for (Map.Entry<String, Integer> entry : map2.entrySet()) { System.out.println(entry.getValue() + "\t" + entry.getKey()); } System.exit(0); } }
五,总结:
本次的三个题目集中正则表达式题目让我对于正则表达式有了更加熟练的使用,图形与日期类的继承,多态的使用使我对Java的单一原则有了更深的理解,这就是Java和C语言的区别。java面向对象的特征主要有封装,继承,多态。类的继承关系是单一的、非多重的,一个子类只有一个父类,子类的父类又只有一个父类。C语言支持“运算符的重载”,这是它的一个很重要的多态特征,是数据抽象和泛型编程的利器。它允许直接对对象进行四则运算,正像基本数据类型那样,Java不支持这种多态机制,也是为降低复杂性。 Java可以支持方法重载和重写,所谓重载就是一个类具备多个相同属性行为;重写就是在继承关系中父类的行为在不同的子类上有不同的实现。在Java中比C语言多了一些修饰符,如访问修饰符Public(公共的)、Private(私有的)、Protected(受保护的)、默认。 从目前的学习,我认为Java更注重结构的运用,每一项分的很细,单一原则紧紧围绕着Java程序,这是目前老师让我们学习的重点,随着学习的深入,以后对Java会有更深的理解。
我们的Java老师还是很不错的,能听取学生的意见,改进题目集的难度和题量,希望以后都是这个题量和难度,并且不要加一些莫名其妙的测试点。
NCHU软件20201228-杨鹏程
浙公网安备 33010602011771号