对Java第四-六周学习内容及三次作业总结

一、前言概括

Java学习过程中的问题和概括:
1.这几周的课程学习重点是面对对象技术的三大特点:封装、继承、多态。
2.自学内容为:接口、hashmap函数的使用,以及对正则表达式的进一步学习。
3.问题:几次实验和PTA题目集的出现的语法问题和对一些Java提供的类的使用遇见的问题。
此次题目集第4-5周的习题题目难度适中,都有一题需要使用正则表达式进行对数据的进行处理的题目,
但很遗憾依旧不能拿到满分,而第六次的作业题目集较为简易,此外题目的量增加了一些。

二、题目集问题分析

第四次题目集1.水文数据校验及处理

public class Main {
public static void main(String[] agrs) {
	DealDate dealDate = new DealDate();
	dealDate.getDealResult();
	}
}

这里是主类,直接定义了一个DealDate类型的对象,在直接获取结果,在getDealResult中把所有输出写好。

import java.time.LocalDateTime;

public class HydrologicalInfo {//信息类,存储所有水文信息
	private LocalDateTime measureDateTime;
	private double actualWaterLevel;
	private double objectWaterLevel;
	private double actualGateOpening;
	private double objectGateOpening;
	private double waterFlow;
	public HydrologicalInfo() {
		super();
		// TODO Auto-generated constructor stub
	}
	public HydrologicalInfo(LocalDateTime measureDateTime, double actualWaterLevel, double objectWaterLevel,
			double actualGateOpening, double objectGateOpening, double waterFlow) {
		super();
		this.measureDateTime = measureDateTime;
		this.actualWaterLevel = actualWaterLevel;
		this.objectWaterLevel = objectWaterLevel;
		this.actualGateOpening = actualGateOpening;
		this.objectGateOpening = objectGateOpening;
		this.waterFlow = waterFlow;
	}
	public LocalDateTime getMeasureDateTime() {
		return measureDateTime;
	}
	public void setMeasureDateTime(LocalDateTime measureDateTime) {
		this.measureDateTime = measureDateTime;
	}
	public double getActualWaterLevel() {
		return actualWaterLevel;
	}
	public void setActualWaterLevel(double actualWaterLevel) {
		this.actualWaterLevel = actualWaterLevel;
	}
	public double getObjectWaterLevel() {
		return objectWaterLevel;
	}
	public void setObjectWaterLevel(double objectWaterLevel) {
		this.objectWaterLevel = objectWaterLevel;
	}
	public double getActualGateOpening() {
		return actualGateOpening;
	}
	public void setActualGateOpening(double actualGateOpening) {
		this.actualGateOpening = actualGateOpening;
	}
	public double getObjectGateOpening() {
		return objectGateOpening;
	}
	public void setObjectGateOpening(double objectGateOpening) {
		this.objectGateOpening = objectGateOpening;
	}
	public double getWaterFlow() {
		return waterFlow;
	}
	public void setWaterFlow(double waterFlow) {
		this.waterFlow = waterFlow;
	}
	
}

设计这个类的目的是,为了在分割好数据的时候将数据存储好,便于后续数据的获取和设置

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;

public class DealDate {
	private List<CheckDate> list = new ArrayList<CheckDate>();
	private int cnt = 0;
	private static List<String> list2;

	public DealDate() {
		Scanner input = new Scanner(System.in);
		String tmpStr = input.nextLine();
		int row = 1;
		while (tmpStr.compareTo("exit") != 0) {//判断何时停止输入
			list.add(new CheckDate(tmpStr, row));
			tmpStr = input.nextLine();
			row++;//计入输入数据行数
		}
		this.cnt = row - 1;
	}

	void getDealResult() {
		Iterator<CheckDate> it = list.iterator();
		double max = 0;
		double sum = 0;
		boolean flag = false;

		while (it.hasNext()) {
			CheckDate tmp = it.next();

			if (tmp.validateDateData()) {//检测输入数据合法性
				flag = true;
			}
			list2 = tmp.getList();
		}
                //若flag为判断是否存在非法输入的情况
                //list2是存储着非法输入的信息
		if (flag) {//输出内容判断
			Iterator<CheckDate> it2 = list.iterator();
			while (it.hasNext()) {
				CheckDate tmp = it2.next();
				HydrologicalInfo tmp2 = tmp.toHydrologicalInfo();
				if (tmp2.getActualGateOpening() > tmp2.getObjectGateOpening()) {
					System.out.println("Row:" + tmp.getRow() + " GateOpening Warning");
				}
				if (tmp2.getActualWaterLevel() > max) {//查找数最大的实际水位
					max = tmp2.getActualWaterLevel();
				}
				sum += tmp2.getWaterFlow();//计算水的总量
			}
		} else {//若存在非法输入,则输出检测出来的非法输入
			Iterator<String> it3 = list2.iterator();
			while (it3.hasNext()) {
				String tempString = it3.next();
				if (it3.hasNext()) {
					System.out.println(tempString);
				} else {
					System.out.print(tempString);
				}
			}
		}

	}
}

该类的设计的目的就是对每行的数据的处理,主要进行检查输入合法性校验并若合法则判断输出内容,
由于是分行输入,而我采用了先将输入全部存入Arraylist中在进行判断,由于是每行一旦有输入合
法性错误的就需要输出该行信息,这样的话就需要先把判断得到信息进行存储,判断后然后一起输出。

import java.time.LocalDateTime;
import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;

public class CheckDate {// 输入合法性校验
	private String data;
	private int row;
	private int totalCoulmn;
	private int year;
	private int month;
	private int day;
	private int hour;
	private int min;
	private double actualWaterLevel;
	private double objectWaterLevel;
	private double actualGateOpening;
	private double objectGateOpening;
	private double waterFlow;
	private static List<String> list = new ArrayList<String>();

	public CheckDate() {
		super();
		// TODO Auto-generated constructor stub
	}

	public CheckDate(String data, int row) {
		super();
		this.data = data;
		this.row = row;
	}

	public List getList() {
		return list;
	}

	public String[] getData() {
		// String format = "([1-9][0-9]{0,3}/[1-9][0-9]?/[1-9][0-9]?
		// [0-9][0-9]?:[0-9][0-9]?)\\|(([1-9][0-9]{0,2}(\\.[0-9]{1,3})?)|([1-9][0-9]{0,2}))\\|(([1-9][0-9]{0,2}(\\.[0-9]{1,3})?)|([1-9][0-9]{0,2}))\\|([0-9]\\.[0-9][0-9])/([0-9]\\.[0-9][0-9])\\|([1-9][0-9]{0,2}(\\.[0-9]{1,3})?)|([1-9][0-9]{0,2})";
		String string[] = Pattern.compile("\\|").split(this.data);// 根据|分割数据
		this.totalCoulmn = string.length;
		for (int i = 1; i < string.length; i++) {
			string[i] = string[i].trim();// 去首位括号
		}
		return string;
	}

	public void setData(String data) {
		this.data = data;
	}

	public int getRow() {
		return row;
	}

	public void setRow(int row) {
		this.row = row;
	}

