Pitfalls in Java

    J. Dalbey, 3/14/00

My experience is that Java is a difficult language for novices to learn programming, in part because the confusing syntax and semantics. Here is a list of difficulties that students encounter using Java in their first programming course. Almost all of these are problems that students don't struggle with in Pascal or Ada languages.

There are two categories: pitfalls and gotchas.

A Pitfall is when the program compiles without error but gives incorrect results. This is really bad and the cause of such famous disasters as the AT&T collapse of 1990.

A Gotcha is just a confusing or obscure issue that often causes difficulties, but usually results in some error message (even if it is difficult to diagnose).


Pitfalls

Case sensitive

Java is case sensitive which causes needless headaches. For example, a student declared a global variable 'Alpha', and within a function declared a local variable 'alpha', but in the function body referred to 'Alpha'. The function compiled and produced incorrect results. Admittedly, the student violated guidelines for good variable names, but the case sensitivity of the language is not an aid to learning.

The keywords in the language are the obvious syntactical elements one might like to emphasize by making them upper case, but this is not allowed.

Integer division

The division operator is overloaded, thus 1 / 2 does not equal 0.5 as most students expect. Pascal has a separate operator, div, for integer division so students don't fall into this pitfall.

Loops without braces

Java allows loops without braces around the body. Almost every student falls into this trap at least once:

while (condition)
    statement1;
    statement2; //only first statement is in body of loop

Students assume body statements are in the body. Similarly,

while (condition)
    statement1; statement2

Both statements appear to be in loop, but only first is.
The above examples would not compile in other languages.

Decisions without braces

Same problem as above.

Null Body Loops

A loop with a null body is allowed to compile, e.g.

while (condition); // compiles ok, causes infinite loop
    statement;

This error is very difficult for students to detect. The same problem occurs with "if" statements.

Ignoring a function value

A peculiarity of the Java language is that it lets you ignore the value returned by a value-returning function. For example, you could write the following statement in your program without any complaint from the compiler:

Math.sqrt(x); // return value ignored

When this statement is executed, the value returned by sqrt is promptly discarded. This function call has absolutely no effect except to waste the computer's time by calculating a value that is never used. This causes problems for students when they invoke their own methods but forget to use the return value ... the program compiles but gives wrong results.

Unexpected results from System.in.read()

System.in is an InputStream and the read() method definition says it returns an int. But calling System.in.read() does NOT return the integer you type at the keyboard.

Methods won't modify parameters of a primitive type
    // Try to Add one to argument: compiler allows it but it doesn't work
    static private void Increment (int Number) { Number = Number + 1; }

    int Sum = 0;
    Increment (Sum);  // does NOT actually increment

Array and String declarations

Arrays and Strings are indexed starting at zero, which is really bizarre. Who starts counting at zero? So naturally a student trying to extract the first character in a string will use the command

// trying to isolate the first character, but it actually gets the second.
OneLetter = MyString.charAt(1);

Array and String declarations

Array declarations are weird: the last item of array int[35] Lucy is Lucy[34]. Go Figure.

Array indices must be integers and start at zero, so students struggle in a situation where the data in the problem are not integers: frequency count of characters is a classic example. For instance:

// Compiles, but sure doesn't do what the student expects.
int[] Count = new int[26];
Count['A'] = 0; // causes run-time error
Increment-assignment

The statement: Total += 2;
increments Total by 2 is commonly miscoded as Total =+ 2;
which unfortunately DOES compile and simply assigns 2 to Total.

Type Casting a Character to an Integer

Students often expect that one could "convert" a character to an integer by type casting. This will compile, but doesn't give the results one might expect.

Char symbol = '7';
System.out.println((int) Symbol); // displays 55 not 7.

Implicit conversion of method parameters

You can pass a character parameter to a method with an integer argument, giving unexpected results.

String Comparison

Don't use equality operator to compare Strings. A statement which uses the equality operator to compare two Strings will compile but will not give the expected results.

Strings Lucy = "one";
Strings Linus = "one";
If (Linus == Lucy) // gives FALSE

This is particularly nasty, because it appears to work correctly, but it doesn't. The proper way to compare Strings is with the equals() method.

If (Linus.equals(Lucy))

Using Java references instead of values

In the code fragment here, d1 and d2 are not values, but references to values. Student's commonly err in thinking that because d1 and d2 "have the same date" that they should be equal. But the "if" statement below is never true.

Date d1 = new Date(25,11,100); // means Dec 25th 2000 (weird!)
Date d2 = new Date(25,11,100);
if (d1 == d2) // never true!

case without break

If you mistakenly forget to put a break after a case in a switch statement, it will compile and execute and give incorrect results. The case just falls through to the next case.

This is a big dangerous pitfall.

Variables of primitive types hold values; variables of class types hold references

Widget Lucy = new Widget("one");
Widget Linus;
Linus = Lucy; //makes both variables refer to the same object.
Linus.Set("three"); // Also sets Lucy to "three" ???? confusing to beginners and causes unexpected results

Similarly, instance variables of a class type can be changed, even if declared private.

(See Savitch, pg 275 - 278)

Overloading occurs before automatic type conversion

If a class has overloaded methods

Public void set(int Age)
Public void set(double Weight)

And you call it with Lucy.set(6) when you meant 6.0, it sets the age instead of the weight, because overloading happens before the 6 can get converted to 6.0.


Gotchas

Braces

Braces are difficult to see because they are so narrow and require carefully scrutiny to determine which direction they point. Other languages use keywords such as BEGIN-END which stand out are are easy to find matching pairs.

Confusing symbols

The following symbols do not have obvious meanings and are often confusing. Other languages have more obvious keywords or clear symbols.

= means assignment not equality
== means comparison but who can remember to type the second equal sign?
&& means logical AND not "more of"
|| means logical OR (why don't they just use keyword OR?)
!  means logical NOT not emphasis (exclamation point)
+ can mean addition or concatenation
\n   who knows what these mean?!
++
+=

Omitting the default constructor

If your provide at least one constructor definition, java won’t provide the default constructor. This is easy to forget - however it will be caught by the compiler, but just confusing tofigure out.

Inconsistent semantics

The use of the "private" keyword is inconsistent. It works in some places, but not others. It may NOT be used on any declaration, only in specific places. It can't be used inside a method. Students frequently do something like the following, and the error message is very misleading.

public void SadMethod ( )
{
private int grumpy;                // keyword "private" not allowed here

Produces this misleading compiler error:
'}' expected.