后三次大作业总结
一、前言
1.知识点
知识点涉及到正则表达式,继承和多态,类的抽象与封装,构造方法,instanceof操作符,接口,抽象类,内部类,SimpleDateFormat和Date用法,哈希表hashmap等等
知识点总体涉及的面比较广,作业完成之后还需要自己慢慢消化。
2.题量
这三次大作业主要题量较大的还是座机计费和手机-座机计费两题,其余的题题量还是比较少的。
3.难度
座机计费和手机-座机计费两题相对较难,需要考虑的测试点很多,我就是因为有很多测试点没有考虑全而导致失分过多。
剩下的题知识点不是很多,而且题量较少,难度相对较低。
二、设计与分析
1.第六次大作业
7-1 电信计费系列1-座机计费

本次作业的圈复杂较高,圈复杂度主要与分支语句(if、else、,switch 等)的个数成正相关。我的代码使用if-else嵌套过多,从而导致逻辑复杂程度就会增加。这次作业为了图方便,没有涉及其他的一些类,所以主函数写的特别复杂,但后来的作业创建了其他的类,让主函数看起来简洁了不少。下面分析相关代码。
相关源码:
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy.MM.dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm:ss");
boolean flag = true;
sdf1.setLenient(false);
sdf2.setLenient(false);
try
{
Date date1 = sdf1.parse(arr[2]);
Date date2 = sdf2.parse(arr[3]);
Date date3 = sdf1.parse(arr[4]);
Date date4 = sdf2.parse(arr[5]);
}
catch (ParseException e)
{
flag = false;
}
这段代码是判断输入的日期格式是否合法,使用了SimpleDateFormat类用来判断日期格式。setLenient(false)方法是为了严格限制日期转换,例如避免将日期2022.13.32判段为正确格式。
这里还用到了try catch 错误处理,执行规则:首先执行try中的代码 如果抛出异常会由catch去捕获并执行 如果没有发生异常 catch去捕获会被忽略掉 但是不管有没有异常最后都会执行。try 语句使你能够测试代码块中的错误。catch 语句允许你处理错误。throw 语句允许你创建自定义错误。(抛出错误)finally 使你能够执行代码,在 try 和 catch 之后,无论结果如何。
代码流程:
try{
代码块;
代码 throw"字符" //抛出错误
}catch(参数){ //抓住throw抛出的错误
//处理错误并执行
}finally{
//无论try catch结果如何还是继续执行
}
相关源码:
ArrayList<User> userList = new ArrayList<User>();
ArrayList<CallRecord> callRecordList = new ArrayList<CallRecord>();
for(int i=0;i<callRecordList.size();i++)
{
for(int j=i+1;j<callRecordList.size();j++)
{
if(callRecordList.get(j).getCallingNumber().equals(callRecordList.get(i).getCallingNumber())&&callRecordList.get(j).getAnswerNumber().equals(callRecordList.get(i).getAnswerNumber())&&callRecordList.get(j).getEndTime().equals(callRecordList.get(i).getEndTime())&&callRecordList.get(j).getStartTime().equals(callRecordList.get(i).getStartTime()))
{
callRecordList.remove(j);
j--;
}
}
}
for(int i=0;i<callRecordList.size();i++)
{
for(int j=0;j<userList.size();j++)
{
if(callRecordList.get(i).getCallingNumber().equals(userList.get(j).getNumber()))
{
if(callRecordList.get(i).getAnswerAddressAreaCode().matches("0791"))
{
userList.get(j).getUserRecords().addCallingInCityRecords(callRecordList.get(i));
}
else if(callRecordList.get(i).getAnswerAddressAreaCode().matches("0701")||callRecordList.get(i).getAnswerAddressAreaCode().matches("0790")||callRecordList.get(i).getAnswerAddressAreaCode().matches("079[2-9]"))
{
userList.get(j).getUserRecords().addCallingInProvinceRecords(callRecordList.get(i));
}
else
{
userList.get(j).getUserRecords().addCallingInLandRecords(callRecordList.get(i));
}
}
// if(callRecordList.get(i).getAnswerNumber().equals(userList.get(i).getNumber()))
// {
//
// }
}
}
ArrayList
相关源码:
Collections.sort(userList, new Comparator<User>() {
@Override
public int compare(User o1,User o2) {
if(o1.getNumber().compareTo(o2.getNumber())>0)
return 1;
else if(o1.getNumber().compareTo(o2.getNumber())<0)
return -1;
return 0;
};
});
由于输出结果需要用字符串大小排序,这里是重写了ArrayList中sort方法,目的是将按字符串从小到大输出。
Collections.sort方法排序的规则是 :调用Comparator.compare()方法依次传入两个参数 参数1,参数2。根据返回的值从小到大正序输出,如果返回值小于0那么就将参数1排序在参数2前,如果大于0就将参数1排序在参数2后,如果等于0位置不动。Collections.sort只根据我们返回的值进行排序,在compare方法中可以按照自己的规则进行计算,决定返回值是多少。
多态测试
相关源码:
interface Container {
public static final double pi=3.1415926;
public abstract double area();
public abstract double volume();
static double sumofArea(Container c[])
{
double s = 0;
for(int i=0;i<c.length;i++)
{
s+=c[i].area();
}
return s;
}
static double sumofVolume(Container c[])
{
double v = 0;
for(int i=0;i<c.length;i++)
{
v+=c[i].volume();
}
return v;
}
}
本题难度不大,创建了一个接口。接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。一个类可以实现多个接口。使用好处:简单、规范性 ,维护、拓展性, 安全、严密性。
2.第七次大作业

