方法

方法:方法可以用于定义可重用的代码以及组织和简化编码

  

方法抽象和逐步求精

  方法抽象:是通过将方法的使用和它的实现分离来实现的。用户在不知道方法是如何实现的情况下,就可以使用方法。方法的实现细节隐藏在方法内,对使用该方法的用户来说是隐藏的。这称为封装,如果决定改变方法的实现,只要不改变方法的签名,用户的程序就不受影响。比如我们前面调用的System.out.println()方法,Math.random()方法等,作为方法的使用者,我们并不需要知道它们是怎么实现的。
方法抽象的概念可以应用于程序的开发过程中。当编写一个大程序,可以使用分治策略,将大问题分解成子问题。子问题又分解成更小、更容易处理的问题。

  假设要编写一个程序、显示给定年月的日历。程序提示用户输入年份和月份,然后显示该月的整个日历。如下图所示:

  先把问题分解成两个子问题:读取用户输入和打印该月的日历

   打印给定月份的日历问题可以分解成两个子问题:打印日历的标题和日历的主体。月历的标题由三部分组成:年月,虚线,每周7天的星期名称。需要通过表示月份的数字(例如:1)来确定该月的全称(例如:January),这个步骤可以抽取为getMonthName来完成。

  为了打印日历主体,需要知道这个月的第一天是星期几(抽取为getStartDay),以及该月有多少天(抽取为getNumberOfDaysInMonth)。

   计算一个月的第一天是星期几:假设知道1800年1月1日是星期三(START_DAY_FOR_JAN_1_1800 = 3),然后计算1800-01-01和日历月份的第一天之间相差的总天数(totalNumberOfDays)。因为每个星期有7天,所以日历月份第一天的星期就是(totalNumberOfDays + START_DAY_FOR_JAN_1_1800 ) % 7。这样getStartDay问题可以进一步细化为getTotalNumberOfDays。

  要计算总天数,需要知道该年是否为闰年以及每个月的天数,所以getTotalNumberOfDays可以继续细化成两个子问题:isLeapYear和getNumberOfDaysInMonth。完整的结构图:

   现在我们把注意力转移到实现上来。通常,一个子问题对应实现中的一个方法,即使某些子问题太简单,以至于都不需要方法来实现,而哪些模块需要与其他方法结合完成。这种决策应该基于所做的选择是否使整个程序更易阅读而做出的。

package edu.uestc.avatar;

import java.util.Scanner;

/**
 * 要求用户输入指定的年月,打印出该年月对应的日历
 *
 */
public class PrintCalendar {
    public static void main(String[] args) {
        //readInput
        System.out.println("Enter full year.(eg..2018):");
        Scanner input = new Scanner(System.in);
        int year = input.nextInt();
        System.out.println("Enter month as number between 1 and 12:");
        int month = input.nextInt();
        input.close();
        //打印日历
        printMonth(year,month);
    }
    /**
     * 打印月历
     * @param year 用户输入的年份
     * @param month 月份
     */
    public static void printMonth(int year, int month) {
        printMonthTitle(year, month);
        printMonthBody(year, month);
    }
    
    /**
     * 打印日历头
     * 月份英文名称 年份
     * -----------------------
     * 星期的缩写
     */
    public static void printMonthTitle(int year,int month) {
        String monthName = getMonthName(month);
        System.out.println("         " + monthName + "  " + year);
        System.out.println("---------------------------------------------");
        System.out.println(" Sun Mon Tue Wed Thu Fri Sat ");
    }
    
    /**
     * 打印日历主体
     * @param year 对应年份
     * @param month 对应月份
     */
    public static void printMonthBody(int year,int month) {
        //获取该月第一天是星期几
        int startDay = getStartDay(year, month);
        
        //获取该月的总天数
        int numberOfDaysInMonth = getNumberOfDaysInMonth(year, month);
        int i = 0;
        for(i = 0; i < startDay; i++)
            System.out.print("       ");
        for(i = 1; i <= numberOfDaysInMonth; i++) {
            System.out.printf(i < 10 ? "%5d" : "%4d", i);
            if((i + startDay) % 7 == 0) System.out.println(); 
        }
        System.out.println();
    }
    
    /**
     * 将给定的月份(1--12)转换成对应的英文月份
     * @param month 月份
     * @return 月份英语名称
     */
    public static String getMonthName(int month) {
        String monthName = "";
        switch(month) {
            case 1:  monthName = "January"; break;
            case 2:  monthName = "February"; break;
            case 3:  monthName = "March"; break;
            case 4:  monthName = "April"; break;
            case 5:  monthName = "May"; break;
            case 6:  monthName = "June"; break;
            case 7:  monthName = "July"; break;
            case 8:  monthName = "August"; break;
            case 9:  monthName = "September"; break;
            case 10:  monthName = "October"; break;
            case 11:  monthName = "November"; break;
            case 12:  monthName = "December"; break;
        }
        return monthName;
    }
    
    /**
     * 获取指定月份的第一天是星期几
     * @param year 年
     * @param month 月份
     * @return 该月第一天是星期几
     * 1800-01-01   ---->
     */
    public static int getStartDay(int year, int month) {
        final int START_DAY_FOR_JAN_1_1800 = 3;
        int totalnumberOfDays = getTotalNumberOfDays(year,month);
        return (totalnumberOfDays + START_DAY_FOR_JAN_1_1800) % 7;
    }
    
    /**
     * 获取从1800-01-01到给定年月的中间相差天数
     * @param year 年份
     * @param month 月份
     * @return 相差的总天数
     */
    public static int getTotalNumberOfDays(int year, int month) {
        int total = 0;
        for(int i = 1800; i < year; i++) 
            total += isLeapYear(i) ? 366 : 365;
        for(int i = 1; i < month; i ++)
            total += getNumberOfDaysInMonth(year,i);
        return total;
    }
    
    /**
     * 判断给定的年份是否为闰年
     * @param year 给定的年份
     * @return 如果是闰年,返回true,否则返回false
     */
    public static boolean isLeapYear(int year) {
        return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    }
    
    /**
     * 计算指定月份包含的天数
     * @param month 给定的月份
     * @return 月份总天数
     */
    public static int getNumberOfDaysInMonth(int year,int month) {
        if(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) return 31;
        else if(month == 4 || month == 6 || month == 9 || month == 11) return 30;
        else return isLeapYear(year) ? 29 : 28;
    }
}

 

作业练习题:

 

posted @ 2020-12-03 10:13  Tiger-Adan  阅读(1013)  评论(0编辑  收藏  举报