We started (after getting locked out of our room again and trying two different replacements) by talking about primitive data types versus classes. You can read about the difference in yesterday's lecture notes.
In order to introduce for
loops, we created a program to have the user enter in 5 numbers, keeping a running total of the sum. Here is our first draft:
import java.util.Scanner;
public class summer {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
double sum = 0;
for(int i=0; i<5; i++) {
System.out.println("Enter a number:");
sum = sum + s.nextDouble();
System.out.println("The sum is " + sum);
}
}
}
There are three arguments (in addition to the block of code that will be repeatedly run) in a Java for
loop. You may have seen this syntax before because it's similar to the syntax used in C, C++, and many other programming languages. The first argument is a line of code to be run before the first loop begins, so it's usually used to initialize some sort of index variable. (In this case, we create an integer variable i
and set it to zero). The second argument is a condition that is tested at the beginning of each loop. If the condition holds, then the loop is run, otherwise, it stops. (In this case, we test whether i
is smaller than 5.) The last argument is a line of code to be run at the end of each loop. It's usually used to move the index to the next value. (In this case, we add one to i
. The line i++
is equivalent to writing i=i+1
.) The arguments are separated by semi-colons.
Before we start the loop, we initialize the variable sum
to zero. We have to declare that sum
is of type double
outside the loop because we don't want the JVM to try to redeclare the type of sum
every time the loop runs. Setting it to zero means that we don't have to write special code for the first time the loop runs. We can always just add the new number to the existing sum.
Yesterday, we used the scanner method nextLine()
to grab the next line of input as a string. Today, we used a different method (nextDouble()
), which reads from the input stream until it finds a space or a line break (or any other white space) and then tries to interpret the input as a variable of type double
. Most of the scanner "next" methods behave in the same way, treating the string as divided into words separated by white space. next()
reads the next word as a string, nextInt()
reads the next word as an int
, and similarly for nextFloat()
, nextLong()
, etc. The method nextLine()
is an exception, treating the string as divided up into lines separated by line beaks.
Amazingly, our program worked correctly on the first time without any typos or bugs. If only the people with the keys to our classroom could get things right so easily.
But this isn't really what we wanted to do. What we wanted to do was to allow the user to enter as many numbers as they want, typing in the word "stop" when they were done. To do this, it made more sense* to use a while
loop, which is kind of like a for
loop only it removes the first and last arguments. There's just a condition to be checked, and as long as the condition is true, the loop keeps running. Any manipulation of the values that appear in the condition has to happen inside of the loop.
*It was suggested that we keep the for
loop, and just test if
the input was "stop", and when it is, using the command break
to leave the for
loop. This would work, but we'd have to somehow change the for
loop so that it wouldn't only run 5 times. Since we don't know how many times we'll need to run it, it makes more sense to use a while
loop.
In our case, we want to check whether the user has inputted the string "stop" before we try to read it as a number and add it to sum
. So we create a variable to store the input as a string (call it input
). It's initialized before the loop even gets started, so that the first time the loop is run, we don't have to worry about the variable not existing. We also need to make sure that the conversion from a string to a number happens right after we check whether input
is the word "stop" or not. If we try to convert input
to a number at the end of the loop, then it will try to convert "stop" to a number before it gets to check or not.*
*It took us a couple tries to come across this particular solution, but to keep the notes cleaner, I'll only leave in the mistakes that I think are particularly informative.
So here is the code we came up with:
import java.util.Scanner;
public class summer {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
double sum = 0;
String input = "0";
while( input != "stop" ) {
sum = sum + Double.parseDouble(input);
System.out.println("The sum is " + sum);
System.out.println("Enter a number (type \"stop\" to stop):");
input = s.next();
}
}
}
Notice the escaped quotation marks. If you want to print a quotation mark inside of a string, you can use the escape code \"
. Unlike in Python and PHP, you can't just switch to using single quotes. Double quotes are used for strings (class String
), and single quotes are used for single characters (primitive type char
).
And then we tested the program:
[ewennstr@silo java]$ javac summer.java
Picked up _JAVA_OPTIONS: -Xms512m -Xmx512m
[ewennstr@silo java]$ java summer
Picked up _JAVA_OPTIONS: -Xms512m -Xmx512m
The sum is 0.0
Enter a number (type "stop" to stop):
65537
The sum is 65537.0
Enter a number (type "stop" to stop):
3.1415926
The sum is 65540.1415926
Enter a number (type "stop" to stop):
-2.718281828
The sum is 65537.423310772
Enter a number (type "stop" to stop):
0
The sum is 65537.423310772
Enter a number (type "stop" to stop):
stop
Exception in thread "main" java.lang.NumberFormatException: For input string: "stop"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1241)
at java.lang.Double.parseDouble(Double.java:540)
at summer.main(summer.java:9)
It starts out good, but then it throws an exception. In a future lecture, we'll talk about how we can "catch" exceptions, to keep them from crashing the whole program, but for now, let's just try to figure out what went wrong and fix it.
There's a lot going on in that error message, but there are two things you should pay attention to. The first is near the beginning: For input string: "stop"
. That tells us that there's a string with the content "stop" involved. The other important bit is the second to last line: at java.lang.Double.parseDouble(Double.java:540)
, which tells us that it was in the process of running the function parseDouble()
at the time. You should be able to guess what the problem is from that information, but even if you don't see it right away, this should send up a warning flag. Because when input
has the value "stop", the loop shouldn't be executed, and the parseDouble()
function shouldn't be running.
So there's a problem way up at the top of the while
loop where it checks to see if input != "stop"
is true. Even though the string input
has the value "stop", Java thinks that input
and "stop"
are not "equal".
This is one of the more confusing fine points of Java. In Java, you can have two "different" objects of the same class that store the same data. In our example here, there are two different String
s. The literal "stop"
that shows up in the source code is stored as an object of type String
. The variable input
is an object of class String
, whose value was set after reading in the keyboard input and storing that value (which also happens to be "stop") somewhere else in memory. The values for these two String
s are stored in different places in memory, and as far as Java is concerned, these are two different objects that might or might not happen to have the same value stored in them. This notion of "same" versus "different" is called "identity". We say that the two String
s are not "identical". If the two objects can be used interchangeably (often because they store the same value), we say that they are "equal". In math, we would call this "equivalence", but in Java, we call it "equality".
In Java, the operators ==
and !=
confusingly don't test for equality; they test for identity. So when we ask whether input != "stop"
, the answer is usually "yes, they aren't identical".
This brings up the question "how do we test for equality, then?" Well, since interchangeability might be different for different classes, each class should have its own method for testing equality. For example, if we had class that stored fractions, we might want to say that 1/2 was "equal" to 2/4. Fortunately, just about all of the standard classes (including String
) that you'd want have a method equals()
for testing equality. So we can replace the condition input != "stop"
which tests identity with the condition !( input.equals("stop") )
, which tests equality. Here's the final version of the code:
import java.util.Scanner;
public class summer {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
double sum = 0;
String input = "0";
while( !( input.equals("stop") ) {
sum = sum + Double.parseDouble(input);
System.out.println("The sum is " + sum);
System.out.println("Enter a number (type \"stop\" to stop):");
input = s.next();
}
}
}
In lab, you'll be doing something very similar, but you'll need to account for non-numeric input that is not the string "stop". For your homework, you'll have to do some work with strings.