Find the Bug in this Java Program
Brief Summary of Java
Java programs are compiled into an intermediate format, known as bytecode, and then run through an interpreter that executes in a Java Virtual Machine (JVM).
The basic syntax of Java is similar to C and C++. All white space is treated equally, indent level does not matter, statements end in a semicolon, and blocks of code are enclosed between { and }.
Comments are enclosed between /* and */, or else begin with //, in which case the rest of the line is a comment.
Data Types and Variables
The integer data types are byte, short, int, and long, which correspond to numbers of 8, 16, 32, and 64 bits. The types float and double store floating-point numbers; char stores a 16-bit Unicode character, and boolean can hold one of two values, true or false.
Variables are declared with a type and name, as in the following:
int myint;
They can be initialized at the same time:
char delimeter = '/'; boolean finished = false;
Variables can be declared anywhere they are used. The scope of a variable usually extends to the end of the code block it was declared in.
Java allows variables to be converted between different numeric types by casting, as in the following:
int a; double d = (double)a;
You can also cast between objects, but that is beyond the scope of this book.
Variables can be declared as final, which means that their value cannot be changed after it is initialized:
final int MAX_LEN = 128;
Arithmetic expressions in Java are straightforward, with % used for modulo:
k = a + b; remainder = tot % users;
The ++ and -- operators exist. If they are used in prefix notation, the expression is evaluated after the operation is done. In postfix notation, the expression is evaluated before the operation is complete. So, with the following code
d = 4; e = ++d; f = e--;
e and f are both set to 5.
Strings (and Objects)
Beyond the basic data types, everything in Java is declared as a class. A class is a grouping of variables and methods (functions that operate on those variables). The word object is often used to refer to a class, but technically, a class is a description of an object and an instance is an actual object.
You can define your own classes; Java includes many predefined ones. One such class is String (or more precisely, java.lang.String), which is used to store a constant string. Strings in Java are not just arrays of charactersthey are a class that has defined methods for accessing and modifying the characters.
The String class can serve as an example of how Java objects are used. A String can be created from an array of characters, as follows:
char[] myArray = { 'a', 'b', 'c' }; String myString = new String(myArray);
The expression new String(myArray) invokes what is called a constructor for the class String. Constructors create a new instance of an object, optionally taking parameters. How many parameters a constructor takes, and the type and order of those parameters, are part of the constructor's signature. Multiple constructors can exist for a given class as long as they have different signatures. For example, another constructor for String is called as follows:
String myString = new String(myArray, 2, 1);
That is, specifying an offset and count within myArray. You can also call
String myString = new String();
This creates an empty string. (A String cannot be changed after it's initialized, so it would stay empty.) The String class actually has nine constructors, plus two more obsolete ones.
When Java sees a literal string in double quotes, it automatically creates a String object, so you can write the following:
String newString = "text";
This is actually an assignment of one String to another. This automatic creation of an object from a literal is unique to the String class (all other literals, such as numbers, become primitive types), but it sure is convenient.
No destructors exist in Java; objects are destroyed by the garbage collector at some point after the last reference to them is removed (often because the variables holding that reference go out of scope). A variable can be assigned a keyword null to force a reference it is holding to be removed:
anotherString = null;
However, the garbage collector makes no guarantees about how soon an object will be destroyed once there are no references to it.
Java does not have explicit pointers; in a sense, all variables that refer to objects are pointers. When you assign between two objects of the same type, you actually assign a reference to the object on the right-hand side. To create a new instance of an object, you need to call one of its constructors:
myObject a, b; a = b; // reference a = new myObject(b); // create a new object
Classes define methods that can be called on an instance of that class. For example, the String class has a method length() that returns the length of the string:
String j = "abc123"; x = j.length();
As previously mentioned, a String cannot change after it's initialized. Java has another class, StringBuffer, which holds strings that can change. A StringBuffer can be constructed from a String, or from a length, which specifies how many characters of capacity it should start with:
StringBuffer sb1 = new StringBuffer("howdy"); StringBuffer sb2 = new StringBuffer(100);
StringBuffer has a variety of methods on it:
sb.append("more data"); char c = sb.charAt(12); sb.reverse();
In Java, the + operator can concatenate strings together. A sequence such as the following
String greeting = "Hello"; greeting = greeting + " there";
is legal. Because the original String that greeting points to cannot be modified, the concatenation actually involves the creation of a new String, which greeting is then set to point to. Therefore, the reference to the original "Hello" string is removed, which eventually causes it to be destroyed.
TIP
The concatenation statement also involves some more behind-the-scenes magic by the compiler. It creates a temporary StringBuffer, then calls the StringBuffer.append() method for each expression separated by a + sign, then calls StringBuffer.toString() to convert it back to the result String. As with the automatic creation of String objects from constant strings, this is a special case on the part of Java, but is there because string concatenation is so useful.
StringBuffer.append() is overloaded, so it can be passed any primitive type. Thus, you can call the following
int j = 4; String b = "Value is" + j;
and b will equal "Value is 4". In fact, StringBuffer.append() works for any object by appending the result of the object's toString() method, which can be overridden as needed by the author of the object's class.
Arrays
Arrays in Java are declared with square brackets:
int[] intArray;
The array then has to be created:
intArray = new int[10];
intArray would then be indexed from 0 to 9.
Arrays can also be created at declaration time, if values are specified using an array initializer:
int[] array2 = { 5, 4, 3, 2, 1 };
You can't explicitly specify the length in that case because it's determined from how many values are provided.
You can get the number of elements in an array:
k = array2.length;
Note that this is not a method, so no parentheses appear after length.
Arrays can also hold objects, so you can declare the following:
MyObject[] objarray;
This would then be created as follows (this could be combined with the declaration):
objarray = new MyObject[5];
It is important to note that this creates only the array. You still need to create the five objects:
for (k = 0; k < 5; k++) { objarray[k] = new MyObject(); }
To create subarrays, create an array where each element is an array. The first array can be declared and created in one step
int[][] bigArray = new int[6][];
and then each subarray needs to be created (each one can be a different length, in fact):
for (m = 0; m < 6; m++) { bigArray[m] = new int[20]; }
You can initialize arrays when they are declared:
short[][] shortArray = { { 1, 2, 3 }, { 4 }, { 5 , 6 } };
After that, shortArray[0] would be an array of three elements, shortArray[1] would be an array of one element, and shortArray[2] would be an array of two elements.
Finally, if the entries in the arrays are objects, they also have to be constructed, as shown here:
final int XDIM = 6; final int YDIM = 10; SomeObj[][] oa; oa = new SomeObj[XDIM][]; for (int i = 0; i < XDIM; i++) { oa[i] = new SomeObj[YDIM]; for (int j = 0; j < YDIM; j++) { oa[i][j] = new SomeObj(); } }
Conditionals
Java conditionals use the same if/else syntax as C:
if (j == 5) { // do something } else { // do something else }
The switch statement is also the same, with explicit break statements required, and a default case:
switch (newChar) { case "@": process_at(); break; case ".": process_dot(); break; default: ignore(); }
Loops
Looping is done with for, while, and do/while:
while (k > 8) { do_processing(); } do { eof = get_line(); } while (eof != true);
break breaks out of a loop, and continue jumps to the next iteration. A label can be added to break or continue to specify which loop it refers to:
outerloop: for (x = 0; x < 20; x++) { for (y = x; y < 20; y++) { if (something) { break outerloop; } } }
outerloop: is a label for the loop and the statement break outerloop; breaks out of the labeled loop. It does not jump to the point where the outerloop: label exists in the code.
Classes
A class is defined as follows:
class MyClass { private int a; public StringBuffer b; public MyClass(int j) { a = j; b = new StringBuffer(j); } public MyClass(String s) { a = s.length(); b = new StringBuffer(s); } public int getLength() { return a; } }
a and b are member variables in the class. a is defined with an access specifier of private, which means that it is hidden from the view of external code. b is public, which means that anyone can access it if they have an instance of MyClass. For example
MyClass mc = new MyClass("hello"); String abc = mc.b;__// this is allowed, b is public int def = mc.a;___// this is NOT allowed, a is private
We'll get back to access specifiers within the next few paragraphs. For now, note that MyClass has two constructors, one of which takes an int as a parameter, and the other takes a String (the second one is the one called in the previous code sample). Both constructors initialize a and b. Variables can also be initialized when they are declared, so b could have been declared as follows:
public StringBuffer b = new StringBuffer();
Although, for this class, that would not be necessary because every constructor initializes b.
Classes can also inherit from another class. A subclass inherits all the state and behavior of its superclass (but not the constructors), although it can override methods by providing new ones with the same name (unless those methods were declared with the final keyword).
Inheritance is indicated by the extends keyword:
abstract class Polygon { Point[] points; abstract int getcount(); } class Triangle extends Polygon { public Triangle() { points = new Point[3]; } int getcount() { return 3 }; }
The access specifier of a class variable can be public, private, protected, or package (the default). public means that any code can access it; private means that only methods in the class itself can access it; package means that any code in the same "package" (which is a way to group classes) can access it.
A variable marked protected can be accessed by the class, subclasses, and all classes in the same package. Actually, to be more precise, subclasses can only access a protected member inherited from a superclass when the object is an instance of the subclass (which it usually will be). They can't modify an instance of the superclass itself. (If you didn't catch all that, don't worry too much about it.)
Members of a class (variables or methods) can be declared with the keyword static, which makes them "class members," as opposed to "instance members," which is the case that's been described so far. Class variables and class methods exist just once, as opposed to once per instance. For example, a class could assign unique identifiers to each instance it creates, as shown here:
class ImportantObject { private static int nextcounter = 0; private int counter; public ImportantObject() { counter = nextcounter++; } // continues... }
Each instance of the class has its own counter member, but there is only one global nextcounter.
A method on a class can be declared abstract, which means that it defines the parameters and return value, but has no actual implementation. A class can also be declared abstract; this is required if it defines at least one abstract method. (It is also required if a class does not provide implementation for any abstract methods declared in its superclasses.) An abstract class cannot itself be instantiatedit exists to ensure that subclasses follow the "contract" that it defines.
Closely related to classes are interfaces. The main difference between an interface and an abstract class is that all the methods on an interface must be abstract:
public interface identify { String getName(); }
Other classes can now support an interface using the implements keyword. Unlike inheritance, where a class can only inherit from one class, classes can implement as many interfaces as they like, as long as they provide implementations of all the interfaces' methods (or are declared abstract):
class SomeClass implements identify { final String name = "SomeClass"; String getName() { return name }; // rest of class follows... }
A class with only public member variablesand no methodscan be used to group variables by name, similar to C structures:
class Record { public String name; public int id; public int privilege; } Record r = new Record(); r.name = "Joe"; r.id = 12; r.privilege = 3;
Java likely has a class for almost any standard operation you want to do; the documentation lists constructors and methods. For example, classes exist that wrap all the primitive types, such as this one that wraps the short primitive in a class called Short (note the capital "S" on the class name), and provides various useful methods:
Short s = new Short(12); String str = s.toString();
I won't go into more details about specific classes, except as needed in the examples.
Exceptions
Java supports exceptions, which are objects that can be caught:
try { file = new FileInputStream("data.tmp"); } catch (FileNotFoundException e) { System.err.println("Exception " + e.getMessage()); } finally { // cleanup code }
A try can have multiple catch blocks, each catching a different exception. (There is a hierarchy of exception classes, leading back to a class called Throwable. A catch block that catches a particular exception also catches any exceptions that are subclasses of that exception.)
If an exception happens and is caught, the catch block executes. The finally block always executes, whether or not an exception happens, and is usually used for cleanup code.
You can create and throw exceptions:
if (bytesleft == 0) { throw new EOFException(); }
Java requires that methods that can throw an exception specify it in the declaration of the method, using the throws keyword:
public void read_file(File file) throws IOException { if (!check_valid(file)) { throw new IOException("check_valid() failed"); } }
Method declarations must also list any exceptions that can be thrown by methods they call, unless they catch the exception. Thus, a method that called read_file() (as defined above) would need to either put it in a try block with an associated catch block that caught IOException, or specify in its own declaration that it throws IOException. (This "catch or specify" rule does not apply to a class of exceptions known as runtime exceptions, which inherit from the class RuntimeException. This rule is detailed in the Java documentation.)
Importing Other Code
To use a class, you must import the package that defines it. This is specified in the documentation of the class. For example, to use the Timer class, include the following in the code:
import java.util.Timer;
This can include a wildcard:
import java.util.*;
Command-Line Applications and Applets
The examples used in this chapter are split between command-line applications and applets designed to run in a web browser. A command-line application has to contain a class that implements a main()method, which must be defined as public static, return type void, and receive the command-line parameters as an array of String objects called args (the first element in args is the first parameter, etc.):
public class MyApplication { public static void main(String[] args) { for (int j = 0; j < args.length; j++) { System.out.println(args[j]); } } }
An applet inherits from a class called Applet:
public class MyApplet extends Applet { public void paint(Graphics g) { g.drawString("Testing 123", 10, 10); } }
The paint() method is overridden from a superclass a few levels up from Applet, and is used to display on the screen. The Graphics class has many methods used to draw lines and shapes, display text, change color, and so on.