Java Gotchas

Java Gotchas are quirks in the language or standard libraries. Some might call them bugs, some features, some nasty surprises. Here is a chart of the some dangerous waters.
Gotcha Difficulty
We Aren't In Kansas Anymore
basic differences from C
1
Default Fall Through 1
Inconsistencies 2
Is char a character or a number? 2
Octal 2
The Case Of The Disappearing Constructors 2
Missing Hex 2
Math.sin 2
Where's the Beep? 2
Where's the Root Directory? 2
Unstoppable for 2
Upper Case Surprises 2
Shiftless Shift 2
String.substring 2
String Comparison 2
Override vs Shadow 3
"broken" setLocation, setSize, setBackground, setForeground 3
Unsigned Bytes 3
Modulus 3
Array Initialisation 3
Matrix Initialisation 3
ArrayStoreException 3
Static Initialisers 3
Instance Initialisers 3
Casts 3
Implicit Casts 3
Concatenation 3
M y O u t p u t L o o k s L i k e T h i s 3
Cascaded Assignment Operators 3
Random Numbers 4
java.util.Date 4
java.awt.Graphics.drawRect 4
java.awt.Graphics.drawString 4
GridBagLayout 4
Deprecation Blues 4
java.io.BufferedReader & BufferedInputStream 4
Applets Can't Use The Local Hard Disk 4
Reconstituted Serialised Objects 4
"Broken" Repaint 4
Hidden Components Won't Stay Hidden 4
Misleading Error Messages 5
Bugs 6
Credits -

We Aren't In Kansas Anymore

C/C++ programmers will attempt to write code like this: You need to spell these out longhand in Java:

Default Fall Through

C programmers are familiar with this, but those coming from languages designed by Professor Wirth will gasp in astonishment that Java would allow such unstructured code. When k is 1, the program will print out both "hello" and "hi". Case clauses fall though by default. You won't get a syntax error or even a warning if you leave out the break after each case.

Inconsistencies

The designers of Java never took the time to clean up all the methods and make them use consistent conventions. Here are some examples: The conversion and I/O functions are impossible to memorise there is so little pattern to them. See the conversion table of methods.

Java is quite inconsistent about caps, e.g. java.util.TimeZone but Date.getTimezoneOffset, instanceof but Beans.isInstanceOf, Hashtable but HashCode, StreamTokenizer.whitespaceChars, System.arraycopy Color.RGBtoHSBand GridBagConstraints.gridwidth.

Is char a character or a number?

char can be thought of as a character or an unsigned 16 bit integer. This ambiguity catches up with us in a statement like this: In JDK 1.1, x is "foos". In early JDK 1.2 it is "foo115", but was fixed for the release version.

Octal

I have not seen octal used since the days of the Univac 1106, however Java has carried on the C and C++ tradition. Any numeric literal with a lead zero on it is considered base 8, octal. Beware, programmers are used to using lead zeros simply to align numbers in columns.

The Case Of The Disappearing Constructors

When you define a constructor, you must not specify a return type, even though it returns an object. You may not even specify a void return type. Even though it is effectively a static method, you may not declare it static. If you do any of theses things, the compiler will think your constructor is just an ordinary method. You will be baffled by the compiler's complaints that you have not defined a suitable constructor to match the arguments you provide.

If you don't provide any constructors at all, Java will automatically provide you with a default constructor the form:

However, if you get ambitious and write an extra constructor, say like this: The default constructor will disappear! You have to then provide it yourself explicitly.

You will most likely come across this problem when you see the following error message:

In English, it means you are missing the default constructor for your Applet.

Missing Hex

In Java strings, you can no longer say "\xf2", "\a" or "\v". Happily "\n", "\r", "\\", "\'" and "\"" still work. To encode control characters you must now use octal, e.g. "\012" for a linefeed, formerly "\x0a". Octal character constants must be exactly 3 digits. See literal in the Java Glossary.

The new hex Unicode technique "\u003f" has a catch. It is not suitable for encoding control characters. It is evaluated in a pre-processor pass and converted to a character, then the code is compiled. If for example you wrote "\u000a", this gets converted to:
"
"
Since the \u000a gets converted to a newline character.

Math.sin

Every novice tries code like this: And stares and stares at it wondering why it won't work. You need to write it this way: There are three places to trip up:
  1. the Math package works in radians, not degrees. 360 degrees = 2 pi radians.
  2. You need the Math.sin instead of plain sin because sin, cos, tan etc. are static functions. Compared with other languages you may have used, Java is picky and verbose. It wants the "Math." even when there is no name clash with a sin function in some class other than Math or some package other than java.lang. Java wants to avoid even the possibility of an eventual name clash by making you qualify with Math.sin now. In a similar way, you must precede all static function and method invocations with the classname.
  3. You need the Math.PI instead of plain PI because PI is a static constant. In a similar way you must precede all static constants with the class name.

Where's the Beep?

