Part 3 section 1. I take no credit, I only pass on knowledge. All credit to Allan Young, Brian Barnes, and their sources. Sorry if indentation is incorrect, thats my fault
Chapter 3: Methods3.1 Floating-pointIn the last chapter we had some problems dealing with numbers that were not integers. We worked around the problem by measuring percentages instead of fractions, but a more general solution is to use
floating-point numbers, which can represent fractions as well as integers. In Java, the floating-point type is called double.
You can create floating-point variables and assign values to them using the same syntax we used for the other types. For example:
1...
2double pi;
3pi = 3.14159;
4...
It is also legal to declare a variable and assign a value to it at the same time:
1...
2 int x = 1;
3 String empty = "";
4 double pi = 3.14159;
5...
In fact, this syntax is quite common. A combined declaration and assignment is sometimes called an
initialization.
Although floating-point numbers are useful, they are often a source of confusion because there seems to be an overlap between integers and floating-point numbers. For example, if you have the value 1, is that an integer, a floating-point number, or both?
Strictly speaking, Java distinguishes the integer value 1 from the floating-point value 1.0, even though they seem to be the same number. They belong to different types, and strictly speaking, you are not allowed to make assignments between types. We will reference the following code in the next few paragraphs.
1 ...
2 int x = 1.1; //illegal expression
3 double y = 1; //y = 1.0 technically legal
4 double y = 1 / 3; //y = 0.0 technically legal with unexpected
results
double y = 1.0 / 3.0; //y = 0.33333 legal with expected results
Line 2 is illegal because the variable on the left is an int and the value on the right is a double. But it is easy to forget this rule, especially because there are places where Java will automatically convert from one type to another.
For example, line 3 should technically not be legal, but Java allows it by converting the int to a double automatically. This leniency is convenient, but it can cause problems; take line 4 for example. You might expect the variable y to be given the value 0.333333, which is a legal floating-point value, but in fact it will get the value 0.0. The reason is that the expression on the right appears to be the ratio of two integers, so Java does integer division, which yields the integer value 0. Converted to floating-point, the result is 0.0.
One way to solve this problem (once you figure out what it is) is to make the right-hand side a floating-point expression as we did in line 5. This sets y to 0.333333, as expected.
All the operations we have seen so far-addition, subtraction, multiplication, and division-also work on floating-point values, although you might be interested to know that the underlying mechanism is completely different. In fact, most processors have special hardware just for performing floating-point operations.
3.1.1 Exponential NotationFloating-point numbers can also be written in exponential notation, which is similar to scientific notation and is commonly used to express both very large and very small values in compact form. The following examples illustrate how numbers with decimals can be expressed in exponential and scientific notation.
Decimal notation Exponential notation Scientific notation 1625. 1.625E3 1.625 x 10
3 63421. 6.3421E4 6.3421 x 10
4 0.00731 7.31E-3 7.31 x 10
-3 0.000625 6.25E-4 6.25 x 10
-4 In exponential notation, the letter
E stands for the exponent. The number following the
E represents a power of 10 and indicates the number of places the decimal point should be moved to obtain the standard decimal value. The decimal point is moved to the right if the number after the
E is positive or moved to the left if the number after the
E is negative.
3.2 Converting from double to intAs I mentioned, Java converts ints to doubles automatically if necessary, because no information is lost in the translation. On the other hand, going from a double to an int requires rounding off. Java doesn't perform this operation automatically, in order to make sure that you, as the programmer, are aware of the loss of the fractional part of the number.
The simplest way to convert a floating-point value to an integer is to use a
typecast. Typecasting is so called because it allows you to take a value that belongs to one type and "cast" it into another type (in the sense of molding or reforming, not throwing).
Unfortunately, the syntax for typecasting is ugly: you put the name of the type in parentheses and use it as an operator. For example,
?1 ...
2 int x = (int) 3.14159; //x = 3
3 double y = (int) 3.14159 * 20.0; //y = 60.0
4...
The (int) operator has the effect of converting what follows into an integer, so x gets the value 3. Typecasting takes precedence over arithmetic operations, so in line 3, the value of PI gets converted to an integer first, and the result is 60.0, not 62.0.
Converting to an integer always rounds down, even if the fraction part is 0.99999999. These two properties (precedence and rounding) can make typecasting error-prone.
3.3 Math methodsIn mathematics, you have probably seen functions like sin and log, and you have learned to evaluate expressions like sin(
π/2) and log(1/
x). First, you evaluate the expression in parentheses, which is called the argument of the function. For example,
π/2 is approximately 1.571, and 1/
x is 0.1 (assuming that
x is 10).
Then you can evaluate the function itself, either by looking it up in a table or by performing various computations. The sin of 1.571 is 1, and the log of 0.1 is -1 (assuming that log indicates the logarithm base 10).
This process can be applied repeatedly to evaluate more complicated expressions like log(1/ sin(
π/2)). First we evaluate the argument of the innermost function, then evaluate the function, and so on.
Java provides a set of built-in functions that includes most of the mathematical operations you can think of. These functions are called
methods. Most math methods operate on doubles.
The math methods are invoked using a syntax that is similar to the print and println commands we have already seen:
?1 ...
2 double root = Math.sqrt (17.0); //root = 4.1231...
3 double angle = 1.5;
4 double height = Math.sin (angle); //height = 0.99749...
5 ...
The first example sets root to the square root of 17. The second example finds the sine of 1.5, which is the value of the variable angle. Java assumes that the values you use with sin and the other trigonometric functions (cos, tan) are in radians. To convert from degrees to radians, you can divide by 360 and multiply by 2π. Conveniently, Java provides π as a built-in value:
1 ...
2 double degrees = 90;
3 double angle = degrees * 2 * Math.PI / 60.0; //angle= .5707...
4 int x = Math.round (float)(Math.PI * 20.0); //x = 63
5 ...
Notice that PI is in all capital letters. Java does not recognize Pi, pi, or pie. Another useful method in the Math
class is round, which rounds a floating-point value off to the nearest integer and returns an int.
In line 3, the multiplication happens first, before the method is invoked. The result is 63 (rounded up from 62.8319).
For additional information and methods in the math class google "Java math class".
3.4 CompositionJust as with mathematical functions, Java methods can be
composed, meaning that you use one expression as part of another. For example, you can use any expression as an argument to a method:
1 ...
2 angle = 1.5707
3 double x = Math.cos (angle + Math.PI/2); //x = -0.99999...
4 double y = Math.exp (Math.log (10.0)); //y = 10.00000...
5...
Line 3 takes the value Math.PI, divides it by two and adds the result to the value of the variable angle. The sum is then passed as an argument to the cos method. (Notice that PI is the name of a variable, not a method, so there are no arguments, not even the empty argument ()).
You can also take the result of one method and pass it as an argument to another like in line 4.
In Java, the log function always uses base
e, so this statement finds the log base
e of 10 and then raises
e to that power. The result gets assigned to y. The log function in Java would be equivalent to the natural log on your calculator.
To view the rest of the methods available to you in the Java Math class visit
Math Class' API 3.5 Formating output with printfWith the introduction of floating-point numbers, an issue of how we format output comes into play. Formating your output is a little tough to get used to but once you become comfortable with it, it will become as easy as println. For example when you run the code to solve for square root of 17 and output it, you will find the ouput to be less than desirable. Do you really need 15 decimal places in your ouput?
Earlier you saw the use of the print and println methods for printing strings to standard output (System.out). Since all numbers can be converted to strings, you can use these methods to print out an arbitrary mixture of strings and numbers. The Java programming language has other methods, however, that allow you to exercise much more control over your print output when numbers are included.
The java.io package includes a PrintStream class that has two formatting methods that you can use to replace print and println. The method we will focus on in this course is printf method. The familiar System.out that you have been using happens to be a PrintStream object, so you can invoke PrintStream methods on System.out. Thus, you can use printf anywhere in your code where you have previously been using print or println. The syntax for the printf method is as follows:
1 public PrintStream printf(String format, Object... args) where printf is a string that specifies the formatting to be used and args is a list of the variables to be printed using that formatting. A simple example would be
1 ...
2 double floatVar = 3.14159;
3 int intVar = 15;
4 String stringVar = "sample string";
5 System.out.printf("The value of the float variable is %f, " +
6 "while the value of the integer variable is %d, and the " +
7 "string is %s", floatVar, intVar, stringVar);
8...
The first parameter, printf, is a format string specifying how the objects in the second parameter, args, are to be formatted. The format string contains plain text as well as
format specifiers, which are special characters that format the arguments of Object... args. (The notation Object... args is called
varargs, which means that the number of arguments may vary.)
Format specifiers begin with a percent sign (%) and end with a
converter. The converter is a character indicating the type of argument to be formatted. In between the percent sign (%) and the converter you can have optional flags and specifiers. There are many converters, flags, and specifiers, which are documented in
java.util.Formatter or
hereHere is a basic example:
1 int i = 461012;
2 System.out.format("The value of i is: %d%n", i);
3 System.out.println("That wasn't so bad");
Would output:
1
2 The value of i is: 461012
3 That wasn't so bad
printf Commands
Type Representation Explaination Converter d an integer Converter f a decimal number Converter s a string Converter n A new line character appropriate to the platform running the application Precision .X Where X>0, number of places after the decimal point. The value is right-truncated if necessary Width X Where X>0, the minimum width of the formatted value; the value is padded if necessary. By default the value is left-padded with blanks. Flag + Includes sign, whether positive or negative. Flag , Includes locale-specific grouping characters. Flag - Left-justified. Flag 0 Indicates 0 is the padding character
The following program shows some of the formatting that you can do with printf. The output is shown within double quotes in the embedded comments:
1 public class TestFormat {
2 public static void main(String[] args) {
3 int n = 461012;
4 System.out.printf("%d%n", n); // --> "461012"
5 System.out.printf("%08d%n", n); // --> "00461012"
6 System.out.printf("%+8d%n", n); // --> " +461012"
7 System.out.printf("%,8d%n", n); // --> " 461,012"
8 System.out.printf("%+,8d%n%n", n); // --> "+461,012"
9
10 double pi = Math.PI;
11 System.out.printf("%.3f%n", pi); // --> "3.142"
12 System.out.printf("%10.3f%n", pi); // --> " 3.142"
13 System.out.printf("%-10.3f%n", pi); // --> "3.142 "
14
15 String name = "Joe";
16 System.out.printf("Name: %s%n", name); // --> "Joe"
17 System.out.printf("Name: %5s%n", name); // --> " Joe"
18 System.out.printf("Name: %-5s%n", name); // --> "Joe "
19 }
10}
3.6 Adding new methodsSo far we have only been using the methods that are built into Java, but it is also possible to add new methods. Actually, we have already seen one method definition: main. The method named main is special in that it indicates where the execution of the program begins, but the syntax for main is the same as for other method definitions:
...
/**
* What does the method do?
* can span more than one line keeping within the 80 character limit
*
* @param #1 brief description of expected input for param #1
* @param #2 brief description of expected input for param #2
* @param etc... etc
*/
public static void NAME ( LIST OF PARAMETERS ) {
STATEMENTS
}
...
The first thing you notice in the above code is the long line of *'s. This is how you will be expected to comment each of your methods. The first few lines of text document what the method's purpose is followed by a list of the parameters and a brief explanation of what their expected inputs are.
You can make up any name you want for your method, except that you can't call it main or any other Java keyword. Although a method name can be any legal identifier, code conventions restrict method names. By convention, method names should be a verb in lowercase or a multi-word name that begins with a verb in lowercase, followed by adjectives, nouns, etc. In multi-word names, the first letter of each of the second and following words should be capitalized. Here are some examples:
run
runFast
getBackground
getFinalData
compareTo
setX
isEmpty
The list of
parameters specifies what information, if any, you have to provide in order to use (or
invoke) the new function.
The single parameter for main is String[] args, which indicates that whoever invokes main has to provide an array of Strings (we'll get to arrays in Chapter 10). The first couple of methods we are going to write have no parameters, so the syntax looks like this:
...
public static void newLine () {
System.out.println ("");
}
...
This method is named newLine, and the empty parentheses indicate that it takes no parameters. The method above contains only a single statement, which prints an empty String, indicated by "". Printing a String with no letters in it may not seem all that useful, except remember that println skips to the next line after it prints, so this statement has the effect of skipping to the next line.
In main we can invoke this new method using syntax that is similar to the way we invoke the built-in Java commands:
...
public static void main (String[] args) {
System.out.println ("First line.");
newLine ();
System.out.println ("Second line.");
}
...
The output of this program is
First line.
Second line.
Notice the extra space between the two lines. What if we wanted more space between the lines? We could invoke the same method repeatedly:
...
public static void main (String[] args) {
System.out.println ("First line.");
newLine ();
newLine ();
newLine ();
System.out.println ("Second line.");
}
...
Or we could write a new method, named threeLine, that prints three new lines:
...
public static void main (String[] args) {
System.out.println ("First line.");
threeLine ();
System.out.println ("Second line.");
}
public static void threeLine () {
newLine ();
newLine ();
newLine ();
}
...
You should notice a few things about this program:
- You can invoke the same procedure repeatedly. In fact, it is quite common and useful to do so.
- You can have one method invoke another method. In this case, main invokes threeLine and threeLine invokes newLine. Again, this is common and useful.
So far, it may not be clear why it is worth the trouble to create all these new methods. Actually, there are a lot of reasons, but this example only demonstrates two:
- Creating a new method gives you an opportunity to give a name to a group of statements. Methods can simplify a program by hiding a complex computation behind a single command, and by using English words in place of arcane code. Which is clearer, newLine () or System.out.println ("")?
- Creating a new method can make a program smaller by eliminating repetitive code. For example, how would you print nine consecutive new lines? You could just invoke threeLine three times.
3.7 Classes and methodsPulling together all the code fragments from the previous section, the whole class definition looks like this:
class NewLine {
public static void main (String[] args) {
System.out.println ("First line.");
threeLine ();
System.out.println ("Second line.");
}
public static void newLine () {
System.out.println ("");
}
public static void threeLine () {
newLine ();
newLine ();
newLine ();
}
}
The first line indicates that this is the class definition for a new class called NewLine. A
class is a collection of related methods. In this case, the class named NewLine contains three methods, named newLine, threeLine, and main.
The other class we've seen is the Math class. It contains methods named sqrt, sin, and many others. When we invoke a mathematical function, we have to specify the name of the class (Math) and the name of the function. That's why the syntax is slightly different for built-in methods and the methods that we write:
...
double x;
x = Math.pow (2.0, 10.0); //legal Math class call
newLine (); //legal in class function
//only legal if the method pow exists in the YOUR class
pow (2.0, 10.0);
...
The first statement invokes the pow method in the Math class (which raises the first argument to the power of the second argument). The second statement invokes the newLine method, which Java assumes exists inside your current class (in this case NewLine which is what we are writing).
If you try to invoke a method from the wrong class, the compiler will generate an error. For example, line 5, the compiler will say something like, "Can't find a method named pow in class NewLine." If you have seen this message, you might have wondered why it was looking for pow in your class definition. Now you know.
3.8 Programs with multiple methodsWhen you look at a class definition that contains several methods, it is tempting to read it from top to bottom, but that is likely to be confusing, because that is not the
order of execution of the program.
Execution always begins at the first statement of main, regardless of where it is in the program. Statements are executed one at a time, in order, until you reach a method invocation. Method invocations are like a detour in the flow of execution. Instead of going to the next statement, you go to the first line of the invoked method, execute all the statements there, and then come back and pick up again where you left off.
That sounds simple enough, except that you have to remember that one method can invoke another. Thus, while we are in the middle of main, we might have to go off and execute the statements in threeLine. But while we are executing threeLine, we get interrupted three times to go off and execute newLine.
For its part, newLine invokes the built-in method println, which causes yet another detour. Fortunately, Java is quite adept at keeping track of where it is, so when println completes, it picks up where it left off in newLine, and then gets back to threeLine, and then finally gets back to main so the program can terminate.
Actually, technically, the program does not terminate at the end of main. Instead, execution picks up where it left off in the program that invoked main, which is the Java interpreter. The Java interpreter takes care of things like deleting windows and general cleanup, and
then the program terminates.
What's the moral of this sordid tale? When you read a program, don't read from top to bottom. Instead, follow the flow of execution.
3.9 Parameters and argumentsSome of the built-in methods we have used have
parameters, which are values that you provide to let the method do its job. For example, if you want to find the sine of a number, you have to indicate what the number is. Thus, Math.sin takes a double value as a parameter. To print a string, you have to provide the string, which is why println takes a String as a parameter.
Some methods take more than one parameter, like Math.pow, which takes two doubles, the base and the exponent.
Notice that in each of these cases we have to specify not only how many parameters there are, but also what type they are. So it shouldn't surprise you that when you write a class definition, the parameter list indicates the type of each parameter. For example:
...
public static void printTwice (String string) {
System.out.println (string);
System.out.println (string);
}
...
This method takes a single parameter, named string, that has type String. Whatever that parameter is (and at this point we have no idea what it is), it gets printed twice. Anytime you create a method that takes in a generic parameter, the variable should have the same name as it's type. Now, if the parameter is specific, than the naming convention follows that of standard variable declarations seen in last chapter.
In order to invoke this method, we have to provide a String. For example, we might have a main method like this:
...
public static void main (String[] args) {
printTwice ("Don't make me say this twice!");
}
...
The string you provide is called an
argument, and we say that the argument is
passed to the method. In this case we are creating a string value that contains the text "Don't make me say this twice!" and passing that string as an argument to printTwice where, contrary to its wishes, it will get printed twice.
Alternatively, if we had a String variable, we could use it as an argument instead:
...
public static void main (String[] args) {
String simpleSaying = "Never say never.";
printTwice (simpleSaying);
}
...
Notice something very important here: the name of the variable we pass as an argument (simpleSaying) has nothing to do with the name of the parameter (string). Let me say that again:
The name of the variable we pass as an argument has nothing
to do with the name of the parameter. They can be the same or they can be different, but it is important to realize that they are not the same thing, except that they happen to have the same value (in this case the string "Never say never.").
The value you provide as an argument must have the same type as the parameter of the method you invoke. This rule is very important, but it often gets complicated in Java for two reasons:
- There are some methods that can accept arguments with many different types. For example, you can send any type to print and println, and it will do the right thing no matter what. This sort of thing is an exception, though.
- If you violate this rule, the compiler often generates a confusing error message. Instead of saying something like, "You are passing the wrong kind of argument to this method," it will probably say something to the effect that it could not find a method with that name that would accept an argument with that type. Once you have seen this error message a few times, though, you will figure out how to interpret it.
3.10 Variable ScopeWith having variables in multiple places in your program, we have to introduce the concept of
scope. Scope is basically where a variable can be referenced from and still contain the value you expect it to have. When you declare variables inside a particular method, that variable only has value inside that method. Later on in computer science, you will be exposed to class-level variables which can be used any where in that class. Here is an example of variable scope
...
public static void main (String[] args) {
int x = 2;
System.out.println("the value of x is: " + x); //x = 2
sqrIt (x);
System.out.println("the value of x is: " + x); //x = 2
}
public static void sqrIt (int someInt) {
int x = 3
System.out.println("the value squared is: " + someInt * someInt);
System.out.println("the value of x is: " + x); //x = 3
}
Notice when we declare and use
x inside the method sqrIt it does not contain any reference to the
x declared in main and vice versa. Make sure you do NOT reference variables outside of their scope or you will encounter an error.
3.11 Methods with multiple parametersThe syntax for declaring and invoking methods with multiple parameters is a common source of errors. First, remember that you have to declare the type of every parameter. For example
...
public static void printTime (int hour, int minute) {
System.out.print (hour);
System.out.print (":");
System.out.println (minute);
}
...
It might be tempting to write (int hour, minute) on line 2, but that format is only legal for variable declarations, not for parameters.
3.12 Methods with resultsYou might have noticed by now that some of the methods we are using, like the Math methods, yield results. Other methods, like println and newLine, perform some action but they don't return a value. That raises some questions:
- What happens if you invoke a method and you don't do anything with the result (i.e. you don't assign it to a variable or use it as part of a larger expression)?
- What happens if you use a print method as part of an expression, like System.out.println ("boo!") + 7;
- Can we write methods that yield results, or are we stuck with things like newLine and printTwice?
The answer to the third question is "yes, you can write methods that return values," and we'll do it in a couple of chapters. I will leave it up to you to answer the other two questions by trying them out. In fact, any time you have a question about what is legal or illegal in Java, a good way to find out is to ask the compiler.
3.13 Code style 6. Names representing methods must be verbs and written in mixed case starting with lower case. getName(), computeTotalWidth() Common practice in the Java development community and also the naming convention used by Sun for the Java core packages. This is identical to variable names, but methods in Java are already distinguishable from variables by their specific form.
9. Generic variables should have the same name as their type. void setTopic(Topic topic) // NOT: void setTopic(Topic value)
// NOT: void setTopic(Topic aTopic)
// NOT: void setTopic(Topic t)
void connect(Database database) // NOT: void connect(Database db)
// NOT: void connect(Database oracleDB) Reduce complexity by reducing the number of terms and names used. Also makes it easy to deduce the type given a variable name only. If for some reason this convention doesn't seem to fit it is a strong indication that the type name is badly chosen.
Non-generic variables have a role. These variables can often be named by combining role and type:
Point startingPoint, centerPoint;
Name loginName;
15. The term compute can be used in methods where something is computed. computeAverage(), computeInverse() Give the reader the immediate clue that this is a potential time consuming operation, and if used repeatedly, he might consider caching the result. Consistent use of the term enhances readability.
16. The term find can be used in methods where something is looked up. findNearestVertex();
findSmallestElement();
findShortestPath(Node destinationNode); Give the reader the immediate clue that this is a simple look up method with a minimum of computations involved. Consistent use of the term enhances readability.
42. Type conversions must always be done explicitly. Never rely on implicit type conversion. floatValue = (int) intValue; // NOT: floatValue = intValue; By this, the programmer indicates that he is aware of the different types involved and that the mix is intentional.
58. Floating point constants should always be written with decimal point and at least one decimal. double total = 0.0; // NOT: double total = 0;
double speed = 3.0e8; // NOT: double speed = 3e8;
double sum;
sum = (a + b) * 10.0; // NOT: (a + b) * 10 This emphasize the different nature of integer and floating point numbers. Mathematically the two model completely different and non-compatible concepts.
Also, as in the last example above, it emphasize the type of the assigned variable sum at a point in the code where this might not be evident.
59. Floating point constants should always be written with a digit before the decimal point. double total = 0.5; // NOT: double total = .5; The number and expression system in Java is borrowed from mathematics and one should adhere to mathematical conventions for syntax wherever possible. Also, 0.5 is a lot more readable than .5; There is no way it can be mixed with the integer 5.
74. Method names can be followed by a white space when it is followed by another name. doSomething (currentFile);
doSomethingElse(); Makes the individual names stand out. Enhances readability. When no name follows, the space can be omitted since there is no doubt about the name in this case.
76. Methods should be separated by three blank lines. By making the space larger than space within a method, the methods will stand out within the class.
81. Javadoc comments should have the following form: /**
* Return lateral location of the specified position.
* If the position is unset, NaN is returned.
*
* @param x X coordinate of position.
* @param y Y coordinate of position.
* @param zone Zone of position.
* @return Lateral location.
*/
public double computeLocation(double x, double y, int zone) { A readable form is important because this type of documentation is typically read more often inside the code than it is as processed text.
Note in particular:
- The opening /** on a separate line
- Subsequent * is aligned with the first one
- Space after each *
- Empty line between description and parameter section.
- Alignment of parameter descriptions.
- Punctuation behind each parameter description.
- No blank line bewteen the documentation block and the method/class.
86. All public classes and public and protected functions within public classes should be documented using the Java documentation (javadoc) conventions. This makes it easy to keep up-to-date online code documentation.