	public boolean validateDateData() {// 对数据进行校验
		String[] string = this.getData();
		String[] tmpStr = string[3].split("/");
		boolean flag = true;
		if (this.totalCoulmn != 5) {
			list.add("Wrong Format");
			list.add("Data:" + this.data);
			flag = false;
		} else {
			if (this.validDateMeasureDateTime(string[0]) == false) {
				list.add("Row:" + this.row + ",Column:1Wrong Format");
				flag = false; // Row:1,Column:2Wrong Format
			}
			if (this.validateWaterLevel(string[1]) == false) {
				list.add("Row:" + this.row + ",Column:2Wrong Format");

				flag = false;
			} else {
				this.objectWaterLevel = Double.parseDouble(string[1]);
			}
			if (this.validateWaterLevel(string[2]) == false) {
				list.add("Row:" + this.row + ",Column:3Wrong Format");

				flag = false;
			} else {
				this.actualWaterLevel = Double.parseDouble(string[2]);
			}
			if (this.valiateGateOpening(tmpStr[0]) == false) {
				list.add("Row:" + this.row + ",Column:4Wrong Format");

				flag = false;
			} else {
				this.objectGateOpening = Double.parseDouble(tmpStr[0]);
			}
			if (this.valiateGateOpening(tmpStr[1]) == false) {
				list.add("Row:" + this.row + ",Column:5Wrong Format");
				flag = false;
			} else {
				this.actualGateOpening = Double.parseDouble(tmpStr[0]);
			}
			if (this.validateWaterLevel(string[4]) == false) {
				list.add("Row:" + this.row + ",Column:6Wrong Format");

				flag = false;
			} else {
				this.waterFlow = Double.parseDouble(string[4]);
			}
		}
		if (flag == false) {

			list.add("Data:" + this.data);

		}

		return flag;
	}

	public boolean validDateMeasureDateTime(String measureDateTime) {// 对输入时间部分进行校验
		if (measureDateTime.matches("([1-9][0-9]{0,3}/[1-9][0-9]?/[1-9][0-9]? [1-9][0-9]{0,1}:[0][0])")) {

			String string[] = measureDateTime.split("/");
			this.year = Integer.parseInt(string[0]);
			this.month = Integer.parseInt(string[1]);
			String string2[] = string[2].split(" ");
			this.day = Integer.parseInt(string2[0]);
			String string3[] = string2[1].split(":");
			this.hour = Integer.parseInt(string3[0]);
			this.min = (string3[1].charAt(0) - '0') * 10 + (string3[1].charAt(1) - '0');// 数字转换
			if (hour % 2 != 0) {
				return false;
			}
			return this.checkInputValidity(year, month, day, hour, min);
		}

		return false;
	}

	public static boolean isLeapYear(int year) {
		if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
			return true;
		return false;
	}

	public boolean checkInputValidity(int year, int month, int day, int hour, int min) {// 时间合法性检测
		if (year < 1 || year > 9999 || month < 1 || month > 12 || day < 1 || day > 31 || day > monthDays(year, month)
				|| min > 60 || min < 0 || hour > 24 || hour < 0) {
			return false;
		}
		return true;
	}

	public static int monthDays(int year, int month) {
		int day = 0;
		switch (month) {
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			day = 31;
			break;
		case 2:
			if (isLeapYear(year))
				day = 29;
			else
				day = 28;
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			day = 30;
			break;
		}
		return day;
	}

	public boolean validateWaterLevel(String waterLevel) {// 对水位的合法性检测

		if (waterLevel.matches("([1-9][0-9]{0,2}(\\.[0-9]{1,3})?)|([1-9][0-9]{0,2})")) {
			return true;
		}
		return false;
	}

	public boolean valiateGateOpening(String gateOpening) {// 开度合法性检测
		return gateOpening.matches("([0-9]\\.[0-9][0-9])");
	}

	public HydrologicalInfo toHydrologicalInfo() {
		LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hour, min);
		HydrologicalInfo hydrologicalInfo = new HydrologicalInfo(localDateTime, actualWaterLevel, objectWaterLevel,
				actualGateOpening, objectGateOpening, waterFlow);
		return hydrologicalInfo;
	}
}

该类是用于检测数据合法性的,首先是采用了split函数进行分割数据,根据|分割成5份,
在进行判断若没有5份则说明整体输入格式非法,若为5分则分别对五份进行检测,若都为合法
输入则将数据字符串转化为数值。

还有四个测试点未过,分析了一下可能因为正则表达式并没有检测出所有错误,
比如说在数据中间加空格的情况没有进行排除,同时数据又字符串转化为数值时
出现错误。而且其中一个类并没有用上存在多项问题。

类图:

在设计类的时候准备将checkData用在检测数据,但是为了方便我把数据拆分为5个部分进行分别检测合法性
而此时顺便将各部分转变为数值
dealDate类是为了处理数据,和checkData类为聚合关系
HydrologicalInfo虽然设计成了与checkData类为关联关系但是实际上并没有用到该类

问题:
复杂度较高,而类的多有重复之处,且功能混乱,最好执行类的单一职责原则,
暂且无好的改进思路。

第四次题目集2. 日期问题面向对象设计(聚合一)

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		int option = input.nextInt();
		int year ;
		int month ;
		int day ;
		int year2 ;
		int month2 ;
		int day2 ;
		int n ;
		switch(option) {//操作选择
		case 1://获取N天后的日期
			
			 year = input.nextInt();
			 month = input.nextInt();
			 day = input.nextInt();
			 n = input.nextInt();
			DateUtil dateUtil = new DateUtil(day,month,year);
			if(dateUtil.checkInputVlidity()) {
				DateUtil newDateUtil = dateUtil.getNextNDays(n);
				System.out.println(newDateUtil.showDate());
			}else {
				System.out.println("Wrong Format");
			}
			break;
		case 2://获取前N天的日期
			
			 year = input.nextInt();
			 month = input.nextInt();
			 day = input.nextInt();
			 n = input.nextInt();
			DateUtil dateUtil2 = new DateUtil(day,month,year);
			if(dateUtil2.checkInputVlidity()) {
				DateUtil newDateUtil2 = dateUtil2.getPreviousNDays(n);
				System.out.println(newDateUtil2.showDate());
			}else {
				System.out.println("Wrong Format");
			}
			break;
		case 3://获取两个日期之间相差天数
			 year = input.nextInt();
			 month = input.nextInt();
			 day = input.nextInt();
			 year2 = input.nextInt();
			 month2 = input.nextInt();
			 day2 = input.nextInt();
			 DateUtil dateUtil3 = new DateUtil(day,month,year);
			 DateUtil dateUtil4 = new DateUtil(day2,month2,year2);
			 if(dateUtil3.compareDates(dateUtil4)) {//比较哪个日期更小
				 if(dateUtil4.checkInputVlidity()&&dateUtil3.checkInputVlidity()) {
						int sum = dateUtil4.getDaysOfDates(dateUtil3);
						System.out.println(sum);
					}else {
						System.out.println("Wrong Format");
					}
				 
			 }else {
				 if(dateUtil3.checkInputVlidity()&&dateUtil4.checkInputVlidity()) {
						int sum = dateUtil3.getDaysOfDates(dateUtil4);
						System.out.println(sum);
					}else {
						System.out.println("Wrong Format");
					}
			 }
			
			 break;
			default ://若选择操作越界
				System.out.println("Wrong Format");
		}

	}

}

main类主要控制选择操作和组织类与方法,在case3中由于特殊的函数算法要把日期小的数据放前面。

import java.util.Scanner;

public class DateUtil {
	private Day day;

	public DateUtil() {

	}

	public DateUtil(int d, int m, int y) {
		day = new Day(y, m, d);
	}

	public Day getDay() {
		return day;
	}

	public void setDay(Day day) {
		this.day = day;
	}

	public boolean checkInputVlidity() {//检测年月日输入合法性
		if (day.getMonth().validate() == false || day.getMonth().getYear().validate() == false) {
			return false;
		} else if (day.validate() == false) {
			return false;
		}
		return true;
	}