Java does not have a built-in set of sounds. It ignores '\a' in console output, though you can use \007.

In JDK 1.1 You can make a simple beep with java.awt.Toolkit.beep(). I have seen reports that beep does not work properly on some platforms.

In JDK 1.0.2 you can use

You can also play au files with AudioClip.play.

See sound in the Java glossary for more details.

Where's the Root Directory?

You might look a long time through java.io.* trying to find the directory operations, before you find them hiding in the File class e.g. list a directory to find out the files in it. However, won't cut it. To look at the root. You must say: Be aware that "\" in Java strings has to be written as "\\".

This makes reading strings representing filenames confusing. Unix systems use the "/" path separator character instead of "\". Macintoshes use ":" and have interesting twists like "::" for up instead of "/../" as in Unix. To write platform independent code, you should use the system file.separator and path.separator, or use the File methods that construct filenames for you from parts.

Then you might well ask, how do you find out which drives are available? In JDK 1.2 there is a new method:

Unstoppable for

How many times will that for loop execute? Until you kill the process! It will loop endlessly because i can never get bigger than Long.MAX_VALUE to terminate the loop. There are similar problems with Integer.MAX_VALUE, Long.MIN_VALUE and Integer.MIN_VALUE.

Upper Case Surprises

Take a look at the source of java.lang.String.toUpperCase(). You might expect it to contain only some very simple code of the form: However, you will discover the code is quite elaborate. For example, it tests if the current locale is Turkish to call special code to cope with the dotless i. It tests for the German and converts it to a pair of letters "SS"! If you are only working with English, you might want to roll your own more streamlined version.

Shiftless Shift

The shift operators can give some surprising results. You can't shift ints by more than 31 bits. If you try it, you will shift by that amount modulo 32. Similarly you can't shift longs my more than 63 bits. If you try it, you will shift by that amount modulo 64. This applies to <<< << and >> So:

String.substring

In other languages, to extract a substring, you give an offset where the string starts and a length in characters. In Java, you provide two zero-based offsets. The first points to the start of the string as you might expect, and the second points one past the end of the string.

If you specify offsets that are outside the span of the string, you don't get a truncated or null string; you raise a StringIndexOutOfBoundsException. One way to remember the way it works is that you specify the first character to include and the first character to exclude.

Why do it this way?

String Comparison

Unless Strings have been interned, with String.intern(), you cannot compare them for equality with ==. You have to use equals() instead. The compiler will not warn you if you inadvertently use ==. Unfortunately, the bug may take a long time to surface if your compiler or virtual machine is doing transparent interning. If you want to compare for < or > you cannot use the usual comparison operators, you have to use compareTo() instead. will return: You can think of it roughly like treating the strings as numbers and returning s-t.

Novices might be surprised by the following results:

Override vs Shadow

What happens when you reuse a method or variable name in a subclass? It depends. There are four cases to consider:
  1. static method
  2. instance method
  3. static variable
  4. instance variable
Do you inherit the superclass version or get subclass version? This is all so confusing, I suggest you perform some experiments. Here a little program I wrote to discover the various shadowing and overriding behaviours: class Grandma class Mom extends Grandma For more discussion, see shadowing variables and overriding methods in the Java glossary. My general advice is never to shadow variables. There is no need for it. It just causes confusion. In summary:

"broken" setLocation, setSize, setBackground, setForeground

People often complain they can't get setLocation, setSize, setBounds, setBackground or setForeground to work. The problem is usually that something else is setting them and overriding your settings: Culprits include:
  1. Layout Managers. They do resize() and move() (the deprecated method names) on the contents of each container. Only the null layout manager will leave your sizes intact.
  2. generated code in Visual Cafe will do a move() and show() (the deprecated names) in an overridden show() method.
  3. Your own code using deprecated names like move() or resize().

Unsigned Bytes

Back when the earth was still molten, when characters still had only 7 bits, somebody thought it would be a good idea if characters were signed. This caused a schism in the C world when 8-bit characters later appeared. Java added unsigned 16-bit Unicode characters, but decided to support only signed 8-bit characters, known as bytes. Perhaps Java's designers wanted to encourage migration to Unicode by making handling unsigned bytes awkward. In any case, you most often want unsigned 8-bit characters, not signed. How do you fake them?

Modulus

In Java you take the remainder with the % operator. In Java, the sign of the remainder follows the dividend, not the divisor. Java division has the Euclidean property. When you multiply the quotient by the divisor and add the remainder you get back to the dividend. Java division is truncated division.

Floored division is what you normally want when trying to figure out which bin an item belongs in. You can compute floored division as:

For computing how many fixed-size bins you need to contain N items, you want ceiled division, also known as the covered quotient. You can compute the covered quotient as:

