As a Java programming instructor, you see a lot of strange errors. Eventually, you start to recognize certain patterns or favorite ways that novice programmers get mixed up. The mistakes documented on this page are all ones that I've seen a dozen times or more. They're all easy to fix, if you can recognize them!
This page is designed to be a resource both for new Java programmers and for new Java instructors. Click on the 'Java Page' banner for my main Java page, where you can find a few applets and links to lots of Java resources.
JavaSoft has a nice 'New to Java' section in their training web site, but I think you have to join JDC to use it (free). Check it out: New2Java at java.sun.com.
If you have a common kind of mistake that you would like me to analyze and add to this page, please send mail to ziring@home.com.
Jump right to a particular mistake:
Beginning students often forget this guideline, and name their file something about their assignment, like Lab6.java.
Mistake Example |
---|
File Lab6.java -public class Airplane extends Vehicle Seat pilot; public Airplane() { pilot = new Seat(); } } |
Corrected Example |
File Airplane.java -public class Airplane extends Vehicle Seat pilot; public Airplane() { pilot = new Seat(); } } |
Note that the name of a class is supposed to start with a uppercase letter. On operating systems that enforce case-sensitive file names, this can cause an additional problem, especially for students learning Java on Unix when they are accustomed to using DOS or DOS-derivative file names. The class MotorVehicle must be stored in the file MotorVehicle.java, not motorvehicle.java.
Mistake Example |
---|
// find out if the first argument is "-a" if (args[0] == "-a") { optionsAll = true; } |
The proper way to compare two strings for equality is to the use java.lang.String equals() method. It returns true if the the strings are the same length and contain the same characters.
Corrected Example |
---|
// find out if the first argument is "-a" if ("-a".equals(args[0])) { optionsAll = true; } |
This mistake is particularly nasty because it is legal Java code; it just doesn't do what the student expects. Some students also try to use comparison operators like > and <=, instead of the correct java.lang.String method compareTo(). This mistake is much simpler to catch, because it causes a compile-time error.
In the example below, the student wants to create three StringBuffer objects to hold some data. This code will compile, but will throw a NullPointerException on the last line, where one of the (non-existent) objects is used.
Mistake Example |
---|
// Create an array of data buffers StringBuffer [] myTempBuffers; myTempBuffers = new StringBuffer[3]; myTempBuffers[0].add(data); |
To correct this problem, it is important to remember to initialize the elements of any Java array.
Mistake Example |
---|
// Create an array of data buffers and initialize it. StringBuffer [] myTempBuffers; myTempBuffers = new StringBuffer[3]; for (int ix = 0; ix < myTempBuffers.length; ix++) myTempBuffers[ix] = new StringBuffer(); myTempBuffers[0].add(data); |
Sometimes, a student will forget the second rule in their rush to code up a lab exercise or project. They'll put two or more public classes into a source file.
The error message for breaking rules 2 and 3 are the same.
Occasionally, this mistake will cause a compiler error, usually when the shadowed attribute and the local variable are different types. More commonly, this mistake will cause no errors or exceptions, and the student will be left mystified about their unexpected results.
Mistake Example |
---|
public class Point3 { int i = 0; int j = 0; int k = 0; public boolean hits(Point [] p2list) { for(int i = 0; i < p2list.length; i++) { Point p2 = p2list[i]; if (p2.x == i && p2.y == j) return true; } return false; } } |
There are two ways to fix this problem. The simple way is to refer to the attribute with the construct this.attribute. The better way is to rename the attribute or the local variable so that shadowing does not occur.
Corrected Examples |
---|
// One way to fix the problem int i = 0; int j = 0; int k = 0; public boolean hits(Point [] p2list) { for(int i = 0; i < p2list.length; i++) { Point p2 = p2list[i]; if (p2.x == this.i && p2.y == this.j) return true; } return false; } |
// A better way to fix the problem int x = 0; int y = 0; int z = 0; public boolean hits(Point [] p2list) { for(int i = 0; i < p2list.length; i++) { Point p2 = p2list[i]; if (p2.x == x && p2.y == y) return true; } return false; } |
Another very common manifestation of this mistake is giving a method parameter the same name as an attribute. Some programmers do this as a matter of course for constructors, but it is not really appropriate for regular methods.
Sometimes, a student will forget this requirement. Usually, this is not a problem: the superclass constructor call inserted by the compiler works just fine. However, if the superclass does not have an accessible default no-parameter constructor, then the compiler will complain. In the example below, all constructors of the superclass java.io.File take 1 or 2 parameters.
Mistake Example |
---|
public class JavaClassFile extends File { String classname; public JavaClassFile(String cl) { classname = cl; } } |
The solution is usually to insert an explicit call to the correct superclass constructor.
Corrected Example |
---|
public class JavaClassFile extends File { String classname; public JavaClassFile(String cl) { super(cl + ".class"); classname = cl; } } |
A more subtle situation can come up when the superclass has a constructor that takes no parameters, but doesn't fully initialize the object. Then, the code will compile, but the program will give strange results or throw an exception.
Here, the exception is missing a name. This mistake is caught by the compiler, and the student will usually fix the problem by themselves.
Mistake Example |
---|
try { stream1 = new FileInputStream("data.txt"); } catch (IOException) { message("Could not open data.txt"); } |
Corrected Example |
try { stream1 = new FileInputStream("data.txt"); } catch (IOException ie) { message("Could not open data.txt: " + ie); } |
The order of the catch clauses in a try-catch block define a precedence for catching exceptions, but each such clause will catch all exceptions of the declared class or any of its subclasses. Forgetting this can lead to an unreachable catch clause, which the Java compiler treats as a serious error. In the example below, SocketException is a subclass of
Mistake Example |
---|
try { serviceSocket.setSoTimeout(1000); newsock = serviceSocket.accept(); } catch (IOException ie) { message("Error accepting connection."); } catch (SocketException se) { message("Error setting time-out."); } |
Corrected Example |
try { serviceSocket.setSoTimeout(1000); newsock = serviceSocket.accept(); } catch (SocketException se) { message("Error setting time-out."); } catch (IOException ie) { message("Error accepting connection."); } |
If a piece of code throws an exception, and the exception is not caught by any try-catch block, then the method in which the code appears must declare the exception. (Note that exceptions that are subclasses of RuntimeException are exempt from this rule.). Students sometimes forget that a given method call will throw an exception. The usual fix is to build in a try-catch block.
Mistake Example |
---|
public void waitFor(int sec) { Thread.sleep(sec * 1000); } |
Corrected Example |
public void waitFor(int sec) throws InterruptedException { Thread.sleep(sec * 1000); } |
Mistake Example |
---|
public class Line { private Point start, end; public void getStart() { return start; } } |
Corrected Example |
public class Line { private Point start, end; public Point getStart() { return start; } } |
This is one common manifestation of a broader class of mistake: giving methods incorrect return types. Usually, the Java compiler can catch these kinds of errors and report them; most students can interpret the error messages and fix the problems on their own.
Mistake Example |
---|
public class DivTest { boolean divisible(int x, int y) { return (x % y == 0); } public static void main(String [] args) { int v1 = 14; int v2 = 9; // next line causes a compiler error message if (divisible(v1,v2)) { System.out.println(v1 + " is a multiple of " + v2); } else { System.out.println(v2 + " does not divide " + v1); } } } |
The fix for this problem is almost always one of two choices: make the method in question static, or create an object and call the method on it. To help them decide between these two approaches, ask the student whether the method they want to call uses any attributes or other methods of the object. If it does, then they should create and initialize an object, then call the method. If not, then they should make the method static.
Corrected Example 1 |
---|
public class DivTest { int modulus; public DivTest(int m) { modulus = m; } boolean divisible(int x) { return (x % modulus == 0); } public static void main(String [] args) { int v1 = 14; int v2 = 9; DivTest tester = new DivTest(v2); // next line causes a compiler error message if (tester.divisible(v1) { System.out.println(v1 + " is a multiple of " + v2); } else { System.out.println(v2 + " does not divide " + v1); } } } |
Corrected Example 2 |
public class DivTest { static boolean divisible(int x, int y) { return (x % y == 0); } public static void main(String [] args) { int v1 = 14; int v2 = 9; // next line causes a compiler error message if (divisible(v1,v2)) { System.out.println(v1 + " is a multiple of " + v2); } else { System.out.println(v2 + " does not divide " + v1); } } } |
Mistake Example |
---|
public static void main(String args[]) { String test1 = "Today is "; appendTodaysDate(test1); System.out.println(test1); } public void appendTodaysDate(String line) { line = line + (new Date()).toString(); } |
In the example above, the student is expecting to change the value of main's local variable test1 by assigning a value to the parameter line in the appendTodaysDate method. Of course, this won't work; the local value of line will change, but the string test1 will be unaffected.
This mistake can come up if the student has not yet accepted the fact that (1) Java objects are always passed by handle, and (2) Java strings are immutable. As the instructor, you have to explain Java parameter passing, and emphasize that String objects never change their value, but instead all String operations merely create new String objects.
For the particular problem shown above, the fix is either to return a string value from the method, or to pass a StringBuffer object instead of a String object.
Corrected Example 1 |
---|
public static void main(String args[]) { String test1 = "Today is "; test1 = appendTodaysDate(test1); System.out.println(test1); } public String appendTodaysDate(String line) { return (line + (new Date()).toString()); } |
Corrected Example 2 |
public static void main(String args[]) { StringBuffer test1 = new StringBuffer("Today is "); appendTodaysDate(test1); System.out.println(test1.toString()); } public void appendTodaysDate(StringBuffer line) { line.append((new Date()).toString()); } |
In the example below, the student expects the attribute list to be initialized to a fresh empty Vector object. This does not happen, because the method named 'IntList' is not a constructor.
Mistake Example |
---|
public class IntList { Vector list; // This may look like a constructor, but it is a method public void IntList() { list = new Vector(); } public append(int n) { list.addElement(new Integer(n)); } } |
The code will throw a null pointer exception the first time that append is called. This mistake is usually a tough one for a beginning programmer to figure out on their own, but the fix is actually quite simple: remove the return type.
Corrected Example |
---|
public class IntList { Vector list; // This is a real constructor public IntList() { list = new Vector(); } public append(int n) { list.addElement(new Integer(n)); } } |
Students often forget about the explicit downcast requirement. This is most common when they use Object arrays or the java.util collection classes, like Vector or Hashtable. which will store any kind of object and return type Object. In the example below, a String object is put into an array, and then retreived from the array to do a string comparison operation. The compiler will catch this and refuse to compile the code until a cast is applied.
Mistake Example |
---|
Object arr[] = new Object[10]; arr[0] = "m"; arr[1] = new Character('m'); String arg = args[0]; if (arr[0].compareTo(arg) < 0) { System.out.println(arg + " comes before " + arr[0]); } |
The whole concept of type casting becomes very difficult for some students. Often, it is Java's dynamic methods that cause the confusion. In the example above, has the method equals been used instead of compareTo, the compiler would not have complained, and the code would have worked because the actual string equality method would have been called. It is important to explain to the student that run-time method lookup is distinct from downcasting.
Corrected Example |
---|
Object arr[] = new Object[10]; arr[0] = "m"; arr[1] = new Character('m'); String arg = args[0]; if ( ((String)arr[0]).compareTo(arg) < 0) { System.out.println(arg + " comes before " + arr[0]); } |
Mistake Example |
---|
public class SharkSim extends Runnable { float length; ... } |
Corrected Example |
public class SharkSim implements Runnable { float length; ... } |
There is a related mistake that occasionally confuses students: mis-ordering extends and implements clauses. The Java language specification states that the superclass declaration (extends) must come before any interface declarations (implements). Also, for interfaces, the keyword implements should appear only once; multiple interfaces must be separated by commas.
More Mistake Examples |
---|
// Out of order public class SharkSim implements Swimmer extends Animal { float length; ... } // multiple implements keywords public class DiverSim implements Swimmer implements Runnable { int airLeft; ... } |
Corrected Examples |
// Correct order public class SharkSim extends Animal implements Swimmer { float length; ... } // Multiple interfaces separated by commas public class DiverSim implements Swimmer, Runnable { int airLeft; ... } |
In the example below, the student wants to include the superclass toString() result in the subclass toString() result. The mistake is forgetting to capture and use the return value from super().
Mistake Example |
---|
public class GraphicalRectangle extends Rectangle { Color fillColor; boolean beveled; ... public String toString() { super(); return("color=" + fillColor + ", beveled=" + beveled); } } |
The fix for this problem is usually to assign the result of super() to a local variable, and then use that local variable as part of the computation of the subclass method return value.
Corrected Example |
---|
public class GraphicalRectangle extends Rectangle { Color fillColor; boolean beveled; ... public String toString() { String rectStr = super(); return(rectStr + " - " + "color=" + fillColor + ", beveled=" + beveled); } } |
Students will sometimes forget this two-step process. They will create a component correctly, then forget to incorporate it into the interface. This omission will not trigger any compile-time error messages or any run-time exceptions; the components will simply not be visible.
Mistake Example |
---|
public class TestFrame extends Frame implements ActionListener { public Button exit; public TestFrame() { super("Test Frame"); exit = new Button("Quit"); } } |
The fix, of course, is to add the component or components in question. The example below shows how to do this. Note that a student that forgets to add a component will often also forget to set up appropriate event handling for that component. I always check for proper event handling when a student shows me some AWT code, because it is easy to forget event handling when crafting a GUI.
Corrected Example |
---|
public class TestFrame extends Frame implements ActionListener { public Button exit; public TestFrame() { super("Test Frame"); exit = new Button("Quit"); Panel controlPanel = new Panel(); controlPanel.add(exit); add("Center", controlPanel); exit.addActionListener(this); } public void actionPerformed(ActionEvent e) { System.exit(0); } } |
Classes from the package java.lang are imported automatically into every Java program. Some students may forget that classes from other Java standard packages, such as java.net, are not imported automatically, but must be explicitly imported.
In the example below, the Canvas superclass is not defined. This mistake will be caught by the compiler, but the error message will simply point out that the class is undefined, and will not say anything about the necessity for an import statement. Similarly, the class Vector is not defined either.
Mistake Example |
---|
public class MyCanvas extends Canvas { int x; Vector stuff; } |
To correct the mistake, the class Canvas must be imported from the java.awt package, or it must be referred to by its full name.
Corrected Example 1 |
---|
import java.awt.*; import java.util.*; public class MyCanvas extends Canvas { int x; Vector stuff; } |
Corrected Example 2 |
public class MyCanvas extends java.awt.Canvas { int x; java.util.Vector stuff; } |
In the example below, the student wants to do some animation using a Runnable, but has forgotten to start their thread.
Mistake Example |
---|
public class AnimCanvas extends Canvas implements Runnable { protected Thread myThread; public AnimCanvas() { myThread = new Thread(this); } // the run() method never gets called because the // thread never gets started. public void run() { for(int n = 0; n < 10000; n++) { try { Thread.sleep(100); } catch (InterruptedException e) { } animateStep(n); } } ... } |
Corrected Example |
public class AnimCanvas extends Canvas implements Runnable { static final int LIMIT = 10000; protected Thread myThread; public AnimCanvas() { myThread = new Thread(this); myThread.start(); } public void run() { for(int n = 0; n < LIMIT; n++) { try { Thread.sleep(100); } catch (InterruptedException e) { } animateStep(n); } } ... } |
The thread lifecycle and the relationship of threads to Runnables is an important practical topic for Java instruction. It is a good idea to show students several examples of simple tasks implemented as both Thread subclasses and as Runnables.
The old readLine() method is still part of the JDK, but the compiler flags it as deprecated. This is often very confusing to students. It is important to explain that using the old java.io.DataInputStream readLine() method is not wrong, it is just old-fashioned. Encourage the student to use BufferedReader instead.
Mistake Example |
---|
public class LineReader { private DataInputStream dis; public LineReader(InputStream is) { dis = new DataInputStream(is); } public String getLine() { String ret = null; try { ret = dis.readLine(); // Warning! Deprecated! } catch (IOException ie) { } return ret; } } |
Corrected Example |
public class LineReader { private BufferedReader br; public LineReader(InputStream is) { br = new BufferedReader(new InputStreamReader(is)); } public String getLine() { String ret = null; try { ret = br.readLine(); } catch (IOException ie) { } return ret; } } |
There are several other features from Java 1.0 that are officially deprecated in later Java releases, but this is the one that hits new students the most often.
Now, Java does not permit implicit data type casting when the operation might lose information. Such casting must be explicit. For example, Java will not allow assignment of an int literal to a byte variable without a cast, as shown in the example below.
byte byteValue1 = 17; /* WRONG! */ byte byteValue2 = (byte)19; /* OKAY */
Because a numeric literal with a decimal point in it represents a double, and because assigning a double to a float could lose precision, the Java compiler complain about any attempt to use such a numeric literal as a float. Therefore, simple statements like this one can prevent a class from compiling. Here is an example like the one above.
float realValue1 = -1.7; /* WRONG! */ float realValue2 = (float)(-1.9); /* OKAY */
Programmers accustomed to the forgiving data type strictures of C and C++ will often be stymied by this problem. It is understandable, and the burden sits squarely on the Java programming instructor to give his or her students clear instruction about data types and how to handle them. There are three ways to fix this particular problem.
float realValue1 = 1.7; /* WRONG! */ float realValue2 = 1.9f; /* OKAY */
float realValue1 = 1.7f; double realValue2 = 1.9; realValue1 = (float)realValue2;
For more information about floating point numbers, click here or here.
I'll try to put additional instructive Java mistakes here as my students commit them.
[Main Java Page] [Neal Ziring] [Julie Ziring] [Sign Guestbook]
This page written by Neal Ziring, last modified 7/7/01.