问题的拆解和分析
从控制台输入XXX年XX月,从控制台输出该月日历。已知1900年1月1日是星期一
日历功能已经有非常成熟的库实现了,不过该题目锻炼的就是用分支结构和循环结构解决问题,所以咱们得从头一步一步用最基础的东西实现此功能。
拿到题目后,如果想一步登天,直接完成功能肯定不现实,所以得先将大问题先分解成小问题。首先,咱们得思考这个功能里各个部分,从小往大的去实现。
一开始做不到随便哪个年份和月份进行打印,但是咱们可以先完成打印某个月份的功能,所以咱们需要的解决的一个部分出现了:
先模拟打印某个月的日历。
能够模拟打印某个月的日历实现后,我们继续进一步的扩展功能。现在打印的当月天数、当月一号是星期几都是我们模拟出来的,如何做到输入真实月份,我们还能够打印呢,达到这个功能需要获得怎样的信息呢。这么一思考觉不难发现我们需要做的有哪些:
- 得判断当月的天数有几天,比如大月、小月、二月
- 得判断当月的一号是星期几
需要做的明确了后,那顺理成章的进行下一步推导,如何做到上面说的两步?判断大月、小月好说,一三五七八十腊嘛,二月的天数判断则需要先确定当年是否为闰年,闰年也非常好确定。OK,就是说咱们现在只剩下一个问题,如何判断当月的一号是星期几,解决这个问题,基本上整个功能就已经可以实现了。注意哦,咱们已经将一个大问题,分解到一个小问题了。
咱们现在已知的信息有一点,1900年1月1号是星期1,可能在这里有些人会迷糊,这个又有啥用呢。其实不然,咱们完全可以通过这一点推导出接下来的月份的第一天都是星期几。 1月1号是星期1了,那么2月1号就不难判断出了,将1月的天数(31天)模7然后+1,二月的第一天是星期几不就算出来了嘛!3月的天数呢? 将1月的天数和2月的天数相加,再模7然后+1也就算出来啦! 以此类推,是多少月的,将前面月份的天数相加 再模7然后+1就可以算出当月的天数。 说白了,就是算当月离1900年1月1日隔了多少天,再进行一个简单运算就可以算出!
那么,现在我们又将一个问题分解到一个更小的问题了:如何算出某年某月离1900年1月间隔了多少天。 这个就更能容易实现了,每隔一年我就加个365天呗(闰年是366天),再将当月之前月数的天数进行相加,间隔总天数自然就算出来了。至此,我们至上而下地已经将一个大问题分解成多个容易完成的小问题了:
- 先模拟打印某个月的日历
- 计算出当月的天数,这里需要根据年份判断是否为闰年
- 算出月份距离1900年1月1日的总天数,再进行简单的运算就可以算出当月第一天是星期几
- 最终完善模拟打印日历功能,从而实现输入真实年月也可以打印
各个部分的完成和实现
一、 模拟打印某个月的日历
模拟打印某个月的日历比较容易实现,咱们假定要打印的月份有31天,1号是星期2:
int monthDay = 31; // 当月的天数
int dayOfWeek = 2; // 当月1号的星期数
// 打印日历星期说明
System.out.println("星期一\t星期二\t星期三\t星期四\t星期五\t星期六\t星期天");
// 通过循环先打印空白字符,好实现让当月1号打印到相应的位置
for (int i = 1; i < dayOfWeek; i++) {
System.out.print("\t");
}
// 通过循环打印当月的天数
for (int i = 0; i < monthDay; i++) {
// 打印天数的语句,注意不要换行,每个天数后面加空白字符,更好看一些
System.out.print(i + 1 + "\t");
// 控制换行,每到星期天就需要换行
if ((i + dayOfWeek) % 7 == 0) {
System.out.println();
}
}
二、计算当月的天数
// 用来储存当月一共有多少天
int monthDay;
// 用来记录当年是否为闰年
boolean isLeapYear = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
// 判断大月和小月
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
monthDay = 31; // 大月则是31天
} else {
monthDay = 30; // 小月则是30天
}
// 2月情况不同,所以单独进行判断
if (month == 2 && isLeapYear) { // 如果是闰年,天数就是29天
monthDay = 29; // 当月天数为29天
} else if (month == 2) { // 如果不是闰年,天数就是28天
monthDay = 28; // 当月天数为28天
}
三、算出距离1900年1月1日的间隔天数,并计算当月1号的星期数
这一步最为复杂,所以咱们从简单的来,先实现将当年的月数天算出来,然后再算年份的天数。
// 声明变量,后面用来存放总天数数据
int dayNum = 0;
// 进行数值合法判断
if (month > 13 || month < 1 || year < 1 || day < 1 || day > 31) {
// 如果不合法则提示用户输入错误
System.out.println("输入的数字有误!");
// 程序退出
System.exit(0);
}
// 先判断年份是否为闰年,将结果赋值给变量
boolean isLeapYear = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
// 如果不是闰年 用户输入的是2月29日,则为不合法
if (!isLeapYear && month == 2 && day > 28) {
// 如果不合法则提示用户输入错误
System.out.println("这一年不是闰年,2月只有28天哦!");
// 系统退出
System.exit(0);
}
// 开始循环,将当月以前的月份天数都加上
for (int i = 1; i < month; i++) {
// 判断大月和小月
if (month == 2 || month == 4 || month == 6 || month == 8 || month == 9 || month == 11) {
dayNum += 31; // 大月则是31天,将总天数+31
} else {
dayNum = 30; // 小月则是30天,将总天数+30
}
}
// 2月情况不同,所以单独进行判断
if (month == 3 && isLeapYear) { // 如果是闰年,天数就是29天
dayNum += 29; // 将总天数+29
} else if (month == 3) { // 如果不是闰年,天数就是28天
dayNum += 28; // 将总天数+28
}
// 最后将天数加的总天数里
dayNum += day;
当年当月的间隔当年的1号天数已经算出来,接下来实现当年距离1900年的就非常容易实现了:
// 用来储存距离1900年1月1日有多少天数
int daySum = 0;
// 循环用来计算年份和1900间隔了多少天数
for (int i = 1900; i < year; i++) {
// 如果是闰年就是366天
if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
daySum += 366; // 将总天数进行相加
} else {// 如果不是闰年就是365天
daySum += 365; // 将总天数进行相加
}
}
// 为了代码美观,就将上一个代码块的当年当月天数功能进行了封装,将距离当年1月1号有多少天数作为返回值返回
// 调用方法,计算该月的天数,然后将总天数进行相加,这时候距离1900年1月1号有多少天数就计算出来了
daySum += getDayOfMonth(year, month);
间隔天数拿到,进行简单的运算,即可算出当月的1号是星期几:
// 打印当月日历需要知道当月1号是星期几
// 已得知1900年1月1日是星期1,所以将距离总天数模7然后+1即可得到当月第一天的星期数
int dayOfWeek = daySum % 7 + 1;
四、完善模拟打印日历功能,从而实现输入真实年月也可以打印
所以小功能都已经实现了,当月的天数也有了,当月的1号是星期几也都知道了,那么模拟打印日历功能的参数一改,就实现了整个功能!同样,为了代码结构的美观,将打印日历功能也封装成了方法。
以下是所有代码:
import java.util.Scanner;
/**
*
* @function 输入年和月,打印当月日历
* @author RudeCrab
* @date 2019年3月29日下午8:14:59
* @place 成都
* @version 1.0.0
* @copyright RudeCrab
*/
public class ExtraHomework {
public static void main(String[] args) {
// 创建Scanner对象,方便录入数据
Scanner input = new Scanner(System.in);
// 提示用户输入年份
System.out.print("请输入年份:");
// 读取用户输入的年份,赋值给year
int year = input.nextInt();
// 提示用户输入月份
System.out.print("请输入月份:");
// 读取用户输入的月份,赋值给month
int month = input.nextInt();
// 用来储存距离1900年1月1日有多少天数
int daySum = 0;
// 循环用来计算年份和1900间隔了多少天数
for (int i = 1900; i < year; i++) {
// 如果是闰年就是366天
if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
daySum += 366; // 将总天数进行相加
} else {// 如果不是闰年就是365天
daySum += 365; // 将总天数进行相加
}
}
// 调用方法,计算该月的天数,然后将总天数进行相加
daySum += getDayOfMonth(year, month);
// 打印当月日历需要知道当月1号是星期几
// 已得知1900年1月1日是星期1,所以将距离总天数模7然后+1即可得到当月第一天的星期数
int dayOfWeek = daySum % 7 + 1;
// 调用方法,打印当月日历
showCalendar(dayOfWeek, month, year);
// 关闭input流
input.close();
}
/**
* 打印当月的月历
*
* @param dayOfWeek 当月1号的星期数
* @param month 月份
* @param year 年份
*/
public static void showCalendar(int dayOfWeek, int month, int year) {
// 用来储存当月一共有多少天
int monthDay;
// 用来记录当年是否为闰年
boolean isLeapYear = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
// 判断大月和小月
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
monthDay = 31; // 大月则是31天
} else {
monthDay = 30; // 小月则是30天
}
// 2月情况不同,所以单独进行判断
if (month == 2 && isLeapYear) { // 如果是闰年,天数就是29天
monthDay = 29; // 当月天数为29天
} else if (month == 2) { // 如果不是闰年,天数就是28天
monthDay = 28; // 当月天数为28天
}
// 开始打印月历
System.out.println("周一\t周二\t周三\t周四\t周五\t周六\t周七");
// 先打印空白字符,好让1号打印到对应的星期
for (int i = 1; i < dayOfWeek; i++) {
// 打印空白字符语句
System.out.print("\t");
}
// 再打印具体日期
// 通过循环打印当月的天数
for (int i = 0; i < monthDay; i++) {
// 打印天数的语句,注意不要换行,每个天数后面加空白字符,更好看一些
System.out.print(i + 1 + "\t");
// 控制换行,每到星期天就需要换行
if ((i + dayOfWeek) % 7 == 0) {
// 换行语句
System.out.println();
}
}
}
/**
* 判断这一天是这一年的第几天,并将天数作为返回值返回
*
* @param year 年份
* @param month 月份
* @return 当年当月的总天数
*/
public static int getDayOfMonth(int year, int month) {
// 声明变量,后面用来存放总天数数据
int dayNum = 0;
// 进行数值合法判断
if (month > 13 || month < 1 || year < 1) {
// 如果不合法则提示用户输入错误
System.out.println("输入的数字有误!");
// 程序退出
System.exit(0);
}
// 先判断年份是否为闰年,将结果赋值给变量
boolean isLeapYear = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
// 开始循环,将当月以前的月份天数都加上
for (int i = 1; i < month; i++) {
// 判断大月和小月
if (month == 2 || month == 4 || month == 6 || month == 8 || month == 9 || month == 11) {
dayNum += 31; // 大月则是31天,将总天数+31
} else {
dayNum = 30; // 小月则是30天,将总天数+30
}
}
// 2月情况不同,所以单独进行判断
if (month == 3 && isLeapYear) { // 如果是闰年,天数就是29天
dayNum += 29; // 将总天数+29
} else if (month == 3) { // 如果不是闰年,天数就是28天
dayNum += 28; // 将总天数+28
}
// 最后将天数加的总天数里
dayNum += day;
// 返回当年当月的总天数
return dayNum;
}
}
总结和思考
一个功能或者一个问题如果很难,不要想着一次性解决它,可以将它分解为多个部分,逐步逐步的完成,最后会发现,一开始我们惧怕的功能,居然就这么被我们轻易的解决了!