Signs Division Modulus
+ + +7/+4=+1 +7%+4=+3
- + -7/+4=-1 -7%+4=-3
+ - +7/-4=-1 +7%-4=+3
- - -7/-4=+1 -7%-4=-3
I have a general rule to avoid writing code that depends on the expected sign of the modulus. It is often a source of bugs since people testing have their own ideas of how the answers should be. For example the Microsoft JIT gives wrong signs even for division, but the interpreter gives correct ones.

Array Initialisation

A fixed length block of primitives or references. Java never stores blocks of repeating structures. It always creates contiguous blocks of references to separately stored structures. Novices make two common errors: failing to allocate space for the array and failing to allocate objects for each cell. Java automatically initialises arrays of primitives to 0 and arrays of references to null. It won't create any objects for you automatically. Here is how you would allocate an array of primitives: Here is how you would allocate an array of objects.

Matrix Initialisation

In Java, a matrix is an array of arrays. Each row of the array can even have a different number of columns. It is not stored in one contiguous block

Under the hood, to find an element, you first index by row into a contiguous block of pointers. That points you to the start of one of another contiguous block of pointers. You index into that block by column, which points you to associated object. If you had a 3x5 matrix of objects you would have 1+3+(3*5)=19 separate allocation regions for the matrix and its objects.

Here is the generalized way you would use declare and initialize a 3x5 rectangular matrix.

If you fail to initialise the array, Java automatically initialises it for you to zeroes. If you have a matrix of objects, and you fail to initialise, Java initialises it to nulls for you. It does not allocate empty objects at each grid point for you. You have to allocate the objects yourself like this: Here is how you could create a triangular matrix: You can initialise a matrix to a list of values this way: You might think you could similarly assign a matrix constant to an array like this: However, the syntax needed (introduced with JDK 1.1) is more verbose: In all these examples, you can use mat.length and mat[i].length to avoid repeating the constants that define the matrix's dimensions.

ArrayStoreException

Arrays are like virgins. They are very careful about what they allow in themselves. When you construct an array you declare what sorts of Object you are going to allow into the array. You cannot violate that sacred trust:

For example:

This will get you an ArrayStoreException since the array, as constructed, only allows Dalmatians (or their subclasses) in it, even though the declaration says it will allow any Dog in.

however this:

Whenever you store an object into an array, at run time, the JVM checks to make sure the object is of a compatible type. There are a few cases where this check is not necessary, e.g. if Dog had no subclasses.

Static Initialisers

You have to enclose any initialisation code for static (class) variables inside a sandwich like this: Newbies just stick such code anywhere inside the class { } sandwich and are baffled by the misleading error messages.

Instance Initialisers

You have the option of initialising an instance variable in the declaration or in the constructor.

The advantage of putting it on the declaration is that you need to specify it only once, not once for each constructor. This means there is less likelihood of error if its value is ever changed.

Casts

There are four sorts of cast:
  1. to expand a value: This cast is nugatory, though you might want to use the cast as a documentation aid. It does some conversion work -- sign extension.
  2. to trim a value: This style of cast actually may do some real conversion work -- zeroing out high order bits.
  3. to treat a reference as its superclass: This cast is nugatory, though you might want to use the case as a documentation aid. All Dalmatians automatically have all the Dog fields, so this cast has no run-time overhead.
  4. to treat a reference as one of its descendants: At run time, this cast actually checks that aDog truly is already a Dalmatian, and raises a ClassCastException if it does not. It does not make any attempt to convert a Dog to a Dalmatian.

    Casts with abstract classes and interfaces work the same way as classes.

So where are the gotchas?

Implicit Casts

Conversions and promotions occur both when you explicitly request them, and sometimes automatically.
  1. Automatic Assignment Conversion converts an expression's type to a variable's type (ex. short value = 26). This type of conversion is allowed when converting from a type to that same type (identity conversion), when performing a widening conversion, or when performing a narrowing conversion which assigns an int to a byte, short, or char variable where the int is representable by the (byte, short, or char) variable. Note that this form of conversion occurs only in assignments that preclude exceptions by definition.
  2. Automatic Numeric Promotion homogenates operands to allow an operation (ex. 1.0f + 2.0 will cause 1.0f to be promoted to a double).
  3. Automatic Method Invocation Conversion occurs when passing arguments during a method invocation (ex. calling methodA(45) on a method defined as methodeA(long value)). Except for disallowing implicit narrowing of integer constants, this form of conversion's behavior is identical to that of automatic assignment conversion. Note that this form of conversion occurs only when the argument types passed to the method can be automatically converted to those specified in the method signature in a manner which precludes exceptions by definition.
  4. Automatic String Conversion allows any type to be converted to type String. This occurs when the "+" String concatenating operator is used (ex. String resultString = "the answer is:" + result, where result can be of any type)

Concatenation

Java uses the + operator to mean both addition and concatenation. Parsers can unambiguously figure out which your intent is from the context, but humans can be easily fooled. For example: Which + are addition? Which are concatenation?