本次作业复杂度也比较高,此后为减少复杂度,还需要加强练习。由于上述分析了圈复杂度,所以在此不再累述。
相关源码:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class Handle {
public static void accountOpeningHandle(ArrayList<User> userList,String[] arr)
{
String a3 = "0791[0-9]{7,8}";
String a5 = "1[0-9]{10}";
User u = new User();
if(arr.length==2&&arr[1].matches("0")&&arr[0].matches(a3))
{
u.setNumber(arr[0]);
u.setChargeMode(new LandlinePhoneCharging());
userList.add(u);
}
if(arr.length==2&&arr[1].matches("1")&&arr[0].matches(a5))
{
u.setNumber(arr[0]);
u.setChargeMode(new MobilePhoneCharging());
userList.add(u);
}
}
public static boolean dateFormat(String[] arr,String[] d)
{
for(int i = arr.length-1,j = 3;i>arr.length-5;i--,j--)
{
d[j] = arr[i];
}
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy.MM.dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm:ss");
sdf1.setLenient(false);
sdf2.setLenient(false);
boolean flag = true;
try
{
Date date1 = sdf1.parse(d[0]);
Date date2 = sdf2.parse(d[1]);
Date date3 = sdf1.parse(d[2]);
Date date4 = sdf2.parse(d[3]);
}
catch (ParseException e)
{
flag = false;
}
return flag;
}
public static void callRecordHandle(ArrayList<CallRecord> callRecordList,String[] arr,CallRecord callRecord )
{
String a3 = "0791[0-9]{7,8}";
String a4 = "0[0-9]{9,11}";
String a5 = "1[0-9]{10}";
String a6 = "[0-9]{3,4}";
if(arr.length==6&&arr[0].matches(a4)&&arr[1].matches(a4))
{
if(arr[0].length()==10)
{
callRecord.setCallingAddressAreaCode(arr[0].substring(0,3));
}
else
{
callRecord.setCallingAddressAreaCode(arr[0].substring(0,4));
}
if(arr[1].length()==10)
{
callRecord.setAnswerAddressAreaCode(arr[1].substring(0,3));
}
else
{
callRecord.setAnswerAddressAreaCode(arr[1].substring(0,4));
}
callRecord.setCallingNumber(arr[0]);
callRecord.setAnswerNumber(arr[1]);
}
if(arr.length==7&&arr[0].matches(a4)&&arr[1].matches(a5)&&arr[2].matches(a6))
{
if(arr[0].length()==10)
{
callRecord.setCallingAddressAreaCode(arr[0].substring(0,3));
}
else
{
callRecord.setCallingAddressAreaCode(arr[0].substring(0,4));
}
callRecord.setCallingNumber(arr[0]);
callRecord.setAnswerNumber(arr[1]);
callRecord.setAnswerAddressAreaCode(arr[2]);
}
if(arr.length==7&&arr[0].matches(a5)&&arr[1].matches(a6)&&arr[2].matches(a4))
{
if(arr[2].length()==10)
{
callRecord.setAnswerAddressAreaCode(arr[1].substring(0,3));
}
else
{
callRecord.setAnswerAddressAreaCode(arr[1].substring(0,4));
}
callRecord.setCallingNumber(arr[0]);
callRecord.setAnswerNumber(arr[2]);
callRecord.setCallingAddressAreaCode(arr[1]);
}
if(arr.length==8&&arr[0].matches(a5)&&arr[1].matches(a6)&&arr[2].matches(a5)&&arr[3].matches(a6))
{
callRecord.setCallingNumber(arr[0]);
callRecord.setAnswerNumber(arr[2]);
callRecord.setCallingAddressAreaCode(arr[1]);
callRecord.setAnswerAddressAreaCode(arr[3]);
}
}
public static void handle1(ArrayList<CallRecord> callRecordList,ArrayList<User> userList)
{
for(int i=0;i<userList.size();i++)
{
for(int j=i+1;j<userList.size();j++)
{
if(userList.get(i).getNumber().equals(userList.get(j).getNumber()))
{
userList.remove(j);
j--;
}
}
}
for(int i=0;i<callRecordList.size();i++)
{
for(int j=i+1;j<callRecordList.size();j++)
{
if(callRecordList.get(j).getCallingNumber().equals(callRecordList.get(i).getCallingNumber())&&callRecordList.get(j).getAnswerNumber().equals(callRecordList.get(i).getAnswerNumber())&&callRecordList.get(j).getEndTime().equals(callRecordList.get(i).getEndTime())&&callRecordList.get(j).getStartTime().equals(callRecordList.get(i).getStartTime())&&callRecordList.get(j).getAnswerAddressAreaCode().equals(callRecordList.get(i).getAnswerAddressAreaCode())&&callRecordList.get(j).getCallingAddressAreaCode().equals(callRecordList.get(i).getCallingAddressAreaCode()))
{
callRecordList.remove(j);
j--;
}
}
}
}
public static void handle2(ArrayList<CallRecord> callRecordList,ArrayList<User> userList)
{
for(int i=0;i<callRecordList.size();i++)
{
for(int j=0;j<userList.size();j++)
{
if(callRecordList.get(i).getCallingNumber().equals(userList.get(j).getNumber()))
{
if(callRecordList.get(i).getCallingAddressAreaCode().matches("0791"))
{
if(callRecordList.get(i).getAnswerAddressAreaCode().matches("0791"))
{
userList.get(j).getUserRecords().addCallingFromCityToCityRecords(callRecordList.get(i));
}
else if((callRecordList.get(i).getAnswerAddressAreaCode().matches("0701")||callRecordList.get(i).getAnswerAddressAreaCode().matches("0790")||callRecordList.get(i).getAnswerAddressAreaCode().matches("079[2-9]")))
{
userList.get(j).getUserRecords().addCallingFromCityToProvinceRecords(callRecordList.get(i));
}
else
{
userList.get(j).getUserRecords().addCallingFromCityToLandRecords(callRecordList.get(i));
}
}
else if(callRecordList.get(i).getCallingAddressAreaCode().matches("0701")||callRecordList.get(i).getCallingAddressAreaCode().matches("0790")||callRecordList.get(i).getCallingAddressAreaCode().matches("079[2-9]"))
{
userList.get(j).getUserRecords().addCallingInProvinceRecords(callRecordList.get(i));
}
else
{
userList.get(j).getUserRecords().addCallingInLandRecords(callRecordList.get(i));
}
}
if(callRecordList.get(i).getAnswerNumber().equals(userList.get(j).getNumber()))
{
if(callRecordList.get(i).getAnswerAddressAreaCode().matches("0701")||callRecordList.get(i).getAnswerAddressAreaCode().matches("079[0-9]"))
{
userList.get(j).getUserRecords().addAnswerInProvinceRecords(callRecordList.get(i));
}
else
{
userList.get(j).getUserRecords().addAnswerInLandRecords(callRecordList.get(i));
}
}
}
}
}
}
为避免主函数过于复杂,因此创建了Handle类用来处理用户输入字符串,判断格式是否正确,以及解析字符串,便于后续存储字符串信息。
相关源码:
public static void userListSort1(ArrayList<User> userList)
{
Collections.sort(userList, new Comparator<User>() {
@Override
public int compare(User o1,User o2) {
if(o1.getChargeMode() instanceof LandlinePhoneCharging||o2.getChargeMode() instanceof MobilePhoneCharging)
return -1;
else if(o2.getChargeMode() instanceof LandlinePhoneCharging||o1.getChargeMode() instanceof MobilePhoneCharging)
return 1;
return 0;
};
});
}
public static void userListSort2(ArrayList<User> userList)
{
Collections.sort(userList, new Comparator<User>() {
@Override
public int compare(User o1,User o2) {
if(o1.getNumber().compareTo(o2.getNumber())>0)
{
return 1;
}
else if(o1.getNumber().compareTo(o2.getNumber())<0)
{
return -1;
}
return 0;
};
});
}
由于新添了一个手机计费,所以输出格式也发生变化。之前重写一次sort方法我发现并不适用,因此我将sort方法重写了两次。第一次是按字符串大小排序,第二次按座机和手机来排序,结果也返回题目输出顺序。这里用到了instanceof操作符,x instanceof A : 检测x是否是类A的实例对象,或者A的子类创建的实例对象,返回值为boolean型。使用情景:为了避免在向下转型时出现ClassCastException的异常,通常在向下转型之前,先进行instanceof的判断,若返回true,就可以向下转型。如果x instanceof A 返回true,且x instanceof C 也返回true,则类A是类C的父类,或者类C是类A的父类。
sdut-Collection-sort--C~K的班级(II)
这里的作业用到了HashMap<K,V>,HashMap是一个散列表,它存储的内容是键值对(key-value)映射。HashMap实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null。HashMap不会记录插入的顺序,即它是无序的。
3.第八次大作业
电信计费系列3-短信计费
本次作业相较于前两次计费作业较为简单,前面大致框架可以使用,在写本次作业由于没有理解计费方式,导致很多测试点没有通过,还好最后及时发现。
编写一个类Shop(商店)、内部类InnerCoupons(内部购物券)
本次作业使用了新知识内部类。其中 InnerCoupons为内部类。
相关源码:
class Shop {
private int milkCount;
InnerCoupons coupons50 = new InnerCoupons(50);
InnerCoupons coupons100 = new InnerCoupons(100);
public int getMilkCount() {
return milkCount;
}
public void setMilkCount(int milkCount) {
this.milkCount = milkCount;
}
public class InnerCoupons
{
public int value;
InnerCoupons(int value)
{
this.value = value;
}
public void buy()
{
System.out.println("使用了面值为"+value+"的购物券进行支付");
System.out.println("牛奶还剩"+(getMilkCount()-value/50)+"箱");
setMilkCount(getMilkCount()-value/50);
}
}
}
这里使用的是普通内部类,这个是最常见的内部类之一了,其定义也很简单,在一个类里面作为类的一个字段直接定义就可以了。内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象那个的信息相互独立;在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类;方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
三、采坑心得
一定要创建好类,弄清类与类之间的联系。平时需要注意一些小的细节,有时这是这些导致整个代码运行结果不对。
四、改进建议
学习减少圈复杂度的方法,不断改善和优化自己的代码。多使用面向对象设计原则,开闭原则、里氏代换原则、迪米特原则(最少知道原则)、单一职责原则、接口分隔原则、依赖倒置原则、组合/聚合复用原则。学习他人优秀的设计方法,不断使用面向对象。
五、总结
通过这几次大作业,我最有体会的一点是要多去查找相关资料和信息,有很多现成的方法和代码。在查找信息的同时,还有将其转换成自己的知识。平时真的需要多加练习写代码,这也是学习Java一个不断去试错的过程,从而去优化和完善自己代码。当然我也从中学习到了SimpleDateFormat类如何使用;了解了try catch 错误处理,执行规则;如何对ArrayList中sort方法的重写;还有内部类使用方法及好处等等。