	public boolean compareDates(DateUtil date) {//比较两个日期大小
		if (day.getMonth().getYear().getValue() > date.getDay().getMonth().getYear().getValue()) {
			return true;
		} else if (day.getMonth().getYear().getValue() < date.getDay().getMonth().getYear().getValue()) {
			return false;
		} else {
			if (day.getMonth().getValue() > date.getDay().getMonth().getValue()) {
				return true;
			} else if (day.getMonth().getValue() < date.getDay().getMonth().getValue()) {
				return false;
			} else {
				if (day.getValue() > date.getDay().getValue()) {
					return true;
				} else {
					return false;
				}
			}

		}
	}

	public boolean equalTwoDates(DateUtil date) {//判断两个日期是否相等
		if (this.compareDates(date) == false && day.getValue() == date.getDay().getValue()) {
			return true;
		}
		return false;
	}

	public String showDate() {//格式化输出
		String temp;
		Integer x = this.day.getMonth().getYear().getValue();
		Integer z = this.day.getMonth().getValue();
		Integer c = this.day.getValue();
		temp = x.toString() + "-" + z.toString() + "-" + c.toString();
		return temp;
	}

	public DateUtil getNextNDays(int n) {//获取N天后日期
		int[] mom_maxmum = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int year = this.day.getMonth().getYear().getValue();
		int month = this.day.getMonth().getValue();
		int day = this.day.getValue();
		int totalDays = 365;
		n = n + day;//这里是调整日期号为0,更好实现月的递加
		DateUtil newday = new DateUtil(0, month, year);
		while (true) {
			if (newday.getDay().getMonth().getYear().isLeapYear()) {
				totalDays = 366;
				mom_maxmum[1] = 29;
			} else {
				totalDays = 365;
				mom_maxmum[1] = 28;
			}
			if (n > mom_maxmum[newday.getDay().getMonth().getValue() - 1]) {
				n -= mom_maxmum[newday.getDay().getMonth().getValue() - 1];
				newday.getDay().getMonth().monthIncrement();
				if (newday.getDay().getMonth().getValue() > 12) {
					newday.getDay().getMonth().resetMin();
					newday.getDay().getMonth().getYear().yearIncrement();
				}
			} else {
				newday.getDay().setValue(n);
				break;
			}
		}
		return newday;

	}

	public DateUtil getPreviousNDays(int n) {
		int[] mom_maxmum = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int year = this.day.getMonth().getYear().getValue();
		int month = this.day.getMonth().getValue();
		int day = this.day.getValue();
		int totalDays = 365;
		n = n - day;//这一段为了使日期号day为上个月最大数,便于后面的日期号直接减去剩余的n
		month--;
		if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) {

			mom_maxmum[1] = 29;
		} else {

			mom_maxmum[1] = 28;
		}
		if(month<1) {
			month = 12;
			year--;
		}
		DateUtil newday = new DateUtil(mom_maxmum[month - 1], month, year);
		while (true) {
			if (newday.getDay().getMonth().getYear().isLeapYear()) {
				totalDays = 366;
				mom_maxmum[1] = 29;
			} else {
				totalDays = 365;
				mom_maxmum[1] = 28;
			}
			if (n > mom_maxmum[newday.getDay().getMonth().getValue() - 1]) {
				n -= mom_maxmum[newday.getDay().getMonth().getValue() - 1];
				newday.getDay().getMonth().monthReduction();
				if (newday.getDay().getMonth().getValue() < 1) {
					newday.getDay().getMonth().resetMax();
					newday.getDay().getMonth().getYear().yearReduction();
				}
			} else {

				int temp = newday.getDay().getValue() - n;
				newday.day.setValue(temp);
				break;
			}
		}
		return newday;

	}

	public int getDaysOfDates(DateUtil date) {//获取两个日期之间的相差天数
		int left = 0;
		int right = 1000;
		int mid = (left + right) / 2;
		DateUtil newday = this.getNextNDays(right);//使用了本类里的获取下N天,所以日期小的数据在前
		while (true) {//保证right的值代入得到的日期比第二个日期大
			if (newday.day.getMonth().getYear().getValue() < date.day.getMonth().getYear().getValue()) {
				left = right;
				right = right * 2;
				newday = this.getNextNDays(right);
			} else {
				break;
			}

		}
		mid = (left + right) / 2;
		newday = this.getNextNDays(mid);
		while (right > left) {//在使用折半查找进行得到符合要求的N
			if (newday.day.getMonth().getYear().getValue() > date.day.getMonth().getYear().getValue()) {
				right = mid - 1;
				mid = (int) ((right + left) / 2);
				newday = this.getNextNDays(mid);
			} else if (newday.day.getMonth().getYear().getValue() < date.day.getMonth().getYear().getValue()) {
				left = mid + 1;
				mid = (right + left) / 2;
				newday = this.getNextNDays(mid);
			} else if (newday.day.getMonth().getValue() > date.day.getMonth().getValue()) {
				right = mid - 1;
				mid = (right + left) / 2;
				newday = this.getNextNDays(mid);
			} else if (newday.day.getMonth().getValue() < date.day.getMonth().getValue()) {
				left = mid + 1;
				mid = (right + left) / 2;
				newday = this.getNextNDays(mid);
			} else if (newday.day.getValue() > date.day.getValue()) {
				right = mid - 1;
				mid = (right + left) / 2;
				newday = this.getNextNDays(mid);
			} else if (newday.day.getValue() < date.day.getValue()) {
				left = mid + 1;
				mid = (right + left) / 2;
				newday = this.getNextNDays(mid);
			} else {
				break;
			}
		}
		return mid;
	}
}

这里对于getNextDays()getPreviousDays()两个函数的算法都是,判断n是否大于该月最大天数,
若是则减去该月的最大天数,直到n<该月最大天数则此时用day加上或减去n而对于函数getDaysOfDates()
则调用了函数getNextDays()函数去找到一个N满足使更小的日期加上N天等于更大的日期。这里我采用了
折半查找,但是提前要设置好right的初始值,使其保证初始值right使得小的日期加上最大值right天数可
以大过最大日期,既保证N一定会在[left,right]的范围内,这样才能通过折半查找找出满足条件的N

public class Year {
	private int value;
	
	public Year() {
		
	}

	public Year(int value) {
		
		this.value = value;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
	public boolean isLeapYear() {//闰年判断
		if(this.value%400 == 0 ||(this.value%4 == 0 &&this.value%100 != 0))
			return true;
		return false;
	}
	public boolean validate() {//合法性判断
		if(this.value>=1900&&this.value<=2050) {
			return true;
		}
		return false;
	}
	public void yearIncrement() {//年份自增
		this.value++;
	}
	public void yearReduction() {//月份递增
		this.value--;
	}
}

public class Month {
	private int value;
	private Year year;
	public Month() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Month(int yearValue, int monthValue) {
	
		this.value = monthValue;
		this.year = new Year(yearValue);
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	public Year getYear() {
		return year;
	}
	public void setYear(Year year) {
		this.year = year;
	}
	public void resetMin() {//重置最小月份
		this.value = 1;
	}
	public void resetMax() {//重置最大月份
		this.value = 12;
	}
	public boolean validate() {//合法性判断
		if(this.value >= 1 && this.value <=12) {
			return true;
		}
		return false;
	}
	public void monthIncrement() {//月份自增
		this.value++;
	}
	public void monthReduction() {//月份自减
		this.value--;
	}
	
	
}


public class Day {
	private  int value;
	private Month month;
	private int []mom_maxmum = {31,28,31,30,31,30,31,31,30,31,30,31};
	