The concatenation operator has the magic power of being able to implicitly coerce an int into a String by automatically invoking the
static String Integer.toString(int)
method, however, you can't do the same thing explicitly with a (String) cast.

M y O u t p u t L o o k s L i k e T h i s

There are 9 common character handling types in Java
Type mutable? size in bits signed? Description
Strings immutable 16 unsigned Unicode
StringBuffer mutable 16 unsigned Unicode
char mutable 16 unsigned individual Unicode character.
Character immutable 16 unsigned Unicode character object.
char[] mutable 16 unsigned array of Unicode characters.
byte mutable 8 signed individual ASCII char.
Byte immutable 8 signed ASCII char object.
byte[] mutable 8 signed array of ASCII chars.
UTF immutable 8/16 unsigned 16-bit length, 7 bit chars, multibyte codes for 16-bit chars with high bit on.
Especially when you are doing I/O. you need to be very clear whether you have 8 or 16 bit characters internally and 8 or 16 bit characters externally. Some I/O methods convert, some do not. A hex file viewer will help you track down such problems. An ASCII character when converted to Unicode has a high order 0 byte prepended, since all Java I/O is big-endian.

Cascaded Assignment Operators

Consider the following correct code to swap a and b without using an intermediate temporary: You might think you could collapse that program like this, as you can in some C++ compilers. It may not be legitimate C++, but some compilers allow it. However, in Java it does not work. You just get a=0. Even adding parentheses does not help: As a general rule, avoid cascading any of the Java combo assign/compute operators such as ^= += -= *= /= %= &= |= <<= >>= >>>= or =, especially when you use the same variable both on the left and right of an assignment operator.

Random Numbers

I have seen dozens of routines posted on the Internet for generating uniformly distributed random numbers. Almost none of them worked. If you want 100 evenly distributed random integers 0..10 here is how you would generate them: To get an evenly distributed random number between integers low and high inclusive use: Using nextInt for generating random integers is faster than using nextDouble, multiplying and converting to int as many authors suggest. In solving problems of this sort, you must be mindful that nextInt returns a positive or negative integer, including MIN_VALUE (whose absolute value cannot be represented) and MAX_VALUE, and that Java division/modulus has unexpected results for negative operands. You must also be careful to maintain even distribution. Consider the following code that also produces a random integer in the range 0 .. 10. However that code would generate 5 twice as frequently as the other values.
If you wanted a random double between 0.0 and 10.0 here is how you would generate it. nextDouble() can return a 0.0, but never a 1.0. However, if you want a random number in the interval [0, N), taking the result returned by nextDouble() and multiplying by N will not always work; for some values of N, the final result can be N (the high bound). nextDouble() works by taking 53 random bits divided by (double) (1 << 53). So, the closest it can get to 1.0 is 0.99999999999999988897. When you multiply by a sufficiently large number, the tiny difference from 1.0 gets lost, and the result is the same as if you had started with 1.0 not a number just less than 1.0. According to Merlin Hughes, any number that the generator produces will occur 32 times more commonly than for a perfect distribution; 31 of every 32 numbers just won't ever be produced. Math.Random is an alternate method create random doubles that does not require you to create a Random object, but it does not give you control over the seed.

About every four days someone will post the following code as a way to generate a random integer 0 .. 10:

There are four things wrong with the technique:
  1. It will generate a number 0 to 9, not 0 to 10.
  2. However the technique in general may once in a blue moon generate "10". It won't actually do this with 10, but it will hit the upper bound with larger ranges, so I think it wise a avoid the technique on general principles.
  3. It requires two int <=> double floating point conversions and a floating point multiply. These are slow operations, and completely unnecessary.
  4. Math.random gives you no power over the seed. It is hard to debug if you program behaves a totally different way every time you run it.

The random number generator is portable and repeatable. If two Random objects are created with the same seed and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers in all Java Implementations.

The Java random number generator does very poorly at generating the low order bit. It tends to repeat itself every 128 times you call nextInt. It tends to get stuck for long runs of 0s or 1s, if you look only at every 128th result. In other words it is somewhat predictable. Bit 1 tends to repeat on a 256 cycle. The following code to generate simulate a heads or tail coin flip will thus not work very well.

You can avoid the sticky low order bits by picking a bit out of the middle of the result this way:

Sun must have heard me (and others) screaming every time somebody posted yet another piece of unworking code to generate random numbers. In JDK 1.2 they have built in two new generators nextBoolean() which avoids the low order sticky bit problem and nextInt(10) which will generate a positive number 0 .. 9.

One final caveat. Seed the random number generator only once. If you keep restarting it (by doing new Random() more than once) using the system time default or some chosen seed, your numbers are not going to be very random. Since the clock does not tick over all that fast you may even create generators with identical default seeds, which then generate identical streams of numbers.

lemonjava.util.Date

