15-100 Lecture 4 (Thursday, May 25, 2006)

Quick (Practice) Quiz

  // Define the "main method"
  public static void main (String[] args) {
    
    // Declare variables: x, y capable of holding numbers w/fractions
    double x; // float x;
    double y; // float y;


    // Assign 3.14159265 to x and 6.3 to y
    x = 3.14159265; // Or, if float, x = 3.14159265f;
    y = 6.3;        // Or, if float, y = 6.3;


    // Compute the sum of x and y, assigning the result to z
    // BUT, PLEASE TRUNCATE THE REMAINDER AFTER THE COMPUTATION
    int z = (int) (x + y);


    // Print out the value of z
    System.out.println (z);
  }
  

One Note On the Quiz

I had originally intended to ask for the sum of 6.9 and 3.14159265. But, I decided to make it a bit easier. None-the-less, it is worth thinking about how this would have changed things.

The reason for intended values was to encourage you to observe that the result, was 10, not 9. Since the addition is in the domain of doubles, the result is a double, which is then truncated to an integer via the cast and assignment.

So, just in case this would have thrown you, please take note. Also, please note, if the cast had been applied to each variable, individually, the result would have been 9. The reason is that the fractions would have been truncated before the addition and never had the chance to influence the integer component.

  int z = (int) x + (int) y;
  

Methods

In passing, I've discussed the existence of methods. A method in Java is a construct that describes an action. It is somewhat like a receipe: Do this, then this, then this, &c.

We mentioned that, in fact, main() is just a special method, whose instructions Java follows when it first loads a program into memory. It basically gets things started.

We have, ourselves, made use of methods. Consider, "System.out.println ("Hello world");", which is a method invocation. It asks Java to print the line of text, "Hello world", hence the name "println".

Actually, notice the "System.out". The "." is called the "scope" operator. More specifically than asking "Java" to print the line, we are asking "System.out" to do it. System.out is an Object, a "think" that exists within the program. In this case a "thing" that knows how to print a line. More specifically, we are asking the "out[put subsystem]" of the "System" to print the line. The "System" also has an input subsystem, known as "System.in".

Some methods we'll use and write will be methods like println, associated with particular types of objects. Others, like main(), will truly be floating, available for use by any object. These floating methods are known as "static methods" or "class methods" and have the "static" qualifier, as did the declaration of main.

Writing our Own Methods

What if we'd like to create a method to encapulate addition. This way, we can write, "z = add (6.3, 3.14159265);" instead of "z = 6.3 + 3.14159265;"? This type of method is a "static" method. It doesn't require any state from an object. It takes everything it needs, the numbers to add, as input and returns its only output, the sum.

The general form for a static method is as follows:

  public static return-type method-name (argument-list) {
    method-body...
  }
  

For now, our methods will all be "public". We'll talk more about what this means later. But, for now it means that they are "1st class" methods, not "helper methods" designed to further decompose other methods.

In this case, our method will add double-precision numbers, so we'll call it "addDouble":

  public static return-type addDouble(argument-list) {
    method-body...
  }
  

It will add two double-precision numbers, we'll call them "number1" and "number2", so we'll list these within the "formal argument list", within the ()-parenthesis:

  public static return-type addDouble(double number1, double number2) {
    method-body...
  }
  

And, once it is done, it returns a double-precision number:

  public static double addDouble(double number1, double number2) {
    method-body...
  }
  

Ultimately, the method will add the two numbers and return the result.

  public static double addDouble(double number1, double number2) {
  
    double sum;

    sum = number1 + number2; 
   
    return sum; // This "sends back" the value of sum
  
  }
  

In the example above, please notice that the type of what is being returned (result is a double) matches the return-type we declared, also a double. This is absolutely necessary. What we do must match the specification we provide.

Using our addDouble() method

Okay, let's take a look at a complete example that makes use of the method we just wrote:

  public class MethodExample {
  
    public static double addDouble (double number1, double number2) {
      double sum;

      sum = number1 + number2;

      return sum;
    }

    
    public static void main (String[] args) {
      double x = 3.14159265;
      double y = 6.9;
      double z;

      z = addDouble(x, y);
    }
  }
  

Notice the assignment, "z = addDouble(x, y);". This is possible because z is declared as a "double", which is compatible with the return type we declared for addDouble(), also a double. When addDouble() is called, the directions we provided within the method are followed, and the return value is used in place of the method call. In this case, "addDouble(x,y)" is replaced with the value 10.04159265.

One more example: addInt()

  public int addInt (int number1, int number2) {
 
    // In this case, we shortened the code by removing the temporary variable.
    return number1 + number2;
  }
  