	public Day() {
		
	}
	public Day(int yearValue , int monthValue,int dayValue) {
		this.value = dayValue;
		 month = new Month(yearValue , monthValue);
		
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	public Month getMonth() {
		return month;
	}
	public void setMonth(Month month) {
		this.month = month;
	}
	public void resetMin() {\\最小日设置
		this.value = 1;
	}
	public void resetMax() {\\当月最大天数设置
		Year year = month.getYear();
		if(year.isLeapYear()) {
			mom_maxmum[1]=29;
		}else {
			mom_maxmum[1]=28;
		}
		this.value = mom_maxmum[month.getValue()-1];
	}
	public boolean validate() {\\合法性检测
		Year year = month.getYear();
		if(year.isLeapYear()) {
			mom_maxmum[1]=29;
		}else {
			mom_maxmum[1]=28;
		}
		if(this.value>=1&&this.value<=mom_maxmum[month.getValue()-1]) {
			return true;
		}
		return false;
	}
	public void dayIncrement() {\\日自增
		this.value++;
	}
	public void dayReduction() {\\日自减
		this.value--;
	}
}

DayMonthYear三个类是嵌套的,Year嵌套在Month里,而Month嵌套在Year里,他们之间为组合关系同时DayDateUtil也是组合关系。
类图如下:

负责度分析:

其中DateUtil类的复杂度过高,进而导致main类的负责度也过高。

其中getDaysOfDates()函数复杂度过高,原因当时认为折半查找和采用getNextDays()函数会比暴力循环
查找更优越,虽然是更为优越一点但是其复杂度依旧是比较高。

对比第五次题集5.日期问题面向对象设计(聚合二)

代码如下:

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		int option = input.nextInt();
		int year ;
		int month ;
		int day ;
		int year2 ;
		int month2 ;
		int day2 ;
		int n ;
		switch(option) {//选择操作
		case 1://获取N天后的日期
			
			 year = input.nextInt();
			 month = input.nextInt();
			 day = input.nextInt();
			 n = input.nextInt();
			DateUtil dateUtil = new DateUtil(year,month,day);
			if(dateUtil.checkInputValidity()) {//合法性检测
				DateUtil newDateUtil = dateUtil.getNextNDays(n);
				System.out.println(year+"-"+month+"-"+day+" next "+n+" days is:"+newDateUtil.showDate());
			}else {
				System.out.println("Wrong Format");
			}
			break;
		case 2://获取前N天日期
			
			 year = input.nextInt();
			 month = input.nextInt();
			 day = input.nextInt();
			 n = input.nextInt();
			DateUtil dateUtil2 = new DateUtil(year,month,day);
			if(dateUtil2.checkInputValidity()) {//合法性检验
				DateUtil newDateUtil2 = dateUtil2.getPreviousNDays(n);
				System.out.println(year+"-"+month+"-"+day+" previous "+n+" days is:"+newDateUtil2.showDate());
			}else {
				System.out.println("Wrong Format");
			}
			break;
		case 3://获取两个日期之间相差天数
			 year = input.nextInt();
			 month = input.nextInt();
			 day = input.nextInt();
			 year2 = input.nextInt();
			 month2 = input.nextInt();
			 day2 = input.nextInt();
			 DateUtil dateUtil3 = new DateUtil(year,month,day);
			 DateUtil dateUtil4 = new DateUtil(year2,month2,day2);
			 if(dateUtil3.compareDates(dateUtil4)) {//判断两个日期哪个更小
				 if(dateUtil4.checkInputValidity()&&dateUtil3.checkInputValidity()) {
						int sum = dateUtil4.getDaysOfDates(dateUtil3);
						System.out.println("The days between "+dateUtil3.showDate()+" and "+dateUtil4.showDate()+" are:"+sum);
					}else {
						System.out.println("Wrong Format");
					}
				 
			 }else {
				 if(dateUtil3.checkInputValidity()&&dateUtil4.checkInputValidity()) {
						int sum = dateUtil3.getDaysOfDates(dateUtil4);
						System.out.println("The days between "+dateUtil3.showDate()+" and "+dateUtil4.showDate()+" are:"+sum);
					}else {
						System.out.println("Wrong Format");
					}
			 }
			
			 break;
			default :
				System.out.println("Wrong Format");
		}

		
	}

}

main类里与之前一直,没有改动


public class DateUtil {
	private Year year;//将三个类作为属性
	private Month month;
	private Day day;
	private int x =0;
	private int []mom_maxnum = {31,28,31,30,31,30,31,31,30,31,30,31};
	public DateUtil() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public DateUtil(int y , int m ,int d) {
		this.year = new Year(y);
		this.month = new Month(m);
		this.day = new Day(d);
	}

	public Year getYear() {
		return year;
	}

	public void setYear(Year year) {
		this.year = year;
	}

	public Month getMonth() {
		return month;
	}

	public void setMonth(Month month) {
		this.month = month;
	}

	public Day getDay() {
		return day;
	}