The java.util.Date class is crawling with gotchas. It is a disgrace. It is the lemon of Java and deserves a giant string of raspberries. One way out is to use my BigDate class which handles dates from 999,999 BC to 999,999 AD. Sun has deprecated most of the Date methods and replaced them with GregorianCalendar. It has not nearly as many limitations as Date, it has got rid of those ridiculous 1900-based years, however it is obscenely complicated, still relies on the old Date class and maintains a lot of the Date lunacy such 0-based months. Happily the documentation in JDK 1.2 is better, though ambiguity whether local or UTC parameters are wanted still plagues. Sun still refuses to document units of measure. For example, are TimeZone offsets in milliseconds, seconds, minutes?

java.awt.Graphics.drawRect

draws a rectangle one pixel bigger than the specified width and height. I am told if you understand the abstract drawing model the AWT uses, it turns out this extra pixel is deliberate and unavoidable. One way of thinking about it is the AWT thinks it is drawing lines infinitely thin, but they smudge a bit, one pixel down and to the right.

java.awt.Graphics.drawString

All graphics routines expect x,y to represent the upper left corner of a bounding box. However for Graphics.drawString() x,y refers to the to the baseline (which is distinct yet again from the lower left corner). This inconsistency is traditional in drawing packages. You need to take into account the font metrics:

GridBagLayout

Whenever you use any layout manager, other than null, it is going to decide the sizes and placement of the components. Your setLocation(), setBounds() and setSize() calls will all be overridden. Some ways you can get finer control are: GridBagLayout sometimes behaves strangely, generating oddly asymmetric layouts. The problem can usually be traced to trying to put two components into the same grid cell. You won't get any error message when you do this.

GridBagLayout will generate goofy layouts when components provide incorrect numbers for minimum and preferred size. For example TextFields don't take into consideration setColumns or the size of the current font. All you can do is fudge using the ipadx and ipady parameters to inflate the minimum size.

GridBayLayout does not mind if you have a row or column with nothing does not mind if you have a row or column with nothing in it. It will take no space. You might consider leaving some empty rows and columns in your layouts for future expansion.

weightx and weighty control where the extra space goes if the container is expanded. Think of them as percentages that don't have to add up to 100%. They are automatically normalised. To figure out which column should get the most space, GridBagLayout examines each component in the column, and looks at its weightx. It saves the biggest weightx of all the components in that column as the weight for the entire column. It does not average them, or add them. Then it proportionately assigns the extra space based on the column weights. The component with a lot of weight does not necessarily grow, just the column that component is in. Giving equal but non-zero weight to columns tends to equalize their size.

GridBagLayout does the same thing allocating extra space to rows by using weighty.

The Insets(top, left, bottom, right) can be used to build a border around a component. The four numbers are measured in pixels.

Deprecation Blues

With JDK 1.1, Sun brought more order to the naming of various methods, particularly in the AWT. The old names are still supported but deprecated (discouraged from use pending complete removal). Deprecated names are not aliases the compiler translates to the new names. They are full fledged methods in their own right. I wondered why vendors like Sun and Symantec were so reluctant to abandon the old names entirely and convert completely to the new scheme. I have discovered why.

setVisible()calls the deprecated show(), the reverse of that you might expect. You would think the deprecated method should bear the speed penalty of another layer of indirection. Yet consider what happens if you write a new setVisible() method to override one of the built-in ones. Users of the original show() method will be unaffected. They will continue to use the old code. Only those who directly call setVisible() will use your new routine. Now, consider what happens if you write a new deprecated show() method to override one of the built-in ones. All works properly; everyone will use your new method. You are thus stuck writing new deprecated methods if you want your code to work properly.

Let us say the AWT were redesigned so that instead show()called setVisible(). Then old code that used the deprecated methods would suddenly stop working.

This problem is general and applies to all deprecated methods. Let us hope Sun will soon get rid of the deprecated methods entirely, then this problem will go away. Most of the deprecated names are just name changes to fit the JavaBeans get/set conventions. Such deprecations could be handled as pure aliases by translation to the new names inside the compiler, and do away with the old classes entirely. However, that would cause a political problem of JDK 1.0.2 code no longer running under JDK 1.1 without recompilation or some translation process. You could not then have code that would run both under JDK 1.02 and 1.1. We would need to support the translation process in the JVM to have old cold automatically use the new names. Sun is very reluctant to make any changes to the JVM.

The JDK 1.0.2 event handling routines are also deprecated. It is quite a bit bigger job to convert those. They could not be handled by a simple alias.

java.io.BufferedReader & BufferedInputStream

is advertised to block until some input is available. It returns the number of bytes read, or -1 for EOF. You might erroneously presume that it blocks either: Not so. You might get as little as one-byte back, even when you are nowhere near the EOF. Len just controls the maximum amount you are prepared to accept. has a similar gotcha. You must use readFully if you want to get all the bytes you asked for.

The read routine has another problem. It does it traps and ignores IOExceptions rather than passing them on to you. To get around both the above problems, you can use your own read routine like this:

Applets Can't Use The Local Hard Disk

The whole idea of an Applet is to protect the user from you putting any files or meddling with any files on his hard disk, so you are going to have to cheat if you want your Applet to be able to write or read his local hard disk. Here are seven possibilities:
  1. Give the user a new security manager that has to be installed specially that gives permission to just your Applet to write to disk. Unfortunately, this won't work if anybody else does the same thing. Security managers are still a black art. I have not yet seen any documentation on just how you would do this.
  2. Convert your Applet to an application. The user has to download and install it, find and install some sort of standalone Java system for his platform, then run it. whew!
  3. Write a native class to cheat and do the I/O behind the security manager's back. You will need to write such a native class for each different platform, then arrange to have it installed separately ahead of time. Ouch!
  4. Use JavaScript or Active-X or some other gonzo scheme that cares not a fig for security.
  5. Join the ranks of other programmers with their torches and pitchforks demanding some sort of chimera -- half Applet/half application. It would be allowed access to a limited amount of disk space, and would not have access to any files it did not create. It could run inside a browser. This would have general applicability. You could do off-line data entry for example then upload, or retain application preference information, cache server data, ...
  6. Using the preferences of Internet Explorer, if you list an application's site as a "Trusted Site", then if you set the security zone for "Trusted Sites" to "Custom" and change the settings such that Java permissions are "Unrestricted" and "Launch applications and files" is enabled, whew!, you will be able to write/read files from the local hard drive from within an Applet. Unfortunately Netscape has no equivalent feature.
  7. Lobby for a generic user-configurable security manager, that lets users OK various naughty behaviours from specific applets. The Applet would have an interface to request permission for special dispensation with minimum and ideal requirements.

Reconstituted Serialised Objects

The process of serialisation and reconstituting objects is fraught with problems. What could you do to ensure transient fields in reconstituted objects are properly initialised?

"Broken" Repaint

A very common beginners problem is failure of repaint() to redraw the screen. repaint() works by putting an object in a queue to remind the system to schedule the paint() later. It will never get around to servicing the queue if you don't quickly return from handling the keystroke or button press event. Calling Thread.sleep() just makes matters worse, since the current thread is the one that will have to later do the paint().

Hidden Components Won't Stay Hidden

setEnabled(false) disables a component by graying it out. setVisible(false) (a.k.a hide()) makes the component totally disappear, with an invalidate(), which marks all containing components as needing a repack(), so that surrounding components will be shuffled to grow into the space it vacates. setVisible(true) (a.k.a. show()) also marks visible all contained subcomponents. This means a component you have hidden will infuriatingly unhide itself the next time you setVisible(true) the enclosing window.

I know of no method that will let you hide a component, that does not invalidate, thus leaving its space reserved, with no shuffling of sibling components.

Misleading Error Messages

A compiler looks at source code from quite a different perspective that humans do. You gradually get to know what your compiler really means when it says baffling things like "{ expected."

Sometimes a single syntax error starts off an avalanche of baffling compiler error messages. As a general rule, look slightly ahead of where the compiler is complaining. Fix any problems there and recompile. Most of the time the other errors will disappear.

When you start using a compiler, it is a good idea to deliberately make some common errors, and see what the compiler says. Then you can make a table to help you later when you inadvertently make that error. It also helps to have two or three compilers on tap. When you get a baffling error message, try some other compilers. With three variants on the error message, you have extra clues what it really means.

For example here is a table for Symantec's Visual Cafe Family. Please email me versions of this table for your compiler to post here.
What compiler says real error
';' expected. 'else' without if. statement expected. invalid expression. missing semicolon
All class files and packages you reference in import statements must be accessible via the CLASSPATH, or be part of the project. class Mypackage.Myclass not found in an import
Applet not inited (sic) Missing package statement. These erroneous Applets will often work with the Java Plug-in, or when run locally, but will fail with native Javas when run on the web. There can also be problems with your jar file having too much or too little qualification of class names. Your <APPLET CODE= must have nothing but the classname, with the .class. Make sure the case and name exactly match the name of the *.java file, *.class file, and class name. For a class in a package this would have dots in it, e.g. cmp.MyPackage.Myclass.class, but it would not have any directory qualification. Your CODEBASE= parameters must have an absolute http://-style reference to the base directory where the code is stored. For a local hard disk, the only thing I could get to work reliably on NT with all browsers and Appletviewers is leaving the CODEBASE out entirely. You may find for your platform you have to code it something like this: ///C|//MyDir/ or C:\MyDir\. Your ARCHIVE= parameter must have a list of the jar files, separated by commas. If you have too little or too much qualification, or if you fail to use the file naming conventions of your server, you will be in trouble.
ambiguous class x.y.SomeClass and a.b.SomeClass import x.y.SomeClass;
import a.b.SomeClass;
should be changed to:
import x.y.*;
import a.b.*;
Some compilers may complain about the clash in SomeClass, even if you never reference it. And of course all references to Someclass should be disambiguated to either x.y.SomeClass or a.b.Someclass. Alternatively, you can throw out all the imports, and fully qualify all classes in x.y and a.b. This approach makes code easier to maintain, because it is easier to find the code that implements the class when it is fully qualified.
Array a may not have been initialized. You forgot to initialise an array with new int[5].
Bad magic number The first few bytes of class file are supposed to say CAFEBABE in hex. They don't. Most likely the problem is you uploaded your class file to your Web server using FTP ASCII mode instead of BINARY mode which mangled the class file.
Can't access MyPackage.MyClass. Only classes and interfaces in other packages can be accessed. You forgot to make the class public.
Can't make a static reference to nonstatic variable x in MyClass. using an instance variable in a static method
Class not found This can occur at compile or run time.
  • Some other syntax error ahead of the class declaration is preventing the compiler from seeing the class declaration.
  • The class is not in the proper file in the proper directory.
  • The class is not public.
  • The class does not have the correct case, either in the class name or the file name.
  • The corresponding class or java file is not on the CLASSPATH (considering the package name.)