Java Input/Output

Think of the java input and output system like a producer consumer relationship, you can only do one thing at a time. Either you can consume the item of produce the item, you can’t be both producing and consuming an item at the same time. Java works a lot like that, it can only be either inputting or outputting at a given time.

The most basic part of Java IO is the ‘System’. The System represents the machine. Thus, System.in represents an input stream coming into the machine. Unfortunately this input stream is very rudimentary and just reads the information in byte by byte. Since it would be difficult to be able to manipulate the data in this form Java has some additional classes that you can use to manipulate the data stream. The next level up that we’re going to use is the InputStreamReader. InputStreamReader is passed System.in, and creates an input stream that will return characters instead of just bytes. So far the code that we have is:

InputStreamReader isr  = new InputStreamReader(System.in);

Obviously it is easier to manipulate entire characters instead of just bytes, but we want to be able to access more then just one character at a time. Because of this Java provided the BufferedReader. BufferedReader is passed an InputStreamReader, which it uses to read an input stream character by character into a buffer, until it has read an entire line. Once it has read a line it will return the entire line to the user. So far the code is:

  InputStreamReader isr = new InputStreamReader(System.in);
  BufferedReader br = new BufferedReader(isr);
  String s = br.readLine( );
  

This means that program will go until it reaches the ‘String s = br.readLine()’ line at which point it will wait for the user to enter a line of text and then it will go and read the line. So lets now make these lines actually do something.

  InputStreamReader isr = new InputStreamReader(System.in);
  BufferedReader br = new BufferedReader(isr);
  System.out.print("What's your name? ");
  String s = br.readLine();
  

This gives the user a prompt, asking them for their name, which the program reads, once it has been entered. There is one problem with this section of code though. Printing may be delayed because output is actually a give and take between program and operating system. So what may happen if the operating system buffer isn’t full then it may wait until it is to print this out on the screen. But, now the program is waiting for a String to be input into the computer, and the user doesn't see a prompt to tell them that they need to enter input. So now computer is waiting for user and user is waiting for computer; this creates a dilemma, because the computer is now hung.

There is a way to fix this. In situations like this you have to tell the computer to flush its buffer before the program reads the line. In order to do this you use the System.out.flush() command. The correct code is below.

  InputStreamReader isr = new InputStreamReader(System.in);
  BufferedReader br = new BufferedReader(isr);
  System.out.print("What's your name? ");
  System.out.flush();
  String s = br.readLine();
  

There is one last important thing to remember when using Java IO, that is the location in you're program that you add it. You're goal as a programmer is to create good code that is easy to reuse, and will require little maintenance if you ever need to update it. Unfortunately, many applications of Java use many different types of IO. Since, you want your Java classes to be able to be used across many different applications it is important to keep the IO in the user interface, thus when you get to a new application only the user interface changes, not the entire class. There can be occasional exceptions for when the IO information needs to be inside a particular method, but for the most part points will be deducted for putting the IO information in the wrong method.

One More Example

This example reads in someones name, then prints says hello. First put "import java.io.*" at the top of the file, as the very first line.

  import java.io.*;
  public static void main(....) throws Exception {
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);

    System.out.print("What is your name?");
    System.out.flush();
    String name = br.readLine();

    System.out.println("Hello, "+ name)
  }
  

Java Primitive Data Types and Wrapper Classes

Now that you are able to read Strings into you're program you'll want to be able to convert them to useful things, like ints and floats, that you used in the textbook class. First, lets review the six primitive data classes:

Each of these primitive data types has a class called a "wrapper" class. By Java convention the name of each of their wrapper classes is just the full name of the data type with the first letter capitalized. We'll talk more a bit later about why these classes are called "wrapper" classes. The six wrapper classes are listed below.

Let's consider the Integer class. It contains a static method, "parseInt" that functions as below:

  String numberString = "1234";
  int number;

  number = Integer.parseInt (numberString);
  

As you can see, this method can convert the String representation of an int into a genuine int. The other wrapper classes have similar methods:

So, what happens if you pass in a String that can't be converted? For example, "Float.parseFloat("abcd")" or "Integer.parseInt ("123.456")". This is obviously an error. The conversion can't be completed. When this is encountered, Java "throws an Exception". This means that it kicks the program into a special error-handling mode.

We'll talk more about how to make use of that mode later. But, for now, let's just observe that any method using these methods, and any method using those methods, and so on, should add the "throws Exception" clause to its declaration:

  public static void main (String[] args) throws Exception { ...}
  

This clause just admits that an exception can be thrown, causing the program to end.