	public void setDay(Day day) {
		this.day = day;
	}
	public void setDayMin() {//设置最小日数
		day.setValue(1);
	}
	public void setDayMax() {//设置该月份最大日数
		if(this.year.isLeapYear()) {
			this.mom_maxnum[1] = 29;
		}else {
			this.mom_maxnum[1] = 28;
		}
		this.day.setValue(this.mom_maxnum[this.month.getValue()-1]);
	}
	public boolean checkInputValidity() {//合法性检测
		
		
		if(this.month.validate() == false) {
			return false;
		}
		if(this.year.validate() == false) {
			return  false;
		}
		if(this.year.isLeapYear()) {
			this.mom_maxnum[1] = 29;
		}else {
			this.mom_maxnum[1] = 28;
		}
		if(this.day.getValue() < 1||this.day.getValue()>this.mom_maxnum[this.month.getValue()-1]) {
			return false;
		}
		return true;
	}
	public DateUtil getNextNDays(int n) {
		DateUtil newDate = new DateUtil(this.year.getValue(),this.month.getValue(),this.day.getValue());
		while(true) {
			if(newDate.year.isLeapYear()) {
				this.mom_maxnum[1] = 29;
			}else {
				this.mom_maxnum[1] = 28;
			}
			if(n >this.mom_maxnum[newDate.month.getValue()-1] ) {//是否大于当月最大天数
				n -= this.mom_maxnum[newDate.month.getValue()-1];
				newDate.month.monthIncrement();
				if(newDate.month.getValue()>12) {//判断月份是否要重置
					newDate.year.yearIncrement();
				newDate.month.resetMin();
				}
			}else {
				int tmp =newDate.day.getValue()+n;
				if(tmp>this.mom_maxnum[newDate.month.getValue()-1])//判断是否号数大于当月最大天数
				{
					tmp -= this.mom_maxnum[newDate.month.getValue()-1];
					newDate.day.setValue(tmp);
					newDate.month.monthIncrement();
					if(newDate.month.getValue()>12) {
						newDate.year.yearIncrement();
					newDate.month.resetMin();
					}
				}else {
					newDate.day.setValue(tmp);
				}
				break;
			}
			
		}
		return newDate;
	}
	public DateUtil getPreviousNDays(int n) {
		DateUtil newDate = new DateUtil(this.year.getValue(),this.month.getValue(),this.day.getValue());
		n = n - this.day.getValue();
		
		if(newDate.year.isLeapYear()) {
			this.mom_maxnum[1] = 29;
		}else {
			this.mom_maxnum[1] = 28;
		}
		newDate.month.monthReduction();
		
		if(this.month.getValue()<1) {
			newDate.month.resetMax();
			newDate.year.yearDeduction();
		}
		newDate.day.setValue(this.mom_maxnum[newDate.month.getValue()-1]);
		while(true) {
			if(newDate.year.isLeapYear()) {
				this.mom_maxnum[1] = 29;
			}else {
				this.mom_maxnum[1] = 28;
			}
			if(n >this.mom_maxnum[newDate.month.getValue()-1] ) {
				n -= this.mom_maxnum[newDate.month.getValue()-1];
				newDate.month.monthReduction();
				if(newDate.month.getValue()<1) {
					newDate.year.yearDeduction();
				newDate.month.resetMax();
				
				}
				newDate.day.setValue(this.mom_maxnum[newDate.month.getValue()-1]);
			}else {
				newDate.day.setValue(newDate.day.getValue() - n);
				break;
			}
			
		}
		if(newDate.year.isLeapYear()) {
			this.mom_maxnum[1] = 29;
		}else {
			this.mom_maxnum[1] = 28;
		}
		if(newDate.day.getValue()>this.mom_maxnum[newDate.month.getValue()-1])
		{
			int tmp =newDate.day.getValue() ;
			int tmp2 =this.mom_maxnum[newDate.month.getValue()-1];
			newDate.day.setValue(tmp - tmp2);
			newDate.month.monthIncrement();
			if(this.month.getValue()>12) {
				newDate.month.resetMin();
				newDate.year.yearIncrement();
			}
		}
		return newDate;
	}
	 public boolean equalTwoDates(DateUtil date) {
		 if(this.day.getValue() == date.day.getValue() && this.month.getValue() == date.month.getValue() && this.year.getValue() == date.year.getValue()) {
			 return true;
		 }
		 return false;
	 }
	 public boolean compareDates(DateUtil date) {
		 if(this.year.getValue() > date.year.getValue()) {
			 return true;
		 }else if (this.year.getValue() < date.year.getValue()) {
			 return false;
		 }else if(this.month.getValue() > date.month.getValue()) {
			 return true;
		 }else if(this.month.getValue() < date.month.getValue()) {
			 return false;
		 }else if(this.day.getValue() > date.day.getValue()) {
			 return true;
		 }
		 return false;
	 }
	 public int getDaysOfDates(DateUtil date) {//同样的还是依靠折半查找确定N值
			int left = 0;
			int right = 1000;
			int mid = (left+right)/2;
			DateUtil newday = this.getNextNDays(right);
			while (true) {//保证N值在[left,right]范围内
				if(newday.year.getValue() < date.year.getValue()) {
					left = right;
					right = right*2;
					newday = this.getNextNDays(right);
				}else  {
					break;
				}
			
			}
			mid =(left+right)/2;
			newday = this.getNextNDays(mid);
			while (right>left){//折半查找
				if(newday.year.getValue()> date.year.getValue()) {
					right = mid -1;
					mid = (int)((right+left)/2);
					newday = this.getNextNDays(mid);
				}else if(newday.year.getValue() < date.year.getValue()) {
					left = mid + 1;
					mid = (right+left)/2;
					newday = this.getNextNDays(mid);
				}else if (newday.month.getValue()>date.month.getValue()) {
					right = mid -1;
					mid = (right+left)/2;
					newday = this.getNextNDays(mid);
				}else if(newday.month.getValue()<date.month.getValue()) {
					left = mid + 1;
					mid = (right+left)/2;
					newday = this.getNextNDays(mid);
				}else if (newday.day.getValue()>date.day.getValue()) {
					right = mid -1;
					mid = (right+left)/2;
					newday = this.getNextNDays(mid);
				}else if(newday.day.getValue()<date.day.getValue()) {
					left = mid + 1;
					mid = (right+left)/2;
					newday = this.getNextNDays(mid);
				}else{
	                break;
	            }
			}
			return mid;
	 }
	 public String showDate() {//格式化日期
		 String temp;
			Integer x = this.year.getValue();
			Integer z = this.month.getValue();
			Integer c = this.day.getValue();
			temp = x.toString() + "-" + z.toString() + "-" + c.toString();
			return temp;
	 }
}

这里对于getNextDays()getPreviousDays()两个函数的算法都是,判断n是否大于该月最大天数,
若是则减去该月的最大天数,直到n<该月最大天数则此时用day加上或减去n,但是由于这次题目集有整数
型最大值的边界,所以对于像原来的先把day的值先加给n然后设置day为0进而使得最后day+n时不
用再次判断是否越界当月份最大天数的方法不再适用,因为n一旦为最大边界,则不能整数型n=n+day会超
过整数型最大范围从而导致错误。而对于函数 getDaysOfDates() 则调用了函数getNextDays()函数去
找到一个N满足使更小的日期加上N天等于更大的日期。这里我采用了折半查找,但是提前要设置好right的初
始值,使其保证初始值right使得小的日期加上最大值right天数可以大过最大日期,既保证N一定会在[left,right]
的范围内,这样才能通过折半查找找出满足条件的N。

public class Day {
	private  int value;
	public Day() {
		
	}
	public Day(int value) {
		this.value = value;
		
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	public void dayIncrement() {//自增
		this.value++;
	}
	public void dayReduction() {//自减
		this.value--;
	}
}

public class Month {
	private int value;
	

	public Month() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Month(int value) {
		super();
		this.value = value;
	}
	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}


	public void resetMin() {
		this.value = 1;
	}
	public void resetMax() {
		this.value = 12;
	}
	public boolean validate() {//合法性判断
		if(this.value<1 || this.value>12) {
			return false;
		}
		return true;
	}
	public void monthIncrement() {//自增
		this.value++;
	}
	public void monthReduction() {//自减
		this.value--;
	}
}
public class Year {
	private int value;

	public Year() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Year(int value) {
		super();
		this.value = value;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
	public boolean isLeapYear() {//合法性判断
		if(this.value%400 == 0 ||(this.value%4 == 0 && this.value%100 != 0)) {
			return true;
		}
		return false;
	}
	public boolean validate() {
		if(this.value<1820||this.value>2020) {
			return false;
		}
		return true;
	}
	public void  yearIncrement() {//自增
		this.value++;
	}
	public void yearDeduction() {//自减
		this.value--;
	}
}

类图如下:

很明显DayMonthYear三个类平行且各种相对独立的,他们之间为没有类的关系
但是DayMonthYear三个类都是与DateUtil为组合关系。

复杂度:

其中getDaysOfDates()函数复杂度过高,原因当时认为折半查找和采用getNextDays()函数会比暴力循环
查找更优越,虽然是更为优越一点但是其复杂度依旧是比较高。

题目集4(7-2)、题目集5(7-4)两种日期类聚合设计的优劣比较

相同点:对于两次作业其一些关键的计算天数和日期的算法类似,没有大的改动,唯一不同之处就是由于
给定测试范围问题,之前一次的一些小技巧无法使用,会造成越界,同时对于计算N天前和后的算法一致
没有以n是否大于每年的天数来判断进而加一年或者减一年,原因是这时候要添加不少判断,判断是否闰年,
是否初始日期超过了二月份进而是应该有n-365或者n-366,这样会使得代码更为复杂,故没有选择此方法。
不同点:主要是对类关系的处理:
在题目集4的7-2中采用了DayMonthYear三个类平行且各种相对独立的,他们之间为没有类的关系三个类是嵌套组合的,Year嵌套在Month里组合,
Month嵌套在Day里组合,他们之间为组合关系同时DayDateUtil也是组合关系
但是在题目集5的7-4则是采用DayMonthYear三个类平行且各种相对独立的,他们之间没有关系
虽然在DateUtil是组合关系但是对于DayMonthYear来说在后者的设计中能够其三者数独立的没
有关系,所以后者更具有优越性,他们的耦合度更低,其复用的可能性更大
分析进而可能改进的可能:
对于在题目集5的7-4来说,为什么我将DayMonthYear依旧设计成与DateUtil类是组合关系,
因为DateUtil本来就是对时间进行处理的控制类,虽然用聚合关系可以降低他的耦合性,但完全没必要,
而时间的三个实体类都已经是独立的的类,已经是可以单独复用的类。

题目集4 7-3图形继承

代码如下:

import java.util.Scanner;
public class Main {
	