Class WindowAdapter not found in type declaration. You forgot to import java.awt.event.* or to fully qualify java.awt.event.WindowAdapter.
ClassFormatError You mangled the class file FTP upload by doing it as ASCII instead of binary. Further your web server must be configured to send *.class files as binary rather than ASCII. It needs a MIME configuration entry to define *.class files as type application/octet-stream instead of text/plain. Sometimes it is actually a CLASSPATH problem. It can't find the class.
Duplicate method declaration. duplicate method declaration
Error: MyClass is an abstract class. It can't be instantiated. missing method to fulfill an interface implementation
Exception java.io.IOException is never thrown in the body of the corresponding try statement. This usually means there is some syntax error in the code that would throw the exception. Fix other errors and this one will go away.
Identifier expected. '}' expected. forgetting static { } around class init code
Illegal use of nonvirtual function call An inner class is not permitted to call private methods of the enclosing outer class. It is permitted to call protected methods. This error message appears in Symantec 2.5a only when you have debugging turned off.
IllegalAccessError
  • Failing to use both the <APPLET ARCHIVE= parameter to specify a comma-delimited list of jar file names and the CODEBASE= paramter in the form of an absolute http://-style reference to where the jar file is.
  • Generally problems with CLASSPATH, CODEBASE, and ARCHIVE.
  • Classes in jar have the wrong amount of qualification stored with them.
  • Classes in packages are not stored in the correspondingly named directories. See CLASSPATH and java.exe in the Java glossary for a fuller discussion.
Incompatible type for =. Explicit cast needed to convert int to byte. missing a cast such as (byte)
IncompatibleClassChangeError You forgot the static on your main method.
Invalid method declaration; return type required. forgetting void return type on a method declaration.
load: cmp.MyPackage.MyApplet.class can't be instantiated. java.lang.InstantiationException: cmp/MyPackage/MyApplet You are missing the default constructor for your Applet. See the section earlier in the gotchas on The Case of the Disappearing Constructor.
main must be static and void An application's main class must have a method public static void main (String[] args). Under Project Option Main Class, you must declare the name of that class, without a terminating .class.
Method x() not found in MyClass. undefined method
Method MyClass() not found in MyClass You wrote MyClass x = Myclass(); instead of MyClass x = new Myclass();
myClass.class does not contain myClass as expected, but MyClass. Please remove the file. Class myClass not found in new. missing caps on new MyClass() reference.
myClass.class does not contain myClass as expected, but MyClass. Please remove the file. Class myClass not found in new. missing caps on MyClass() obj declaration.
No error while developing. Security Violation in production. Applets can only access the server they were loaded from.
No error while developing. Security Violation in production. Applets are not permitted to read or write local files.
No method matching myMethod() found in MyClass You have the wrong number of parameters or the wrong parameter types for the method. It can also mean you defined a method with the wrong visibility modifier, e.g. none, private or protected when you meant public.
no warning. missing caps on a class name declaration.
no warning. caps on a variable declaration
no warning. Case fall through is the default. missing break
no warning. The array is automatically initialised to null. This will likely soon lead to a java.lang.NullPointerException when you try to apply some method to one of the elements of the array. Note NullPointerException, not NullReferenceException. You forgot to initialise an array of strings or objects to some value.
no warning. In debug mode, if you forget to make your main method public, you will not be warned. You won't discover the problem until later. main must be public static void.
no warning. The array is automatically initialised to zeroes. You forgot to initialise an int array to some value.
no warning. Constructor treated as a method. specifying a void return type on a constructor
NoClassDefFoundError One of your static initialisers invokes a function that uses a static not yet initialised.
Alternatively, if you have classes included in your project that don't live in the same directory as the project, you must also do a Project | Options | Directory | Classes to include their directories in the invisible CLASSPATH for the project. Simply including the java or class files in the project is not sufficient.
Somehow your runtime is having trouble finding classes it could find at compile time. Check your CLASSPATH. Make sure all the classes are available.
Consider combining class files in to a single jar file.
Check that your browser supports jar files.
You can get this error if you try to run a JDK 1.1 Applet on a 1.0 browser since it does not have the 1.1 classes.
Return required at end of MyClass Myclass(..). specifying a MyClass return type on a constructor
'return' with value from constructor: MyClass(..). specifying a return this in a constructor
Statement expected. missing } in a method
Type expected. identifier expected. extra }, or literally a missing type, especially in constant declarations like: public static final SOMETHING=3; instead of public static final int SOMETHING=3;
Unable to load MyClass.class for debugging. Symantec support suggests copying all the DDLs in VCP\JAVA\BIN to VCP\BIN to correct stability problems. If you do this, all debugging will cease to function. Delete any DDL in VCP\BIN that also exists is VCP\JAVA\BIN. Check that you fully qualified the name of your class with its package name, e.g. cmp.business.TestDate in the Project section of Visual Cafe, no trailing ".class" and that the upper and lower case is precisely correct. Read the section in the glossary on CLASSPATH and under java.exe on how packages, classnames, the directory structure and the CLASSPATH all interact. If that does not solve it, I have bad news.

