只用循环语句和条件分支实现日历功能

问题的拆解和分析

从控制台输入XXX年XX月,从控制台输出该月日历。已知1900年1月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天),再将当月之前月数的天数进行相加,间隔总天数自然就算出来了。至此,我们至上而下地已经将一个大问题分解成多个容易完成的小问题了:

  1. 先模拟打印某个月的日历
  2. 计算出当月的天数,这里需要根据年份判断是否为闰年
  3. 算出月份距离1900年1月1日的总天数,再进行简单的运算就可以算出当月第一天是星期几
  4. 最终完善模拟打印日历功能,从而实现输入真实年月也可以打印

各个部分的完成和实现

一、 模拟打印某个月的日历

模拟打印某个月的日历比较容易实现,咱们假定要打印的月份有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;
    }

}

总结和思考

一个功能或者一个问题如果很难,不要想着一次性解决它,可以将它分解为多个部分,逐步逐步的完成,最后会发现,一开始我们惧怕的功能,居然就这么被我们轻易的解决了!

你可能感兴趣的:(java,程序员,循环,条件表达式)