	public static void main(String [] agrs) {
		Agent agent = new Agent();//一个控制类对象
		agent.setOption();
	}
}
 abstract class Shape {//图形的抽象类
	public abstract double getArea();//计算面积的抽象函数
}
 class Circle extends Shape {//继承类抽象类Shape
	protected double radlius = 0;

	public Circle() {
		super();
		System.out.println("Constructing Shape");
		System.out.println("Constructing Circle");
		// TODO Auto-generated constructor stub
	}

	public Circle(double radlius) {
		
		this.radlius = radlius;
		System.out.println("Constructing Shape");
		System.out.println("Constructing Circle");
	}
	
	
	public double getRadlius() {
		return radlius;
	}

	public void setRadlius(double radlius) {
		 this.radlius = radlius;
	}
	public double getArea() {//实例化抽象函数
		return (double)(Math.pow(radlius, 2)*Math.PI);
	}
}
class Rectangle extends Shape {//继承抽象类Shape
	protected double width = 0;
	protected double length = 0;
	public Rectangle() {
	
		// TODO Auto-generated constructor stub
		System.out.println("Constructing Shape");
		System.out.println("Constructing Rectangle");
	}
	public Rectangle(double width, double length) {

		this.width = width;
		this.length = length;
		System.out.println("Constructing Shape");
		System.out.println("Constructing Rectangle");
	}
	

	public double getWidth() {
		return width;
	}
	public void setWidth(double width) {
		this.width = width;
	}
	public double getLength() {
		return length;
	}
	public void setLength(double length) {
		this.length = length;
	}
	public double getArea() {//实例化函数
		return (double)(width*length);
	}
}
class Ball extends Circle{//继承抽象类Shape
	
	
	public Ball() {
	
		System.out.println("Constructing Ball");
		// TODO Auto-generated constructor stub
	}

	public Ball(double radlius) {
		this.radlius = radlius;

		System.out.println("Constructing Ball");
	}
	public double getRadlius() {
		return radlius;
	}
	
	public void setRadlius(double radlius) {
		this.radlius = radlius ;
	}

	public double getArea() {//实例化函数,计算面积
		return (double)(4*Math.PI*Math.pow(radlius, 2));
	}
	
	public double getVolume() {//子类里添加函数,计算体积
		return (double)(4/3.0*Math.PI*Math.pow(radlius, 3));
	}
	
}
class Box extends Rectangle {//继承抽象类Shape
	private double height =0.0;

	public Box() {
		super();

		System.out.println("Constructing Box");
		// TODO Auto-generated constructor stub
	}
	

	public Box(double width, double length,double height) {
		super(width, length);
		this.height = height;

		System.out.println("Constructing Box");
		// TODO Auto-generated constructor stub
	}
	
	public double getArea() {//实例化抽象函数
		return (width*length*2+width*height*2+height*length*2);
	}
	
	public double getVolume() {
		return width*length*height;
	}
}
class Agent{//控制类,进行组织各类
	public Agent () {
		
	}
	public void setOption() {
		Scanner input = new Scanner(System.in);
		int option = input.nextInt();
		double []num = new double [3];
		switch(option) {
		case 1://建立一个圆
			num[0] = input.nextDouble();
			this.chechValid(num, 1);
			 Circle obj1 = new Circle(num[0]) ;
			System.out.printf("Circle's area:%.2f\n",obj1.getArea());
			break;
		case 2://建立一个矩形
			num[0 ] = input.nextDouble();
			num[1] = input.nextDouble();
			this.chechValid(num, 2);
			 Rectangle obj2 = new Rectangle(num[0],num[1]);
			System.out.printf("Rectangle's area:%.2f\n",obj2.getArea());
			break;
		case 3://建立一个球体
			num[0] = input.nextDouble();
			this.chechValid(num, 1);
			Ball obj3 = new Ball(num[0]);
			System.out.printf("Ball's surface area:%.2f\n",obj3.getArea());
			System.out.printf("Ball's volume:%.2f\n",obj3.getVolume());
			break;
		case 4://建立一个长方体
			num[0] = input.nextDouble();
			num[1] = input.nextDouble();
			num[2] = input.nextDouble();
			this.chechValid(num, 3);
			 Box obj4 = new Box(num[0],num[1],num[2]);
				System.out.printf("Box's surface area:%.2f\n",obj4.getArea());
				System.out.printf("Box's volume:%.2f\n",obj4.getVolume());
			break;
		default:
			System.out.println("Wrong Format");
			System.exit(0);
			break;
				
			
		}	
	}
	public void chechValid(double num[],int n) {//检验输入数据合法性
		for(int i = 0; i<n ;i++) {
			if(num[i]<=0) {
				System.out.println("Wrong Format");
				System.exit(0);
			}
		}
	}
}

对类的设计:
首先设计了一个抽象类Shape里面有一个抽象函数getArea()用来计算图形面积
但是并没有在抽象类里设计属性和计算体积大的方法,因为这样不符合继承的原则
CircleRectangleBallBox三个类是独立的,与Agent控制类是
聚合关系,通过Agent控制类进行组织其他四个实体类,BallBox两个类中
添加了GetVolume函数,进行对体积的计算。
类图如下:

复杂度如图

题目集6 7-5 图形继承与多态

代码如下:


public class Main {
 public static void main(String[] agrs) {
	Agent agent = new Agent();
	agent.runOut();
	 
 }
}
public abstract class Shape {//抽象类

	public abstract double getArea();//抽象函数,计算面积
	public abstract boolean validate();//抽象函数,校验数据合法性
	public abstract String toString();//抽象函数,格式化数据
}


public class Circle extends Shape {//继承Shape
	private double radius = 0;

	public Circle() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Circle(double radius) {
		super();
		this.radius = radius;
	}

	public double getRadius() {
		return radius;
	}

	public void setRadius(double radius) {
		this.radius = radius;
	}
	
	
	public double getArea() {//计算面积
		return Math.PI*Math.pow(radius, 2);
	}
	public boolean validate() {//校验数据合法
		if(this.radius <= 0) {//大于合法
			return false;
		}
		return true;
	}
	public String toString() {//格式化,保留两位小数
		return String.format("%.2f", this.getArea());
	}

}

public class Rectangle extends Shape{//继承Shape
	private double width = 0;
	private double length = 0;
	public Rectangle() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Rectangle(double width, double length) {
		super();
		this.width = width;
		this.length = length;
	}
	public double getWidth() {
		return width;
	}
	public void setWidth(double width) {
		this.width = width;
	}
	public double getLength() {
		return length;
	}
	public void setLength(double length) {
		this.length = length;
	}
	public double getArea() {//实例化抽象函数,计算面积
		return this.width*this.length;
	}
	public boolean validate() {//实例化抽象函数,校验数据合法性
		if(this.width<=0||this.length<=0) {//所有数据大于0合法
			return false;
		}
		return true;
	}
	public String toString() {//实例化抽象函数,格式化数据,保留两位小数
		return String.format("%.2f", this.getArea());
	}
}

public class Triangle extends Shape{//继承Shape
	double sides1 = 0;
	double sides2 = 0;
	double sides3 = 0;
	public Triangle() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Triangle(double sides1, double sides2, double sides3) {
		super();
		this.sides1 = sides1;
		this.sides2 = sides2;
		this.sides3 = sides3;
	}
	public double getSides1() {
		return sides1;
	}
	public void setSides1(double sides1) {
		this.sides1 = sides1;
	}
	public double getSides2() {
		return sides2;
	}
	public void setSides2(double sides2) {
		this.sides2 = sides2;
	}
	public double getSides3() {
		return sides3;
	}
	public void setSides3(double sides3) {
		this.sides3 = sides3;
	}
	public double getArea() {//实例化抽象函数,计算面积
		double s = (this.sides1+this.sides2+this.sides3)/2;
		double area2C = s*(s-this.sides1)*(s-this.sides2)*(s-this.sides3);
		return Math.sqrt(area2C);
	}
	public boolean validate() {//实例化抽象函数,校验数据合法性
		
		if(this.sides1 <= 0||this.sides2 <= 0 || this.sides3 <= 0) {
			return false;
		}
		if(this.sides1 + this.sides2 <= this.sides3 ||this.sides3 + this.sides2 <= this.sides1 || this.sides1 + this.sides3 <= this.sides2) {//利用三角形性质,两边之和大于第三边进行校验
			return false;
		}
		return true;
	}
	public String toString() {//格式化数据
		return String.format("%.2f", this.getArea());
	}

}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;

public class Agent {