Windows 95 has the most incredibly inept scheme for dealing with DLLs from different vendors that just happen to have the same 8+3 name, (even when they are stored in totally different directories). Whichever application starts first gets its DLLs installed, and every other app that comes aftward has to use them. If you start some app before VC, it may load incompatible DLLs. To avoid this, load VC first. Then the other apps may stop working. Phttt! Mr. Gates has guaranteed himself a seat in hell for this (and for the confounded registry where all configuration information for all applications is lumped in one unrestorable basket). There should be separate system DLLs and application DLLs with no possibility of DLLs provided by one vendor being foisted on some other. Try starting Visual Cafe before any other app and keep it running. It stands a better chance then of getting its own DLLs loaded.

Unable to run MyClass.class: Check that you have properly specified name of the class containing main in your project settings. Your vep project file is not in the same directory with the *.java and *.class files that compose the class containing Main.
Undefined variable x; or Variable x in SomeOtherClass not accessible from MyClass Incompatible type for =. caps on a variable reference
Undefined variable: x. undefined variable
UnsatisiedLinkError Missing native class. Class name not properly qualified.
Variable 'x' is already defined in this method. duplicate variable declaration
Variable x may not have been initialized. missing initialisation for a temporary variable
Warning: Public MyClass must be defined in a file called 'MyClass.java'. class name does not match source filename
Warning: Public class MyClass must be defined in a file called 'MyClass.java'. Putting more than one public class per file. Also getting the capitalisation wrong in the filename on the javac command line or in the filename itself.
} expected. Type expected. Identifier expected. missing } at the end of a class. Perhaps the missing } is on a line containing // before the }.

Bugs

There are also outright bugs in the various Java compilers and runtimes. Sun maintains a searchable list at http://developer.java.sun.com/. Don't be put off by the login id and password. It is free to register. You can also submit bugs, and vote on which bugs you feel should be given highest priority. They also accept feature requests and gotchas as bugs.

You can also use http://java.sun.com/cgi-bin/bugreport.cgi.

Use search engines, DejaNews, the newsgroups such as comp.lang.java.programmer, and the Java Developer Connection web pages to see if others have reported a similar bug.

Similarly you can contact Netsape via http://developer.netscape.com/.

You can report bugs in Microsoft's SDK via http://www.microsoft.com/java/misc/sdkbug.htm.

If you think you have found a new bug, build a small test case that clearly demonstrates the bug. Most reported bugs are not bugs at all, but coding errors. If you can keep your example small enough, you will be much more convincing that you truly have found a bug. Ask a friend to test the code on another platform to help decide if the problem is with the compiler or the JVM or a native library.

Finally, if you would just like to complain about the design of Java or its implementation, you can expound on the comp.lang.java.advocacy newsgroup or send email to feedback@java.sun.com.

Credits

As you might guess, a great many people helped compile this list. I have only recently started giving credit. If you would like to be added to this list, please tell me.
Tov Are tovj@stud.ntnu.no
Paul van Keep paul@sumatra.nl
Mike Cowlishaw mfc@vnet.ibm.com
Pierre Baillargeon pierre@jazzmail.com
Bill Wilkinson billw@chilisoft.com
Patricia Shanahan pats@abcm.org
Joseph Bowbeer jozart@csi.com
Charles Thomas cftoma1@facstaff.wisc.edu
Joel Crisp Joel.Crisp@gb.swissbank.com
Eric Nagler epn@eric-nagler.com
Daniel Leuck dan@pretium.com
William Brogden wbrogden@bga.com
Yves Bossu ybossu@fisystem.fr