方法
方法:方法可以用于定义可重用的代码以及组织和简化编码
方法抽象和逐步求精
方法抽象:是通过将方法的使用和它的实现分离来实现的。用户在不知道方法是如何实现的情况下,就可以使用方法。方法的实现细节隐藏在方法内,对使用该方法的用户来说是隐藏的。这称为封装,如果决定改变方法的实现,只要不改变方法的签名,用户的程序就不受影响。比如我们前面调用的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("%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; } }
作业练习题:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!