	public Agent() {
		super();
		// TODO Auto-generated constructor stub
	}
	public void runOut() {
		 Scanner input = new Scanner(System.in);
		 List<Shape> list = new ArrayList<Shape>();//利用列表存储
		int count = 0;
		 int []num = new int [3];
		 for(int i = 0;i<3;i++) {//输入各图形个数
			 num[i] = input.nextInt();
			 count += num[i];
			 if(num[i] < 0) {//合法性校验
				 System.out.println("Wrong Format");
				 System.exit(0);
			 }
		 }
		 
		 for(int i = 0;i<num[0];i++) {
			 double radius = input.nextDouble();
			 Circle tmp = new Circle(radius);
			 if(tmp.validate()) {
				 list.add(tmp);
			 }else {
				 System.out.println("Wrong Format");
				 System.exit(0);
			 }
			
		 }
		 for(int i = 0;i<num[1];i++) {
			 double width = input.nextDouble();
			 double length =input.nextDouble();
			 Rectangle tmp = new Rectangle(width,length);
			 if(tmp.validate()) {
				 list.add(tmp);
			 }else {
				 System.out.println("Wrong Format");
				 System.exit(0);
			 }
		 }
		 for(int i = 0;i<num[2];i++) {
			 double sides1 = input.nextDouble();
			 double sides2 =input.nextDouble();
			 double sides3 =input.nextDouble();
			 Triangle tmp = new Triangle(sides1,sides2,sides3);
			 if(tmp.validate()) {
				 list.add(tmp);
			 }else {
				 System.out.println("Wrong Format");
				 System.exit(0);
			 }
		 }
		 System.out.println("Original area:");
		 Iterator<Shape> it =  list.iterator();
		 double sum = 0.0;
		 while(it.hasNext()) {
			 Shape tmp = it.next();
			 System.out.print(tmp.toString()+" ");
			 sum += tmp.getArea();
		 }
		 System.out.println();
		 System.out.println("Sum of area:"+String.format("%.2f", sum));
		 
		 for( int i = 0 ;i<count-1;i++) {
			 for(int j = 0 ;j<count-i-1;j++) {
				 if(list.get(j).getArea()>list.get(j+1).getArea()) {
					 Shape tmp =list.get(j);
					 list.set(j, list.get(j+1));
					 list.set(j+1,tmp );
				 }
			 }
		 }
		 sum = 0;
		 Iterator<Shape> its =  list.iterator();
		 System.out.println("Sorted area:");
		 while(its.hasNext()) {
			 Shape tmp = its.next();
			 System.out.print(tmp.toString()+" ");
			 sum += tmp.getArea();
		 }
		 System.out.println();
		 System.out.println("Sum of area:"+String.format("%.2f", sum));
	}

}

思路:
CircleTriangleRectangle三个实体类设计成为Shape类的子类,他们有三个共同函数,在父类将其抽象化
然后在三个子类中根据具体要求各自实例化,在通控制类Agent组织三个子类而在Agent通过泛型和多态使得将三个类产生
对象存在List中,再通过循环就算并输出。

类图:

CircleTriangleRectangle三个实体类为Shape类的子类,而三个类和Agent类是聚合关系

题目集5 7-6 实现图形接口及多态性
代码如下:

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		double []data = new double [3];
		for(int i = 0 ;i<3;i++) {
			data[i] = input.nextDouble();
			if(data[i]<= 0) {
				System.out.println("Wrong Format");
				System.exit(0);
			}
		}
		Circle circle = new Circle(data[0]);
		Rectangle rectangle = new Rectangle(data[1],data[2]);
		System.out.println(String.format("%.2f", circle.getArea()));
		System.out.println(String.format("%.2f", rectangle.getArea()));
		

	}

}
interface GetArea {//接口
	public abstract double getArea();//抽象函数,计算面积
}
class Circle implements GetArea{
	private double radlius = 0.0;

	public Circle() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Circle(double radlius) {
		super();
		this.radlius = radlius;
	}

	public double getRadlius() {
		return radlius;
	}

	public void setRadlius(double radlius) {
		this.radlius = radlius;
	}
	public double getArea() {
		return Math.PI*Math.pow(radlius, 2);
	}

}

public class Rectangle implements GetArea{//接口
	private double width = 0.0;
	private double length = 0.0;
	public Rectangle() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Rectangle(double width, double length) {
		super();
		this.width = width;
		this.length = length;
	}
	public double getWidth() {
		return width;
	}
	public void setWidth(double width) {
		this.width = width;
	}
	public double getLength() {
		return length;
	}
	public void setLength(double length) {
		this.length = length;
	}
	public double getArea() {//实例抽象函数,计算面积
		return length*width;
	}

}

}

思路:
RectangleCircle设计成两个独立类,再通过含有GetArea()抽象函数的接口完成两个类的设计
类图

RectangleCircle设计成两个独立类,通过接口实现类功能的完整性

题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计对比:
第一次题目中,是将Shape设计为抽象的父类,而其他为子类,通过子类继承父类的getArea()抽象函数计算面积
但是没有在父类中设计计算体积的抽象函数,因为有部分子类不需要该方法,这里主要的技术是继承和方法对的多态
并且在部分子类中添加了子类独有的计算体积对的方法
第二次题目中,同样的设计了一个Shape的父类里面带有三个抽象类,子类通过继承父类,来校验数据的合法性
和对面积的计算,技术也是继承和方法的多态,但是对多数据的处理,采用了List将其泛型Shape利用类的
多态进行存储。
第三次题目集,利用了接口技术,让三个图形类可以使用接口的抽象函数,进行计算。

改进的可能:
对于第一次题目,其实这样设计父类与子类是违背了类的设计原则
其实对于一些子类有的方法但是父类没有的方法,可以选择采用接
口进行处理。

题目集5 7-4 统计Java程序中关键词的出现次数

