Java学习笔记(6)Methods

时间:2023-02-25 11:33:51

6.2 Defining a Method

A method definition consists of its method name, parameters, return value type, and body.

Java学习笔记(6)Methods

value-returning method: If a method returns a value, it is called avalue-returning method; otherwise it is called avoid method.

formal parameters : The variables defined in the method header are known asformal parametersor simply parameters.

actual parameter: A parameter is like a placeholder: when a method is invoked, you pass a value to the parameter. This value is referred to as anactual parameter or argument.

Parameter list : The parameter list refers to the method’s type, order, and number of the parameters.

method signature: The method name and the parameter list together constitute themethod signature.


6.3 Calling a Method

Java学习笔记(6)Methods

注意:有返回值的方法,一定要有 return 语句,并且确保任何一个分支都能遇到 return 语句。

从其它类调用TestMax类的方法:

方法的好处是可以重用。例如刚才的 max 方法,除了TestMax类内部自己调用,你也可以在TestMax这个类的外部调用。在类的外部,调用类中的方法,Java的调用格式是:ClassName.methodName

具体到刚才的max方法,格式就是:TestMax.max

为了记录方法间的调用顺序,以便能够在调用结束后正确返回调用处,Java采用栈这种数据结构来记录调用信息

Java学习笔记(6)Methods

6.4 void Method Example

A void method does not return a value.


6.5 Passing Arguments by Values

LISTING6.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);
	 }
 }

 Java学习笔记(6)Methods


LISTING6.5 TestPassByValue.java

 public class TestPassByValue {
	 /** Main method */
	 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);
	 }
 }

 Java学习笔记(6)Methods

传值调用图解:

Java学习笔记(6)Methods

6.6 Modularizing Code

Modularizing makes the code easy to maintain and debug and enables the code to be reused.

Methods can be used to reduce redundant code and enable code reuse. Methods can also be used to modularize code and improve the quality of the program.

 Listing 5.9 gives a program that prompts the user to enter two integers and displays their greatest common divisor. You can rewrite the program using a method, as shown in Listing 6.6.


LISTING 6.6 GreatestCommonDivisorMethod.java

 import java.util.Scanner;

 public class GreatestCommonDivisorMethod {
	 /** Main method */
	 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) {
		 int gcd = 1; // Initial gcd is 1
		 int k = 2; // Possible gcd

		 while (k <= n1 && k <= n2) {
			 if (n1 % k == 0 && n2 % k == 0)
				 gcd = k; // Update gcd
			 k++;
		 }

		 return gcd; // Return gcd
	 }
 }

Listing 6.7 applies the concept of code modularization to improve Listing 5.15, PrimeNumber.java.


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) {
		 final int NUMBER_OF_PRIMES_PER_LINE = 10; // Display 10 per line
		 int count = 0; // Count the number of prime numbers
		 int number = 2; // A number to be tested for primeness

		 // Repeatedly find prime numbers
		 while (count < numberOfPrimes) {
		 // Print the prime number and increase the count
			 if (isPrime(number)) {
				 count++; // Increase the count

				 if (count % NUMBER_OF_PRIMES_PER_LINE == 0) {
					 // Print the number and advance to the new line
					 System.out.printf("%-5s\n", number);
				 }
				 else
					 System.out.printf("%-5s", number);
			 }

			 // Check whether the next number is prime
			 number++;
		 }
	 }

	 /** Check whether number is prime */
	 public static boolean isPrime(int number) {
		 for (int divisor = 2; divisor <= number / 2; divisor++) {
			 if (number % divisor == 0) { // If true, number is not prime
				 return false; // Number is not a prime
			 }
		 }

		 return true; // Number is prime
	 }
 }

 

Listing 5.11, Dec2Hex.java, gives a program that converts a decimal to a hexadecimal.

A brute-force approach is to convert each hex character into a decimal number, multiply it by 16i for a hex digit at thei’s position, and then add all the items together to obtain the equivalent decimal value for the hex number.

Note that

hn * 16n + hn - 1 * 16n- 1 +hn- 2 * 16n - 2 + ……+h1 * 161 +h0 * 160= (……((hn * 16 +hn - 1) * 16 +hn- 2) * 16 +……+h1) * 16 + h0

