Textbook notes Chapter 6
6.1 introduction
- Methods can be used to define reusable code and to organize and simplify code
6.2 definition method
-
Method definition consists of method name, parameter, return value type and method body
-
The syntax for defining methods is as follows:
Modifier return type Method name(parameter list){ //Method body; }
-
method header refers to the modifier, return value, method name and parameter of a method.
-
Method can return a value. returnValueType is the data type of the return value of the method. Some methods simply perform certain required operations without returning values. In this case, returnValueType is the keyword void. For example, in the main method, returnValueType is void, and in system exit,System. out. The return value type in the println method is also void. If a method has a return value, it is called a value returning method. Otherwise, it is called a void method
-
The variables defined in the method header are called formal parameters or formal parameters for short. Parameters are like placeholders. When a method is called, a value is passed to the parameter, which is called the actual parameter or argument. The parameter list indicates the type, order, and number of parameters in the method. The method name and parameter list together form a method signature. Parameters are optional, that is, methods can contain no parameters. For example: math The random () method has no parameters.
-
The method body contains a collection of statements that implement the method. The method body of the max method uses an if statement to determine which number is larger, and then returns the value of the number. In order for a method with a return value to return a result, a return statement with the keyword return must be used. The method terminates when the return statement is executed
- In some other languages, methods are called procedures or functions. In these languages, a method with a return value is called a function, and a method with a return value type of void is called a procedure
- In the method header, you need to make a separate data type declaration for each parameter. For example, max(int num1, int num2) is correct and max(int num1,num2) is wrong.
- We often say "define method" and "declare variable". Here we talk about the subtle differences between the two. Definition means that the defined item is a declaration, and declaration usually refers to allocating memory for the declared item to store data.
6.3 calling method
- Method calls refer to the execution of the code in the method
- In the method definition, you define what the method is used for. In order to use the method, you must call (call or invoke) it. Depending on whether a method has a return value, there are two ways to call a method
- In Java, a method with a return value can also be called as a statement. In this case, the function caller simply ignores the return value. Although this is rarely done, it is allowed if the caller is not interested in the return value.
- When a program calls a method, program control is transferred to the called method. When the return statement is executed or the closing bracket indicating the end of the method is executed, the called method returns program control to the caller
Program listing 6-1 TestMax java
public class TestMax { /** * Main method */ public static void main(String[] args) { int i = 5; int j = 2; int k = max(i, j); System.out.println("The maximum of " + i + " and " + j + " is " + k); } /** * Return the max of two numbers */ public static int max(int num1, int num2) { int result; if (num1 > num2) { result = num1; } else { result = num2; } return result; } }
- The return statement is required for methods with return values. The method shown in figure a below is logically correct, but it will have compilation errors because the Java compiler believes that the method may not return any values
- To fix this problem, delete if (n < 0) in figure a, so that the compiler will find that no matter how the if statement is executed, it can always execute to the return statement
- Method can bring code sharing and reuse. In addition to calling the max method in TestMax, you can also call it in other classes. If you create a new class, you can call the max method by using "class name. Method name" (i.e. TestMax.max)
- Whenever a method is called, the system creates an activity record (also known as the activity framework) to save the parameters and variables in the method. Activity records are placed in a memory area called call stack. Call stack is also called execution stack, runtime stack, or a machine stack, often referred to as "stack". When one method calls another method, the caller's activity record remains unchanged, and a new activity record is created for the called new method. When a method ends running and returns to the call, its corresponding activity record is also released.
- After the call stack, the activity record is saved after the first in first out mode. The activity record of the last call method is first removed from the stack. For example, suppose method m1 calls method m2 and method M2 calls method m3 At run time, the activity record of m1 is pushed to the stack, then m2, and then m3. When m3 finishes running, its activity record is removed from the stack. When M2 finishes running, its activity record is removed from the stack. When m1 finishes running, its activity record is removed from the stack.
- Understanding the call stack helps to understand how methods are called. The main method in listing 6-1 defines variables i, j and k; The variables num1, num2, and result are defined in the max method. The variables num1 and num2 defined in the method signature are parameters of the method max. Their values are passed through method calls. Figure 6-3 shows the activity records in the stack for method calls.
6.4 void method and return value method
- void method does not return a value
Program listing 6-2 testvoid method java
public class TestVoidMethod { public static void main(String[] args) { System.out.print("The grade is "); printGrade(78.5); System.out.println("The grade is "); printGrade(59.5); } public static void printGrade(double score){ if (score >= 90){ System.out.println('A'); } else if (score >= 80){ System.out.println('B'); } else if (score >= 70){ System.out.println('C'); } else if (score >= 60){ System.out.println('D'); } else { System.out.println('F'); } } }
Program listing 6-3 testreturngrademethod java
public class TestReturnGradeMethod { public static void main(String[] args) { System.out.print("The grade is " + getGrade(78.5)); System.out.print("\nThe grade is " + getGrade(59.5)); } public static char getGrade(double score) { if (score >= 90) { return 'A'; } else if (score >= 80) { return 'B'; } else if (score >= 70) { return 'C'; } else if (score >= 60) { return 'D'; } else { return 'F'; } } }
-
The return statement is not required for void methods, but it can be used to abort the method and return to the caller of the method. Its syntax is:
return;
-
This usage is rare, but it is useful for changing the normal flow control in the void method. For example, the following code ends the method with a return statement when the score is an invalid value
public static void printGrade(double score){ if (score < 0 || score > 100){ System.out.println("Invalid score"); return; } if (score >= 90.0){ System.out.println('A'); } else if (score >= 80.0){ System.out.println('B'); } else if (score >= 70.0){ System.out.println('C'); } else if (score >= 60.0){ System.out.println('D'); } else { System.out.println('F'); } }
6.5 transferring parameters by value
-
When calling a method, the argument is passed to the formal parameter by passing a value
-
Method is powerful in its ability to handle parameters. You can use the println method to print any string and the max method to find the maximum of any two int values. When calling a method, you need to provide arguments, which must be in the same order as the corresponding formal parameters in the method signature. This is called parameter order Association. For example, the following method prints a message n times:
public static void nPrintln(String message, int n){ for(int i = 0; i < n; i++) System.out.println(message); }
- The arguments must match the formal parameters defined in the method signature in order and quantity, and be compatible in type. Type compatibility means that the value of the argument can be passed to the formal parameter without the displayed type conversion. For example, the value of the int argument can be passed to the double parameter
- When a method with parameters is called, the value of the argument is passed to the formal parameter. This process is called pass - by - value. If the argument is a variable rather than a literal, the value of the variable is passed to the parameter. This variable is not affected whether the formal parameter changes in the method or not. As shown in listing 6-4, the value of x(1) is passed to parameter n to call the method increment (line 5). In this method, n increases by 1 (line 10), and the value of x remains unchanged regardless of what the method does.
Procedure listing 6-4 increment java
public class Increment { public static void main(String[] args) { int x = 1; System.out.println("Before the call, x is " + x); increment(x); System.out.println("After the call, x is " + x); } public static void increment(int n) { n++; System.out.println("n inside the method is " + n); } }
Program listing 6-5 testpassbyvalue java
public class TestPassByValue { /** * Main method * * @param args */ public static void main(String[] args) { //Declare and initialize variables int num1 = 1; int num2 = 2; System.out.println("Before invoking the swap method, num1 is " + num1 + " and num2 is " + num2); //Invoke the swap method to attempt to swap two variables swap(num1, num2); System.out.println("After invoking the swap method, num1 is " + num1 + " and num2 is " + num2); } /** * Swap two variables */ public static void swap(int n1, int n2) { System.out.println("\tInside the swap method"); System.out.println("\t\tBefore swapping, n1 is " + n1 + " and n2 is " + n2); //Swap n1 with n2 int temp = n1; n1 = n2; n2 = temp; System.out.println("\t\tAfter swapping, n1 is " + n1 + " and n2 is " + n2); } }
- For simplicity, Java programmers often say that the argument x is passed to the formal parameter Y, which actually means that the value of X is passed to y
6.6 modular code
- Modularity makes the code easy to maintain and debug, and makes the code reusable
- The use of repetition can reduce redundant code and improve the reusability of code. Method can also be used to modularize code to improve program quality
Program listing 6-6 greatestcommondivisormethod java
import java.util.Scanner; public class GreatestCommonDivisorMethod { /** * Main method * @param args */ public static void main(String[] args) { //Create a Scanner Scanner input = new Scanner(System.in); //Prompt the user to enter two integers System.out.print("Enter first integer: "); int n1 = input.nextInt(); System.out.print("Enter second integer: "); int n2 = input.nextInt(); System.out.println("The Greatest common divisor for " + n1 + " and " + n2 + " is " + gcd(n1,n2)); } /** * Return the gcd of two integers */ public static int gcd(int n1,int n2){ //Initial gcd is 1 int gcd = 1; //Possible gcd int k = 2; while (k <= n1 && k<=n2){ if (n1 % k == 0 && n2 % k == 0){ //Update gcd gcd = k; } k++; } return gcd; } }
By encapsulating the code for finding the maximum common divisor in one method, this program has the following advantages:
- It separates the problem of calculating the maximum common divisor from other code in the main method, which will make the logic clearer and the program more readable
- The error of calculating the maximum common divisor is limited to the gcd method, which reduces the scope of debugging
- Now, other programs can reuse the gcd method
Program listing 6-7 primenumbermethod java
public class PrimeNumberMethod { public static void main(String[] args) { System.out.println("The first 50 prime numbers are :\n"); printPrimeNumbers(50); } public static void printPrimeNumbers(int numberOfPrimes) { //Display 10 per line final int NUMBER_OF_PRIMES_PER_LINE = 10; //Count the number of prime numbers int count = 0; //A number to be tested for primeness int number = 2; //Repeatedly find prime numbers while (count < numberOfPrimes) { //Print the prime number and increase the count if (isPrime(number)) { //Increase the count count++; if (count % NUMBER_OF_PRIMES_PER_LINE == 0) { //Print the number and advance to the new line System.out.printf("%-5d\n", number); } else { System.out.printf("%-5d", number); } } //Check whether the next number is prime number++; } } public static boolean isPrime(int number) { for (int divisor = 2; divisor <= number / 2; divisor++) { if (number % divisor == 0) { //If true ,number is not prime //Number is not a prime return false; } } //Number is prime return true; } }
We divide a big problem into two subproblems: determining whether a number is a prime number and printing a prime number. In this way, the new program will be easier to read and debug. Moreover, other programs can reuse the methods printPrimeNumbers and isPrime
6.7 example learning: convert hexadecimal numbers to decimal numbers
-
This section gives a program to convert hexadecimal numbers to decimal numbers
-
An exhaustive method is to convert each hexadecimal character into a decimal number, that is, the hexadecimal number at position I is multiplied by 16i, and then add all these items to obtain the decimal number equivalent to the hexadecimal number
-
be aware:
hn × 16n + hn-1 × 16n-1 + hn-2 × 16n-2 + ... + h1 × 161 + h0 × 160
= (...(( hn × 16n + hn-1 ) × 16 + hn-2) × 16 + ... + h1) × 16 + h0
- This discovery, called the Horner algorithm, can lead to the following efficient algorithm for converting hexadecimal strings to decimal numbers:
int decimalValue = 0; for(int i = 0; i <hex.length(); i++){ char hexChar = hex.charAt(i); decimalValue = decimalValue * 16 + hexCharToDecimal(hexChar); }
- The following is the tracking of the program when the algorithm is applied to the hexadecimal number AB8C:
i | Hexadecimal number | Convert hexadecimal number to decimal number | Decimal number | |
---|---|---|---|---|
Before the cycle starts | 0 | |||
After the first iteration | 0 | A | 10 | 10 |
After the second iteration | 1 | B | 11 | 10 * 16 +11 |
After the third iteration | 2 | 8 | 8 | (10 * 16 + 11) * 16 + 8 |
After the fourth iteration | 2 | C | 12 | ((10 * 16 + 11)*16 +8) * 16 + 12 |
Procedure list 6-8 HEX2DEC java
import java.util.Scanner; public class Hex2Dec { /** * Main method * * @param args */ public static void main(String[] args) { //Create a Scanner Scanner input = new Scanner(System.in); //Prompt the user to enter a string System.out.print("Enter a hex number: "); String hex = input.nextLine(); System.out.println("The decimal value for hex number " + hex + " is " + hexToDecimal(hex.toUpperCase())); } public static int hexToDecimal(String hex) { int decimalValue = 0; for (int i = 0; i < hex.length(); i++) { char hexChar = hex.charAt(i); decimalValue = decimalValue * 16 + hexCharToDecimal(hexChar); } return decimalValue; } public static int hexCharToDecimal(char ch) { if (ch >= 'A' && ch <= 'F') { return 10 + ch - 'A'; } else { //ch is '0','1',...,or '9 return ch - '0'; } } }
6.8 heavy load method
- Overloaded methods allow you to define different methods with the same name, as long as their parameter lists are different
Program listing 6-9 testmethodoverloading java
public class TestMethodOverloading { /** * Main method * * @param args */ public static void main(String[] args) { //Invoke the max method with int parameters System.out.println("The maximum of 3 and 4 is " + max(3, 4)); //Invoke the max method with double parameters System.out.println("The maximum of 3.0 and 5.4 is " + max(3.0, 5.4)); //Invoke the max method with three double parameters System.out.println("The maximum of 3.0 and 5.4 , and 10.14 is " + max(3.0, 5.4, 10.14)); } /** * Return the max of two int values */ public static int max(int num1, int num2) { if (num1 > num2) { return num1; } else { return num2; } } /** * Find the max of two double values */ public static double max(double num1, double num2) { if (num1 > num2) { return num1; } else { return num2; } } /** * Return the max of three double values */ public static double max(double num1, double num2, double num3) { return max(max(num1, num2), num3); } }
- Overloading method can make the program clearer and more readable. Methods that perform the same function but have different parameter types should use the same name
- Overloaded methods must have different parameter lists. Methods cannot be overloaded based on different modifiers or return value types.
- Sometimes when a method is called, there are two or more possible matches, but the compiler cannot determine which is the most accurate match. This is called ambiguous invocation. Ambiguous calls produce a compilation error. Consider the following codes:
public class AmbiguousOverloading { public static void main(String[] args) { System.out.println(max(1,2)); } public static double max(int num1, double num2){ if (num1 > num2) { return num1; } else { return num2; } } public static double max(double num1, int num2){ if (num1 > num2){ return num1; } else { return num2; } } }
Both max (int, double) and max(double, int) may match max(1,2). Since neither of the two methods is more precise than the other, this call is ambiguous and will lead to a compilation error.
6.9 scope of variables
- The scope of a variable refers to the range in which a variable can be referenced in the program.
- Variables defined in methods are called local variable s.
- The scope of a local variable starts from the place where the variable is declared until the end of the block containing the variable. Local variables must be declared and assigned before use
- A parameter is actually a local variable. The scope of a method's parameters covers the entire method. The scope of the variable declared in the initial operation part of the for loop header is limited to the loop body, starting from its declaration to the end of the block containing the variable.
- You can declare a local variable with the same name in different blocks in a method, but you cannot declare the same local variable twice in a nested block or in the same block
- A common mistake is to declare a variable in a for loop and then try to use it outside the loop. As shown in the following code, i is declared in the for loop, but accessed outside the for loop, which will cause a syntax error.
for(int i= 0; i < 10;i++){ } //Causes a syntax error on i System.out.println(i);
6.10 example learning: generating random characters
- Characters are encoded using integers. Generating a random character is generating a random integer
- Each character has a unique Unicode with a value between hexadecimal number 0 and FFFF (i.e. 655 35 in decimal). To generate a random character is to use the following expression to generate a random integer from 0 to 65 535 (Note: since 0 < = math. Random() < 1.0, you must add 1 to 65 535):
(int)(Math.random() * (65535 + 1))
-
Now let's consider how to generate a random lowercase letter. The Unicode of a lowercase letter is a series of consecutive integers, starting with the Unicode of the lowercase letter 'a', followed by the Unicode of 'b', 'c',... And 'z' Unicode of a 'is:
(int)'a'
Therefore, the random integer between (int)'a' and (int)'z' is:
(int)((int)'a' + Math.random() * ((int)'z' - (int)'a' + 1))
As discussed in section 4.3.3, all numeric operators can be applied to char operands. If the other operand is a number or character, the char operand is converted to a number. In this way, the previous expression can be simplified as follows:
'a' + Math.random() * ('z' - 'a' + 1)
Thus, the random lowercase letters are:
(char)('a' + Math.random() * ('z' - 'a' + 1))
Thus, random characters between any two characters ch1 and ch2 can be generated, where ch1 < ch2, as follows:
(char)(ch1 + Math.random() * (ch2 - ch1 + 1))
This is a simple but useful discovery. In listing 6-10, create a class named RandomCharacter, which has five overloaded methods to randomly obtain a specific type of character. You can use these methods in future projects.
Program listing 6-10 randomcharacter java
public class RandomCharacter { /** * Generate a random character between ch1 and ch2 */ public static char getRandomCharacter(char ch1, char ch2){ return (char)(ch1 + Math.random() * (ch2 - ch1 + 1)); } /** * Generate a random lowercase letter */ public static char getRandomLowerCaseLetter(){ return getRandomCharacter('a','z'); } /** * Generate a random uppercase letter */ public static char getRandomUpperCaseLetter(){ return getRandomCharacter('A','Z'); } /** * Generate a random digit character */ public static char getRandomDigitCharacter(){ return getRandomCharacter('0','9'); } /** * Generate a random character */ public static char getRandomCharacter(){ return getRandomCharacter('\u0000','\uFFFF'); } }
Program listing 6-11 shows a test program that displays 175 random lowercase letters.
Program listing 6-11 testrandomcharacter java
public class TestRandomCharacter { /** * Main method * * @param args */ public static void main(String[] args) { final int NUMBER_OF_CHARS = 175; final int CHARS_PER_LINE = 25; //Print random characters between 'a' and 'z' , 25 chars per line for (int i = 0; i < NUMBER_OF_CHARS; i++) { char ch = RandomCharacter.getRandomLowerCaseLetter(); if ((i + 1) % CHARS_PER_LINE == 0) { System.out.println(ch); } else { System.out.print(ch); } } } }
- Line 9 calls the method getRandomLowerCaseLetter() in the RandomCharacter class that defines the child. Note that although the method getrandomlowercaseletter () does not have any parameters, parentheses are still required when defining and calling such methods.
6.11 method abstraction and gradual refinement
- The key to developing software is to apply abstract concepts
- method abstraction is achieved by separating the use of a method from its implementation. Users can use methods without knowing how they are implemented. The implementation details of the method are encapsulated in the method and hidden from the users using the method. This is called information hiding or encapsulation. If you decide to change the implementation of the method, the user's program will not be affected as long as the method signature is not changed. The implementation of the method is hidden in the "Black Box" for users.
- The system. Method has been used previously out. Print to display a string, and use the max method to find the maximum number. You also know how to write code in your program to call these methods. But as a user of these methods, you don't need to know how they are implemented.
- The concept of method abstraction can be applied to the process of program development. When writing a large program, you can use the "divide and conquer" strategy, also known as stepwise refinement, to decompose a large problem into subproblems. The subproblem is decomposed into smaller and easier problems
6.11.1 top down design
- How to start writing such a program? Will you start writing code immediately? Programming beginners often want to solve every detail from the beginning. Although details are important to the final procedure, paying too much attention to details in the early stage will hinder the process of solving the problem. In order to make the problem-solving process as smooth as possible, this example first uses method abstraction to separate the details from the design, and then realizes these details later
- For this example, the problem is divided into two sub problems: reading the user input and printing the calendar of the month. At this stage, it should be considered that it can be decomposed into a declarative subproblem, rather than how to read, input and print the whole calendar. You can draw a structure diagram, which helps to see the decomposition process of the problem. (Figure 6-8 shows that the printCalendar problem is decomposed into two sub problems - read input readInput and print calendar printMonth, as shown in figure a; while the printMonth problem is decomposed into two smaller problems - print calendar header printMonthTitle, print calendar header printMonthTitle and print calendar body printMonthBody, as shown in Figure b) Learn how to draw pictures in md documents in the future
- You can use Scanner to read the input of year and month. The problem of printing the calendar for a given month can be divided into two sub problems: printing the title of the calendar and the body of the calendar, as shown in figure 6-8b. The title of the monthly calendar consists of three lines: month, year, dotted line and the name of the week seven days a week. The whole process of the month (e.g. January) needs to be determined by the number representing the month (e.g. 1). This step is completed by getMonthName (see figure 6-9a)
- In order to print the main body of the calendar, you need to know the day of the week (getStartDay) and the number of days in the month (getNumberOfDaysInMonth), as shown in figure 6-9b. For example, December 2013 has 31 days, and December 1, 2013 is Sunday.
- How do you know what day of the week is the first day of the month? There are several ways to get it. Here, we use the following method. Suppose you know that January 1, 1800 is Wednesday (START_DAY_FOR_JAN_1_1800=3), and then calculate the total number of days (totalNumberOfDays) between January 1, 1800 and the first day of the calendar month. Because there are 7 days in each week, the week value of the first day of each month in the calendar is (totalNumberOfDays + START_DAY_FOR_JAN_1_1800)% 7. In this way, the getStartDay problem can be further refined into getTotalNumberOfDays, as shown in figure 6-10a.
- To calculate the total number of days, you need to know whether the year is a leap year and the number of days in each month. Therefore, getTotalNumberOfDays can be further refined into two sub problems: isLeapYear and getNumberOfDaysInMonth, as shown in figure 6-10b. The complete structure diagram is shown in Figure 6-11.
6.11.2 top down and bottom up implementation
- Now let's turn our attention to implementation. Usually, a subproblem corresponds to a method in the implementation, even if some subproblems are too simple to require a method to implement. You need to decide which modules should be implemented by methods and which modules should be combined with other methods. This decision should be based on whether the choice made makes the entire program more readable. In this example, the subproblem readInput can only be implemented in the main method.
- The method of "top-down" or "bottom-up" can be adopted. The "top-down" method is to implement one method in the structure diagram from top to bottom. The method to be implemented can be replaced by stub method, which is a simple but incomplete version of the method. Using the stub method can quickly build the framework of the program. First implement the main method, and then use the stub method of the printMonth method. For example, let the stub method of printMonth display the year and month, and the program starts in the following form:
import java.util.Scanner; public class PrintCalendar { /** * Main method */ public static void main(String[] args) { Scanner input = new Scanner(System.in); //Prompt the user to enter year System.out.println("Enter full year (e.g.,2012): "); int year = input.nextInt(); //Prompt the user to enter month System.out.println("Enter month as a number between 1 and 12: "); int month = input.nextInt(); //Print calendar for the month of the year printMonth(year, month); } /** * A stub for printMonth may look like this */ private static void printMonth(int year, int month) { System.out.println(month + " " + year); } /** * A stub for printMonthTitle may look like this */ public static void printMonthTitle(int year, int month) { } /** * A stub for printMonthBody may look like this */ public static void printMonthBody(int year, int month) { } /** * A stub for getMonthName may look like this */ public static String getMonthName(int month) { //A dummy value return "January"; } /** * A stub for getStartDay may look like this */ public static int getStartDay(int year, int month) { //A dummy value return 1; } /** * A stub for getTotalNumberOfDays may look like this */ public static int getTotalNumberOfDays(int year,int month) { //A dummy value return 10000; } /** * A stub for getNumberOfDaysInMonth may look like this */ public static int getNumberOfDaysInMonth(int year,int month) { //A dummy value return 31; } /** * A stub for isLeapYear may look like this */ public static Boolean isLeapYear(int year) { //A dummy value return true; } }
- Compile and test the program, and then correct all errors. Now, you can implement the printmonth method. The method of invoking in printMonth can continue to use the stub method.
- The bottom-up method is to implement a method in the structure diagram from bottom to top. A test program (called driver) is written for each implemented method to test. Both top-down and bottom-up are good methods: they are asymptotically implemented methods, which helps to separate programming errors and make debugging easier. The two methods can be used together.
6.11.3 implementation details
- As we know from section 3.11, the method isLeapYear(int year) can be implemented using the following code:
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
- Implement the getNumberOfDaysInMonth(int year, int month) method using the following facts:
- One, three, five, seven, eight, ten wax, thirty-one days never go wrong, forty-nine, thirty days in winter, and twenty-eight days in February
- To implement the getTotalNumberOfDays(int year,int month) method, you need to calculate the total number of days (totalNumberOfDays) between January 1, 1800 and the first day of the month to which the calendar belongs. You can calculate the total number of days from 1800 to the year of the calendar, and then calculate the total number of days before the month of the calendar in the year. The sum of these two total days is totalNumberOfDays.
- To print the calendar body, first fill in some spaces before the first day, and then print one line for each week.
- The complete procedure is shown in procedure list 6-12
Program listing 6-12 printcalendar java
package Based_On_Article.The_Textbook_Source_Code.Chapter06; import java.util.Scanner; public class PrintCalendar { /** * Main method */ public static void main(String[] args) { Scanner input = new Scanner(System.in); //Prompt the user to enter year System.out.println("Enter full year (e.g.,2012): "); int year = input.nextInt(); //Prompt the user to enter month System.out.println("Enter month as a number between 1 and 12: "); int month = input.nextInt(); //Print calendar for the month of the year printMonth(year, month); } /** * Print the calendar for a month in a year */ public static void printMonth(int year, int month) { //Print the headings of the calendar printMonthTitle(year, month); //Print the body of the calendar printMonthBody(year, month); } /** * Print the month title,e.g.,March 2012 */ public static void printMonthTitle(int year, int month) { System.out.println(" " + getMonthName(month) + " " + year); System.out.println("--------------------------"); System.out.println(" Sun Mon Tue Wed Thu Fri Sat "); } /** * Get the English name for the month */ 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; default: System.out.println("error");; } return monthName; } /** * Print month body */ public static void printMonthBody(int year, int month) { //Get start day of the week for the first date in the month int startDay = getStartDay(year, month); //Get number of day in the month int numberOfDaysInMonth = getNumberOfDaysInMonth(year, month); //Pad space before the first day of the 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(); } /** * Get the start day of month/1/year */ public static int getStartDay(int year, int month) { final int START_DAY_FOR_JAN_1_1800 = 3; //Get total number of days from 1/1/1800 to month/1/year int totalNumberOfDays = getTotalNumberOfDays(year, month); //Return the start day for month/1/year return (totalNumberOfDays + START_DAY_FOR_JAN_1_1800) % 7; } /** * Get the total number of days since January 1, 1800 */ public static int getTotalNumberOfDays(int year, int month) { int total = 0; //Get the total days from 1800 to 1/1/year for (int i = 1800; i < year; i++) { if (isLeapYear(i)) { total = total + 366; } else { total = total + 365; } } //Add days from Jan to the month prior to the calendar month for (int i = 1; i < month; i++) { total = total + getNumberOfDaysInMonth(year, i); } return total; } /** * Get the number of days in a month */ 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; } if (month == 4 || month == 6 || month == 9 || month == 11) { return 30; } if (month == 2) { return isLeapYear(year) ? 29 : 28; } //If month is incorrect return 0; } /** * Determine if it is a leap year */ public static boolean isLeapYear(int year) { return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); } }
- The program does not detect the validity of user input. For example, if the month entered by the user is not between 1 and 12, or the year is before 1800, the program will display the wrong calendar. To avoid such errors, you can add an IF statement to detect input before printing the calendar.
- The program can print a month's calendar and can be easily modified to print a whole year's calendar. Although it can only process the months after January 1800, it can print the months before 1800 with a little modification.
6.11.4 advantages of gradual refinement
- Step by step refinement decomposes a large problem into small and easy to deal with sub problems. Each subproblem can be implemented using one method. This method makes the problem easier to write, reuse, debug, modify and maintain
- Advantages:
- Simpler procedures
- The program for printing calendar is relatively long. The step-by-step refinement method is to decompose it into smaller methods instead of writing a long statement sequence in a method. This simplifies the program and makes the whole program easy to read and understand
- Reuse method
- Step by step refinement improves method reuse in a program. The isLeapYear method is defined only once and is called from the getTotalNumberOfDays and getNumberOfDaysInMonth methods. This reduces redundant code.
- Easy to develop, debug and test
- Because each subproblem is solved in one method, and one method can be developed, debugged and tested separately. This isolates errors and makes development, debugging, and testing easier
- When writing large programs, you can use a top-down or bottom-up approach. Don't write the whole program at once. Using these methods seems to waste more development time (because you have to compile and run the program repeatedly), but in fact, it will save more time and make debugging easier
- More convenient for teamwork
- When a large problem is decomposed into many subproblems, each subproblem can be assigned to different programmers. This makes it easier for programmers to work in teams
- Simpler procedures