import java.util.Scanner;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);

		String str;
		StringBuilder tmpStr = new StringBuilder();

		String[] key = { "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"

		};//首先在数组里将关键字升序进行排序

		int[] count = new int[53];
		int j = 0;
		for (int i = 0; true; i++) {
			str = input.nextLine();
			if (str.compareTo("exit") == 0)
				break;

			if (str.matches("(.*)//(.*)")) {
				String t[] = str.split("//");
				tmpStr.append(t[0] + " ");

			} else {
				tmpStr.append(str + " ");

			}
		}
		String string = tmpStr.toString();
		
		Pattern p = Pattern.compile("\"(.*?)\"");//匹配注释符里的字符串
		Matcher m = p.matcher(string);
		while (m.find()) {
			string = string.replace(m.group(), " ");//用空格与注释符里字符串进行替换
			p = Pattern.compile("\"(.*?)\"");
			m = p.matcher(string);
		}
		string = string.replace(".", " ");//将特殊字符改为空格
		string = string.replace("(", " ");
		string = string.replace(")", " ");
		string = string.replace("[", " ");
		string = string.replace("]", " ");
        string = string.replace(";", " ");
		if (string.isEmpty()) {
			System.out.println("Wrong Format");
			System.exit(0);
		}
		String tempS;
		for (int i = 0; i < 53; i++) {
        
			 tempS = key[i]+" ";//保证检测出来的关键字就是一个单词,而不是某个字符串单词中的子串
            

			count[i] = stringFind(string, tempS);
                
		}
		for (int i = 0; i < 53; i++) {
			if (count[i] != 0) {
				System.out.println(count[i] + "	" + key[i]);
			}
		}

	}

	public static int stringFind(String source, String target) {
		int number = 0;
		int i = 0;
		while ((i = source.indexOf(target, i)) != -1) {
			number++;
			i++;
		}
		return number;
	}

}

思路:
一开始本来是想用hashmap进行存储各关键字,然后通过哈希算法简化查找的量,然后再进行查找
但是发信息关键字数量也不多,而且通过数组先进行排序还可以简化算法,所以没有采用hashmap函数
而是先用数组把关键字排好序再对输入的字符串进行处理,首先先把//注释符后的字符串进行替换成空格
再把各关键字换成空格,再用通过查找字符串进行统计。但是这样的复杂度比较高。

改进的可能:
对于没有满分,没有将全部特殊字符转化为空格,并别没有讲/**/里的字符串进行去除在去除
注释符中的字符串后,对于特殊字符的转化,完全可以采用一个匹配字符串,将非字母和空格
的字符全部转化空格。然后进行匹配。

对正则表达式技术的分析:
首先要分析好并选好要怎么对字符串进行处理,再根据要处理字符串的特点进行分组的设计正则表达式

踩坑心得

两个迭代器引用要不一样,不然后面的一个迭代器初始值已近到了尾部,或者重置迭代器

to String的用法:

toString(): 返回表示 Integer 值的 String 对象。

toString(int i): 返回表示指定 int String 对象。

public class Test{
    public static void main(String args[]){
        Integer x = 5;

        System.out.println(x.toString());  
        System.out.println(Integer.toString(12)); 
    }
}


public String getS() {
		Integer x = this.width;
		Integer y = this.length;
		return "Rectangle [width="+x.toString()+", length="+y.toString()+"]";
		
	}

存储不同的类,但是需要R和C都是Shape的子类,是多态的运用

Scanner input = new Scanner(System.in);
		int n = Integer.parseInt(input.nextLine());
		int radlius = 0;
		int width = 0,length = 0;
	-------->	List<Shape> list = new ArrayList<Shape>();
		String tmpStr ;
		for(int i = 0 ;i < n ;i++) {
			tmpStr = input.nextLine();
			if(tmpStr.compareTo("rect") == 0) {
		
				width =input.nextInt();
				length = input.nextInt();
				input.nextLine();
	---------------->list.add (new Rectangle(width,length));
			}else if(tmpStr.compareTo("cir") == 0) {
				radlius = input.nextInt();
				input.nextLine();
	---------------------->  list.add(new Circle(radlius)) ;
			}
		}

或者运用多态的特性。如果定义数组类型的时候定义为父类,而存进数组为父类的子类的话可以

public class test2 {
    public static void main(String args[]) {
     father []a=new father[2];
     a[0]=new son();
     a[1]=new son2();
    }
}
class father{
     int i=0; 
}
class son extends father{
 int x=0;
}
class son2 extends father{
 int y=0;
}

在一个类里设置 private类型的数组,但是另建立一个类,类可以直接调用数组?
好像是在相同的一个类里再次建立,所谓的private型属性仍然是visiable
但是在别的不同的类就是invisiable

同时对于protected是在包内的类可见,一开始误认为private修饰的属性不能被继承
实际上是可以继承的,只有'final'修饰的属性不能被继承。

总结:
三次作业中:
1.对于正则表达式有了更好的了解,但依旧是薄弱点
2.这三次对类的设计比较完善,对于对类的设计也有更好的理解
学习到了继承,多态,接口,封装等技术的使用
3.程序算法和设计较为薄弱,导致写出来的代码复杂度过高
课堂上:
1.单一职责原则:(首先保障这个原则)
一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小,
而且如果一个类承担职责过多,就相当于将这些职责耦合在一起,当其中一个
职责变化时,可能会影响其他职责的运作。(保障类之间没有直接关系,迪米
特法则)也就是说,设计类的时候,都要将他们当作独立的个体

2.开闭原则
一个软件实体应当对扩展开放,对修改关闭。也就是说在设计一个模块的时候,
应当使这个模块可以在不修改的前提下被扩展,即实现在不修改源代码的情况
下改变这个模块的行为。

原则分析:
1)抽象化是开闭原则的关键

抽象
第一层:对象到类
第二层:类到抽象类
行为抽象->接口

2)
需求可能有变化就进行封装
开闭原则还还可以通过一个更具体的“对可变性封装原则”来描述,
对可变性封装原则要求找到系统的可变因素并将其封装起来。

3.里氏代换原则LSP(什么时候可以继承)

所有引用基类(父类)的地方必须能透明地使用其子类独的对象
原则分析
让鸟类飞 让麻雀飞 (则麻雀是鸟的子类)
让企鹅飞但是企鹅不能飞(所以企鹅不能是鸟的子类)即使与现实相反

4.依赖倒转原则DIP

要针对接口编程,不要针对实现编程(接口就是想象成抽象的层而不是具体层)---->针对抽象编程而不是具体
《Aglie Software Development Principles Patterns and Practice》《Clean Code》

5.接口隔离原则ISP
定义:(这里的接口可以是接口也可以是抽象类)
客户端不应该依靠那些他不需要的接口

一旦一个接口太大,则需要将它分个成一些更细小的接口,使用该接口的客户端仅需要知道与之相关的

6.合成复用原则

定义:
尽量使用对象组合,而不是继承来达到复用达到目的(继承的耦合性最强),如果需要扩展那就要继承。

7.迪米特法则(耦合性越低越低):dont talk to strangers(类与类之间都是陌生人,不要和陌生
人说话只和直接朋友发送消息。在设计类之初的时候耦合性越低越好)
example:教师——学生(在最初之间教师和学生都是独立的,不会因某个对象消失而不存在,具有独立性)
但是 在教师和学生发生了交集(让学生做作业)(发送了消息)这就出现了耦合性

一旦类之间没有关系(没继承,没组合/聚合),独立设计
比如
图书馆-借书
设计各类读者(而不是学生,因为读者范围更大也可能是老师)
读者和学生或者老师什么关系----->继承和聚合都可
但是用哪个呢?如果要扩展则用继承,如果不就聚合
书籍和读者什么关系?------>没关系(他们之间无继承/聚合/组合关系)
那他们怎么发生关?---------->找中介啊(原则7)

8.什么做出接口(把共有的行为做成接口)

经过了老师对八大原则的教学后,对于面向对象设计,以及对类的设计有了
更深刻的了解,同时也明白了这种思维的优越性。

posted @ 2021-05-02 10:02  Trash_CODER  阅读(61)  评论(0)    收藏  举报