This observation, known as the Horner’s algorithm, leads to the following efficient code for converting a hex string to a decimal number:

int decimalValue = 0;

for (inti =0; i < hex.length(); i++) {

char hexChar = hex.charAt(i);

decimalValue = decimalValue * 16+ hexCharToDecimal(hexChar);

}


LISTING 6.8 Hex2Dec.java

 import java.util.Scanner;

 public class Hex2Dec {
	 /** Main method */
	 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 Overloading Methods

方法重载指的是同一个类拥有多个相同名字的方法。

LISTING 6.9 TestMethodOverloading.java

 public class TestMethodOverloading {
	 /** Main method */
	 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 the 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, 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);
	 }
 }

方法重载的要素

1、方法名一定相同,到底执行哪个由实参的个数和类型决定

2、参数列表一定不同,即至少满足以下一项:参数个数不同; 参数类型不同

3、返回值类型不能作为重载标识


6.9歧义调用(Ambiguous Invocation)

由于Java会进行隐式的参数类型转换,因此当方法重载时,可能会出现多个方法都符合调用的实参的情况,此时Java无法决定被调用的方法是哪一个,于是会报编译出错。这种情况叫做歧义调用。

歧义调用的例子:

 Java学习笔记(6)Methods

6.10变量的作用范围

1、局部变量: 在方法内部定义的变量

2、作用范围: 可以访问到该变量的代码部分

    局部变量的作用范围是从声明的地方开始,直到它所在的语句块结束(也就是包含它的最近的那个右括号’}’)

3、局部变量需要先声明后使用。

for语句的循环变量

for语句可以定义变量,此时该变量的作用范围仅仅局限于for内部,如下面的i;for语句内部也可以定义变量,作用范围也在for内部,如下面的j:

Java学习笔记(6)Methods

具有嵌套关系的语句块中,不允许定义同名变量。注意以下两段代码的不同之处,左边正确,右边错误。


 Java学习笔记(6)Methods

再看一个错误的例子

 Java学习笔记(6)Methods


6.11方法的好处

1、模块化(参见L6.7 L6.8  L6.9)

2、可重用(同一个类内L6.10或不同类L6.11之间)

3、封装-隐藏实现细节

4、降低编程复杂性-在设计、实现、调试和维护各个方面


6.12逐步细化的编程方法

编写大型程序的时候,分治法(divide and conquer)或者逐步细化(stepwise refinement)是最常用的做法。

下面用一个例题来阐述这种编程方法。

例题.打印一个日历:编程实现日历打印。输入年和月份,输出该月份的日历。程序运行效果如图:

Java学习笔记(6)Methods

Java学习笔记(6)Methods


6.13.1 编码实现:自顶向下( Top-down)

把刚才的设计图按照自顶向下的设计思路来实现,首先开始搭程序框架,下面是main函数的造型:


<pre name="code" class="java">public static void main(String[] args) {
	Scanner input = new Scanner(System.in);
        // Prompt the user to enter year
	System.out.print("Enter full year (e.g., 2012): ");
	int year = input.nextInt();
	// Prompt the user to enter month
	System.out.print("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 */

public static void printMonth(intyear,intmonth){

System.out.print(month + " "+ year);

}

/** A stub for printMonthTitle may look like this */

public static void printMonthTitle(intyear,intmonth){

}

/** A stub for getMonthBody may look like this */

public static void printMonthBody(intyear,intmonth){

}

/** A stub for getMonthName may look like this */

public static String getMonthName(intmonth) {

return"January"; // A dummy value

}

/** A stub for getStartDay may look like this */

public static int getStartDay(intyear,intmonth) {

return1; // A dummy value

}

/** A stub for getTotalNumberOfDays may look like this */

public static int getTotalNumberOfDays(intyear,intmonth) {

return10000; // A dummy value

}

/** A stub for getNumberOfDaysInMonth may look like this */

public static int getNumberOfDaysInMonth(intyear,intmonth) {

return31; // A dummy value

}

/** A stub for isLeapYear may look like this */

public static Boolean isLeapYear(intyear) {

return true; // A dummy value

}

总结:

1、上述的框架已经可以运行,虽然结果现在还不完善,至少可以确保整个设计思路没有遗漏。

2、这种逐步细化的搭框架编程方法,符合我们平时解决大问题的思维方式。

3、接下去的工作,就是一个个函数慢慢填空……

6.13.2编码实现: 自底向上( Bottom-up)

还有一种编程思路和上面所讲的相反,它是先搭小模块,然后组装模块,最后形成一个大程序。例如上面这个例子,自底向上的方式是先把每一个需要的函数写完,测试通过后,再组装到一起的。

无论哪种方法都是可用的,常常会结合到一起用,自己看着办。

6.13.3 Implementation Details

The isLeapYear(int year) method can be implemented using the following code from Section before:

returnyear %400==0|| (year % 4==0&& year %100!=0);

Use the following facts to implement getTotalNumberOfDaysInMonth(int year, int month):

January, March, May, July, August, October, and December have 31 days.

April, June, September, and November have 30 days.

February has 28 days during a regular year and 29 days during a leap year. A regular year, therefore, has 365 days, a leap year 366 days.

To implement getTotalNumberOfDays(int year, int month), you need to compute the total number of days (totalNumberOfDays) between January 1, 1800, and the first day of the calendar month. You could find the total number of days between the year 1800 and the calendar year and then figure out the total number of days prior to the calendar month in the calendar year. The sum of these two totals istotalNumberOfDays.

To print a body, first pad some space before the start day and then print the lines for every week.

The complete program is given in Listing 6.12.

LISTING 6.12 PrintCalendar.java

 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.print("Enter full year (e.g., 2012): ");
		 int year = input.nextInt();

		 // Prompt the user to enter month
		 System.out.print("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";
		 }

		 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 days 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;

		 return 0; // If month is incorrect
	 }

	 /** 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 validate user input. For instance, if the user enters either a month not in the range between1and12or a year before1800, the program displays an erroneous calendar. To avoid this error, add anifstatement to check the input before printing the calendar. This program prints calendars for a month but could easily be modified to print calendars for a whole year. Although it can print months only after January 1800, it could be modified to print months before1800.


CHAPTER 6 SUMMARY

1. Making programs modular and reusable is one of the central goals in software engineering. Java provides many powerful constructs that help to achieve this goal. Methodsare one such construct.

2. The method header specifies themodifiers,return value type,method name, and parameters of the method. Thestaticmodifier is used for all the methods in this chapter.

3. A method may return a value. ThereturnValueTypeis the data type of the value the method returns. If the method does not return a value, thereturnValueTypeis the keywordvoid.

4. The parameter listrefers to the type, order, and number of a method’s parameters. The method name and the parameter list together constitute the method signature.Parameters are optional; that is, a method doesn’t need to contain any parameters.

5. A return statement can also be used in avoidmethod for terminating the method and returning to the method’s caller. This is useful occasionally for circumventing the normal flow of control in a method.

6. The arguments that are passed to a method should have the same number, type, and order as the parameters in the method signature.

7. When a program calls a method, program control is transferred to the called method. A called method returns control to the caller when its return statement is executed or when its method-ending closing brace is reached.

8. A value-returning method can also be invoked as a statement in Java. In this case, the caller simply ignores the return value.

9. A method can be overloaded. This means that two methods can have the same name, as long as their method parameter lists differ.

10. A variable declared in a method is called a local variable. Thescope of a local variable starts from its declaration and continues to the end of the block that contains the variable. A local variable must be declared and initialized before it is used.

11. Method abstractionis achieved by separating the use of a method from its implementation. The client can use a method without knowing how it is implemented. The details of the implementation are encapsulated in the method and hidden from the client who invokes the method. This is known asinformation hidingor encapsulation.

12. Method abstraction modularizes programs in a neat, hierarchical manner. Programs written as collections of concise methods are easier to write, debug, maintain, and modify than would otherwise be the case. This writing style also promotes method reusability.

13. When implementing a large program, use the top-down and/or bottom-up coding approach. Do not write the entire program at once. This approach may seem to take more time for coding (because you are repeatedly compiling and running the program), but it actually saves time and makes debugging easier.