Last updated 2004-06-28 by Roedy
Green ©1996-2004 Canadian Mind Products
Java definitions: 0-9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
You are here : home : Java Glossary : G words : Gotchas.
I spoke on this topic in 1997 November at the Colorado Summit Conference, and again in 1999 November.
| Where | Extension Mandatory? | Example |
|---|---|---|
| compiling | mandatory |
CD \MyDir javac.exe -classpath . HelloWorld.java
|
| executing | must exclude |
CD \MyDir java.exe -classpath . HelloWorld
|
| Applet | mandatory |
<applet code="HelloWorld.class" width="230" height="240"> <img src="images/NoJava4U.jpg"> </applet>
|
if ( width ) { widen( width ); } if ( myDog ) { myDog.bark(); }
You need to spell these out longhand in Java:
if ( width != 0 ) { widen( width ); } if ( myDog != null ) { myDog.bark(); }
However, for boolean variables you can say things like:
if ( tooWide ) { widen( width ); }
Beware, you can also inadvertently say things like like:
if ( tooWide = true ) { widen( width ); }
Note: that the = above is an assignment operator, not an == comparison operator. The if is always true.
switch ( k ) { case 1: System.out.println( "hello" ); case 2: System.out.println( "hi" ); }
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.
Because the syntax for defining cases and labels is so similar, it is easy to make an error like this: The programmer left out a space and, instead of handling n == 3, he defined a goto label called case3 in his switch statement.
switch ( n ) { case 1: ... case 2: ... case3: ... case 4: ... }
In JDK 1.4+ you can use the javac.exe -Xswitchcheck to get the compiler to warn you if you do this. However, there is no syntax to tell Java when you meant to do it deliberately. e.g. a break case or fallthrough keyword.
You need to treat primitives as objects for another purpose -- so that you can put them into containers such as ArrayLists or Vectors. In Java, you must manually wrap your primitive up in an object. Sun provides a set of immutable object wrapper classes called: Byte, Short, Character (not Char), Integer (not Int), Float and Double, the very same classes that house your static methods for fiddling with primitives! It might have been clearer had they called these classes names like ImmutableByte or ByteWrapper, but they didn't. You can't change the value of the primitive inside any of these wrapper objects. All you can do is create a new object with a new immutable primitive sitting inside.
To make matters all the more confusing, the Double class then has double duty, to deal with both double primitives and Double wrapper objects. The instance methods for Double manipulate Double objects and the static methods for Double manipulate double primitives. These methods naturally have similar if not identical names! The same problem occurs for all the other primitive/class pairs, but most newbies seem to trip over it first with
double d; /* double primitive */ Double dd; /* Double wrapper */ String g; g = Double.toString( d ); /* double primitive */ g = dd.toString(); /* Double wrapper */
See the conversion for help in interconverting the various primitives and wrappers.
// ways to set text in a component TextField.setText(); Label.setText(); Button.setLabel(); AbstractButton.setText(); Frame.setTitle(); // Since setTitle does not put the text in the frame itself, this naming is reasonable. // ways to change an element setCharAt(int index, char c ); Vector.setElementAt( Object o, intindex ); List.set( int index, Object o ); // ways to get length lenOfArray = myArray.length; lenOfString = myString.length(); myList.size();
We have System.currentTimeMillis but Calendar.getTime InMillis.
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.
Beans.isInstanceOf
Character.isWhitespace
Checkbox
Color.RGBtoHSB
Date.getTimezoneOffset
File.mkdirs
GridBagConstraints.gridwidth
hashCode
Hashtable
instanceof
java.lang.Runtime
java.util.TimeZone
JpopupMenu
Keymap
org.omg.SendingContext.RunTime
StreamTokenizer.whitespaceChars
System.arraycopy
Some collections use putAll others addAll, some put some add. You can remember this way, you put with a key or index, but you add to the end.
Iterators and Enumerations are almost identical, but use shorter names for the methods. In general older classes offer an Enumeration and newer ones offer an Iterator or a Collection. To the newcomer, it seems almost random if a class offers an Iterator or a Enumeration with iterator/ elements, hasNext/hasMoreElements, next/getNextElement.
With the default toString, you just get the class name and hashcode of the object, which looks like gibberish, e.g. [C@ad3ba4.
char[] c = { 'a' , 'b' , 'c'}; System.out.println( c.toString() );
will print out the address of c, not the characters that compose it. You must use new String ( char[] ) to convert a char array to a String.
Label.toString() is not an alias for Label.getText(). It will print out a summary of the Label fields like this: java.awt.Label[label0,0,0,0x0,invalid,align=left,text=def]
How then do you convert to String? With a hodgepodge of techniques that rival French verbs for irregularity.
String x = "foo" + 's';
In JDK 1.1, x is "foos". In early JDK 1.2 it is "foo115", but was fixed for the release version.
Even though a constructor is similar to static method that returns a new object, you may not declare the constructor static. It is not static, since it works on specific default object. this is defined in the constructor.
If you do any of these 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.
It may help if you realise that new allocates/creates the object and the constructor initializes it. Therefore the constructor has no need to return an object.
If you don't provide any constructors at all, Java will automatically provide you with a default constructor the form:
public MyClass() { super(); }
However, if you get ambitious and write an extra constructor, say like this:
private MyClass( int fudgicles ) { this.fudgicles = fudgicles; }
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 public constructor for your Applet. See constructor.
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. It definitely won't work for Cr and Lf. It might work for some of the other control chars in some compilers. I suggest using the octal forms for safety.
double x = 90; double y = sin( x ); double z = tan( x + PI );
And stares and stares at it wondering why it won't work. You need to write it this way:
static final double CONVERT_DEGREES_TO_RADIANS =Math.PI / 180; double x = 90 * CONVERT_DEGREES_TO_RADIANS; double y = Math.sin( x ); double z = Math.tan( x + Math.PI );
There are three places to trip up:
public static double toDegrees ( double angleInRadians ); public static double toRadians ( double angleInDegrees );
Futher, every time you do a floating point calculation you loose a little precision. It all adds up.
In JDK 1.1 You can make a simple beep with j 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
System.out.print ( "\007" ); System.out.flush();
You can also play AU, wav, midi and aiff files with AudioClip.play.
See sound in the Java & Internet Glossary for more details.
File dir = new File( "C:\\" ); String[] files = dir.list();
won't cut it. To look at the root. You must say:
File dir = new File( "C:\\." ); String[] files =dir.list();
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:
File[] roots = File.listRoots();
for ( long i=Long.MAX_VALUE -2; i<=Long.MAX_VALUE; i++ ) { /* ...*/ }
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. You get used to thinking of the upper limit as the logical stopping point, where canonical for loops actually stop on one past that value.
if ( 'a' <= theChar && theChar <= 'z' ) theChar -= ('a' -'A' );
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. If you take a string to lower case then back to upper case then back to lower case, you won't necessarily end up where you started.
int i = -1; i = i >>> 32; // i is -1, not 0 as you might expect. long k = 1; k = k << 65; // k is 2, not 0 as you might expect.
As a corollary, shifting left 32 bits does not clear an int. It is a no-op! Be especially careful when working with ints that will eventually be promoted to longs.
byte b = 4; long x = b << 56; // does not work, since shift done modulo 32 and result is int. long x = ( ong)b << 56; // correct
Be especially careful with any calculated shift operands. The shift modulo behavior makes creation of bit selections masks unnecessarily tricky. Suppose you want a general way of extracting bits i through j of an int. An obvious way to do it is to right shift the input by j bits, then & it with a mask containing j-i+1 one bits at the low order end. An obvious way to construct an n-bit right justified mask is (1<<n)-1. This is not correct in Java for n==32. (1 <<32) is 1, not 0, so subtracting 1 from it will result in a zero mask rather than an all-ones mask.
Java works this way for speed since many CPUs work that way. There is an RFE to provide an official way around this. In the meantime here is a little class you can use:
"abcdefg".substring ( 3, 5 ) gives "de". "abcdefg".substring ( 3, 7 ) gives "defg". "abcdefg".substring ( 3, 8 ) gives StringIndexOutOfBoundsException.
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. But not quite. You are allow to be
just barely past the end.
"emptiness".substring( 9 )
returns "" (an empty string)
"emptiness".substring( 10 )
gives a StringIndexOutOfBoundsException
Why do it this way?
Then:
"APPLE".substring (1,4)
gives "PPL".
"APPLE".substring (3,3)
gives "".
"APPLE".substring (0,5)
gives "APPLE".
Beware of using substring with only one argument.
tail = x.( endindex );
gets you the tail end of a string starting at endindex.
head = x.substring( 0, length );
gets you the first part of the string, the first length characters.
if ( Double.isNaN( d ) )
or with isInfinite. You can't test it directly with:
if ( d == Double.NaN )
Though for some bizarre reason you can use == to compare with Double.POSITIVE_INFINITY. There is a corresponding Float.NaN and Float.isNaN. The theory is making NaN not equal to itself allows a quick and dirty way to test for a calculation going haywire.
if ( result != result ) { System.out.println( "oops" ); }
String s = "apple"; String t = "orange"; if ( s.compareTo(t) < 0 ) { System.out.println( "s < t" ); }
compareTo will return:
Novices might be astonished by the following results:
int[] intArray = null; String.valueOf( intArray ); // produces the String "null" char[] charArray = null; String.valueOf( charArray ); // throws NullPointerException
final Thing thing = new Thing( 7 ); thing = otherThing; // generates a compiler error thing.setSize( 10 ); // ok thing.girth = 6; // ok
This also applies to parameters declared final.
Watch out for chains of multiplied integer constants:
IBM's BigDecimal and Archimath BigDecimal have provisions for detecting overflow and generating an exception. You can roll your own with code like this in standard Java.
/* * Overflow checking. * adds the two parameters, throwing a MyOverflowException if the result is not an int. * @param a first operand to add * @param b second operand to add * @result a + b */ public static int addSafe(int a , int b) throws MyOverflowException { if ( (a > 0 && b > Integer.MAX_VALUE - a) || ( a < 0 && b < Integer.MIN_VALUE - a ) ) { throw new MyOverflowException( a + " + " + b + " = " + ((long)a + (long)b) ); } return a + b; }
or this one to detect overflow in multiplication:
/** * multiplies the two parameters, throwing a MyOverflowException if the result is not an int. * @param a multiplier * @param b multiplicand * @result product */ public static int multSafe(int a, int b) throws MyOverflowException { long result = (long)a * (long)b; int desiredhibits = - ((int)( result >>> 31 ) & 1); int actualhibits = (int)( result >>> 32 ); if ( desiredhibits == actualhibits ) { return (int)result; } else { throw new MyOverflowException( a + " * " + b + " = " + result ); } }
If you know both values are positive, you can use a simpler overflow check. You could check if the sum is less than 0 or less than one of the two operands. I'm not sure if this catches all overflows, however.
Floating point arithmetic uses the IEEE error propagating scheme similar to that used in a spreadsheet to propagate invalid value flags to cells that depend on invalid values. In Java, though there is no overflow interrupt or notification, there is a good chance you will eventually find out about an overflow error when you see a java.lang.Double.POSITIVE_INFINITY, java.lang.Double.NEGATIVE_INFINITY or java.lang.Double.NaN showing up in one of your double variables. You can't use == to test for NaN, you must use java.lang.Double.isNan().
For more discussion, see shadowing variables and overriding methods in the Java & Internet Glossary. My general advice is never to shadow variables. There is no need for it. It just causes confusion. In summary:
int i1 = b2 & 0xff; byte b2 = (byte)( b2 + 1 ); byte b3 = b2;
Floored division is what you normally want when trying to figure out which bin an item belongs in. You can compute floored division as:
( dividend >= 0 ) ? ( dividend / divisor ) : ( ( dividend-divisor + 1 ) / divisor );
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:
(dividend >= 0) ? ( ( dividend+divisor -1 ) / divisor) : ( dividend / divisor );
| 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 |
// note how you never specify the array's size in its type declaration. int[]v; // allocate space for the array v =new int [100]; for ( int i=0;i<v.length;i++ ) { v[i] = i*2 + 999; }
Here is how you would allocate an array of objects.
Cell[] v = new Cell [100]; for ( int i=0; i<v.length; i++ ) { v[i] = new Cell( i, 999 ); }
A common error to make is to declare an array: e.g.
long[] a;
then later to attempt to initialise it with:
long[] a = new long[100];
This does not work because you have declared a local stack variable, unrelated to the first one. Make sure you do it like this:
long[] a; // then later initialise it with: a = new long [100];
You can initialise an array to a list of values this way:
int[] array = { 11, 12 , 13, 21, 22, 21, 31, 32, 33 };
The same technique works with arrays of objects:
BigDate[] dates = { new BigDate( 1999, 1, 1 ), new BigDate( 1999, 12, 31 ) };
You might think you could similarly assign a array constant to an array like this:
array = { 11, 12, 13, 21, 22, 21, 31, 32, 33 };
However, the syntax needed (introduced with JDK 1.1) is more verbose:
array = new int[] { 11, 12 , 13, 21, 22 , 21, 31, 32 , 33 };
You can put in an extra comma at the end, without ill effect. This makes maintaining vertical lists easier:
array = new int[] { 11, 12, 13, 21, };
You can even use anonymous arrays:
// Array Compare gotchas import java.util.Arrays; ... int[] a = new int[]{ 10, 20 }; int[] b = new int[]{ 10, 20 }; // gives "wrong" answer: false, compares for same object System.out.println( a == b ); // gives "wrong" answer: false, compares for same object System.out.println( a.equals( b ) ); // gives correct answer: true, equal if all elements equal. System.out.println( Arrays.equals( a, b ) );
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.
// This is the fully general way to initialise a matrix. // There are shorter ways to specify initialisation, // but this is always what happens under the covers. // Note how you never specify the array's size in its type declaration. int[][] mat; // for each row, allocate a slot for a pointer to an array mat = new int [3][]; for ( int i=0; i<3; i++ ) { // allocate an array for each row mat[i] = new int[5]; for ( int j=0; j<5; j++ ) { mat[i][j] = i*j+100; } }
Here is a shorter way:
// This is the usual shorthand way to initialise a matrix. int[][] mat = new int[3][5]; for ( int i=0; i<3; i++ ) { for ( int j=0; j<5; j++ ) { mat[i][j] = i*j+100; } }
And here is a more realistic way, avoiding hard coded constants:
// Using length instead of hard-coded constants static final int NUM_ROWS = 3; static final int NUM_COLS = 5; int[][] mat = new int [NUM_ROWS][NUM_COLS]; for ( int i=0; i<mat.length; i++ ) { for ( int j=0; j<mat[i].length; j++ ) { mat[i][j] = i*j+100; } }
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:
Cell[][] mat = new Cell[3][5]; for ( int i=0; i<3; i++ ) { for ( int j=0; j<5; j++ ) { mat[i][j] = new Cell( i, j, 100 ); } }
Here is how you could create a triangular matrix:
int[][] mat; // for each row, allocate a slot for a pointer to an array mat = new int [100][]; for ( int i=0; i<100; i++ ) { // allocate an array for each row mat[i] = new int [i+1]; for ( int j=0; j<=i; j++ ) { mat[i][j] = i * j + 100; } }
You can initialise a matrix to a list of values this way:
int[][] mat = { { 11 , 12, 13} , { 21, 22 , 21} , { 31 , 32, 33}};
You might think you could similarly assign a matrix constant to an array like this:
mat = { { 11, 12, 13} , { 21, 22, 21} , { 31, 32, 33} };
However, the syntax needed (introduced with JDK 1.1) is more verbose:
mat = new int [][] { { 11 , 12, 13} , { 21, 22 , 21} , { 31 , 32, 33}};
In all these examples, you can use mat.length and mat[i].length to avoid repeating the constants that define the matrix's dimensions.
For serious matrix work you need a matrix package such as MTJ.
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:
Dog[] mutts = new Dalmatian[20]; mutts[2] = new PitBull(); /* Prang!! */
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:
Dog[] mutts = new Dog [20]; mutts[2] = new PitBull(); /* is perfectly ok */
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.
A common error is to have an Object[] returned from Vector in which all the elements actually are Strings. You can cast the elements individually from Object to String, but you can't cast the entire array object, Object[] to String[], even if all the individual elements currently happen to be Strings. You could only cast Object[] to String[] if the array Object actually were created as a String[]. To get around this problem, to ensure Vector gives you an array of Strings instead of an array of Objects, use code like this to extract arrays from Vectors:
String[] strArray = ( String[])v.toArray( new String [v.size()] );
System.arraycopy will copy element by element casting each as necessary. That allows you to convert an Object[] to a String[]. You can do the same yourself in a for loop. Surprisingly, the for loop technique often generates faster code than arraycopy. arraycopy will not grow an array to hold the new elements.
You might imagine a cast from Dog[] to Dalmatian[] should be allowed if the array currently contains only Dalamatian elements. It would be an expensive check. Further, you would then have two references to the array. There would be nothing to stop you from later adding some PitBull elements via the Dog[] reference, violating the consistency requirement of the Dalmatian[] reference. That's why the ArrayStore gotcha has to be there.
static { calcPriceTab();}
Newbies just stick such code anywhere inside the class { } sandwich and are baffled by the misleading error messages.
The order of your variables that are statically initialised matters. Consider this little program:
import java.util .Random; public class OrderTest { static Random wheel = new Random(); static final int age1 = getAge(); static final int age = wheel.nextInt (80 ) + 5; static final int age2 = getAge(); public static int getAge() { return age; } public static void main(String [] args) { System.out.println( age + " " + age1 + " " + age2 ); } }
The output with Java 1.4.1 was no compile error and 61 0 61, YMMV.
The order of those three static finals is crucial to the results! You must put them in the order you want the calculations done. Java is not smart like a spreadsheet to do natural order recalcs for you. It will notice and either handle or reject such forward references when only literals and simple variables are involved, but as soon as you do things inside methods, it throws up its hands and says on your own head be it.
// init on the declaration int n = 100; // init in the constructor public MyClass() { n = 100; } // init in instance initializer block { n =hairyFunction(); if ( n > 1000 ) { n = 1000; }
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. The other safe approach is to put all your initialisation code in one method or one constructor and have all the constructors call it.
However, there is one particularly tricky problem with initialisation order. In general you must avoid calling any non-final methods in a constructor. The problem is that instance initialisers / variable initialisation in the derived class is performed after the constructor of the base class. This can cause a problem if the base class constructor calls a method polymorphically, since that method will be presuming its derived class fields have all been initialised when they have not.
So base class constructors can safely call private or final methods of the base class provided those methods directly or indirectly call only private or final methods of the base class. If you call methods in your constructor polymorphically, the compiler will not stop or warn you of the initialisation pitfalls awaiting.
There are four sorts of cast:
byte b = -42; int i = (int)b;
This cast is nugatory, though you might want to use the cast as a documentation aid. It does some conversion work -- sign extension.
int i = -16411; byte b = (byte)i;
This style of cast actually may do some real conversion work -- zeroing out high order bits.
Dog myDog = (Dog)aDalmatian;
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.
Dalmatian myDalmatian = (Dalmatian)aDog;
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.
String s1 = (String) i; int i = (int)s2; String s3 = (String) myDalmatian;
Yet casting only works for two primitives. When there is a primitive and an object involved, there is a system of conversion functions with about as much regularity as French verbs.
Cat c = null; Object o = c; Dalmatian d = (Dalmatian)o;
In other words, there is one universal representation for null, not a special one for each class.
In contrast if ( null instanceof Dog ) is always false. instanceof does filter out nulls.
Dog fido = new Dalmatian(); int spots = (Dalmatian)fido.spotCount();
Hint: What are you trying to cast to Dalmatian, the Dog fido or the int result of the spotCount method? When you are casting, you often need a forest of parentheses. This is what you should have written:
Dog fido = new Dalmatian(); int spots = ((Dalmatian)fido).spotCount();
(B)( ( (A)va.get( 0 )).b ).c = x;
What does that mean? Get the first element of vector va (an Object), cast it to an A object which then, using field b, points to a another object. Cast that object to class B. In that object is a string reference c that you want set to x.
That would be so much easier to read as:
In getting those ()((()()))) right, count +1 for each ( and -1 for each ). You should get back to 0 at the end and any place you think you should be outside all (). You should never go negative. In that case above you count:
The problem is the infernal mix of prefix, postfix and infix operators that Java inherited from C. (At least the goofiness of String x [] is sort of gone).
In a language with postfix casts and flatter precedence that might read:
System.out.println(" x+y " + x+y); System.out.println( x+y + " x+y " );
Which + are addition? Which are concatenation?
The way you most commonly get caught is is code like this where the last + is treated as concatenation.
System.out.println( "value: " + v + 1 );
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.
What do you think this little code snippet produces?
System.out.println( 'A' ); System.out.println( 'A' + 'B' );
You might naively expect: A AB, or perhaps 65 131, however, the answer is: A 131.
The problem is Java's design blunder of using + to mean both addition and concatenation. Addition also promotes to int, which println displays differently from char.
I really want this fixed. Concatenation should get a new operator symbol and using + for concatenation should be deprecated. The current scheme leads to code too easy to misread. + with int can mean either addition or concatenation. Deciding which depends too much on subtle context clues.
| Type | mutable? | size in bits | signed? | Description |
|---|---|---|---|---|
| String | immutable | 16 | unsigned | Unicode |
| StringBuffer | mutable both in value and size | 16 | unsigned | Unicode |
| char | mutable value | 16 | unsigned | individual Unicode character. |
| Character | immutable | 16 | unsigned | Unicode character object. |
| char[] | mutable value | 16 | unsigned | array of Unicode characters. |
| byte | mutable value | 8 | signed | individual ASCII char. |
| Byte | immutable | 8 | signed | ASCII char object. |
| byte[] | mutable value | 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. |
int a=4; int b=5; a ^= b; // a=1, b=5 b ^= a; // a=1, b=4 a ^= b; // a=5, b=4
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.
int a=4; int b=5; a ^= b ^= a ^= b;
Even adding parentheses does not help:
int a=4; int b=5; a ^= ( b ^= ( a ^= b ) );
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.
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 you would use JDK 1.2s Random.nextInt(int) to generate them:
// Generate random integers 0 .. 10 import java.util.Random; ... // To get exactly the same results each run // seed the generator with a repeatable value. // Create the Random object only once. Random wheel = new Random( 149L ); ... for ( int i=0; i<100; i++ ) { // generate another random integer 0 ..10 int m = wheel.nextInt( 11 ); System.out.println( m ); }
Note, nextInt generates numbers 0 .. n-1, not 0 .. n.
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.
Don't use two different generators with a null seed. The default seed is based on time of day and thus the two generators will then usually give identical streams of numbers.
If you have JDK 1.1, which does not support nextInt(int), you can extend the Random class with the following code for nextInt stolen from JDK 1.2. Random.next(bits) cleverly selects the high order bits of the seed, which are more random. When n is a power of two, this code selects the high order bits of 31 selected bits. When n is not a power of two, it uses the less random low order bits.
class MyRandom extends java.util.Random { public int nextInt ( int n ) { if ( n <= 0 ) throw new IllegalArgumentException ( "n must be positive" ); if ( ( n & -n ) == n ) { // i.e., n is a power of 2 return(int)(( n *(long)next( 31) ) >> 31 ); } int bits, val; do { bits = next ( 31 ); val = bits % n; } while ( bits - val + ( n - 1 ) < 0 ); return val; } }
To get an evenly distributed random number between integers low and high inclusive use:
int m = wheel.nextInt( high - low + 1 ) + low;
To simulate throwing a coin, generating a boolean, use Random.nextBoolean. If you are using JDK 1.1 which does not support nextBoolean, you can use the following code which bypasses the sticky low-bit problem with nextInt. You can avoid the sticky low order bits by picking a bit out of the middle of the result this way:
boolean heads = ( wheel.nextInt() & 1 << 15 ) !=0;
If you wanted a random double between 0.0 and 10.0 here is how you would generate it.
// Generate random doubles 0.0 <= d < 10.0 import java.util.Random; ... // This time, to get different results each run, // seed the generator with the current time in milliseconds. Random wheel = new Random(); //... for ( int i=0; i<100; i++ ) { // generate a number between 0.0 and 1.0, then scale double d = wheel.nextDouble() * 10.0d; System.out.println( d ); }
If you want your random numbers to lie on a Bell shaped normal curve, use nextGaussian, which produces numbers with mean 0.0 and standard deviation 1.0.
![]() | Art Of Computer Programming | ||||||
| 0-201-48541-9 | |||||||
| Donald Knuth | |||||||
| volumes 1, 2 and 3. | |||||||
| |||||||
What if you need unique random integers? Generate them in the ordinary way with nextInt. Tick them off as used in a java.util.BitSet. Discard any that are already in use. If that Bitset would be too huge and you only need to generate a few random numbers, wrap them in Integers and store them in a HashSet.
When you are trying to simulate playing card hands, you can create an ArrayList of Integers 0 .. n-1, (or Card objects) and use Collections.shuffle. Then just peel the cards off the bottom or top of the ArrayList.
Math.Random is an alternate method to create random doubles that does not require you to create a Random object, but it does not give you control over the seed. Other than that, it has the same shortcomings as java.util.Random.
If you need very high quality random numbers for cryptography, see java.security.SecureRandom. Since it is derived from java.util.Random, it works much the same way. The main difference is the way you construct the wheel:
SecureRandom wheel = SecureRandom.getInstance ( "SHA1PRNG" , "SUN" );
Unfortunately, Netscape does not include any of the java.security classes. For ordinary use, use java.util.Random.
For a faster, higher quality random number generator try the Mersenne Twister.
To get true random numbers, suitable for use as one-time cryptographic xor pads, you need hardware that uses a physical process such as radioactive decay or a noisy diode to generate the randomness. See the Orion Random Number Generator. Random.org serves true random numbers on the web. The problem is anyone can see them. They are not your secrets. In a lighter vein, there is a random number generator on the web that watches a lava lamp. You could cook up a true random number generator with a low tech method. Connect a radio tuned to static to a 16 bit digital to analog converter (e.g. the digitizer on your sound card). Set gain to get samples close to 16 bit in amplitude. Use the collection of the lower 8 bits from all of the 16 bit samples. Again the source is not private, but close enough for most purposes.
Knuth gave me permission to translate his definitive random number generators into Java. Now all I need in the time and energy to tackle the task.
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.
int m = wheel.nextInt() % 6 + 5; // not recommended!!
However that code would generate 5 twice as frequently as the other values.
The following paragraph is controversial. Two experts disagreed on it. I have not taken the time to research it. In any case nextInt is faster than use nextDouble.
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.
About every four days someone will post the following code as a way to generate a random integer 0 .. 10:
int m = (int)( Math.random() * 10.0d ); // not recommended!!
There are four things wrong with the technique:
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.
boolean heads = ( wheel.nextInt() & 1 ) != 0; // not recommended!!
The following code works quickly to generate a random number between 0 and n, but the technique skews the results for large values of n, returning small numbers more frequently than it should. Instead use the nextInt(int) technique given above.
// Generate random integers 0 .. 10 import java.util.Random; ... // To get exactly the same results each run // seed the generator with a repeatable value. // Create the Random object only once. Random wheel = new Random( 149L ); ... for ( int i=0; i<100; i++ ) { // generate another random integer, mask off sign bit, take modulus int m = ( wheel.nextInt() & Integer.MAX_VALUE ) % 11; // use with caution!! System.out.println( m ); }
public int square( int n ) { try { return n*n; } finally { return 0; } }
Also, the behavior of exceptions thrown from within finally blocks is not obvious.
public class MyDateClass { public MyDateClass( Date d ) { m_date = d; } public String toString() { return ms_dateFormat.format(d); } // all instances of this class use the same date format // object to build their String representation: BAD!!! private static SimpleDateFormat ms_dateFormat = new SimpleDateFormat( "YYYY" ); }
java.math.BigDecimalFor greater efficiency, you can often use a long or int, and keep track of the scaling yourself, and inserting a decorative decimal point on output.
BigDecimal is a travesty and deserves raspberries just like Date. Briefly, it slow, difficult to use, uses native methods, and it quietly drops off significant digits during conversion.
Also, java.text.DecimalFormat.parse returns either a Long or a Double. There is no built-in way to define custom number formatting for BigDecimal or BigInteger objects.
Fortunately, IBM has made available its proposed replacement for Sun's class, which is 23 times faster, smaller, uses no native methods, and implements standard ANSI X3.274 floating arithmetic. For the spec see: IBM's Decimal Arithmetic For Java and AlphaWorks. Dirk Bosmans has implemented the IBM spec as ArciMath. Another implementation is PSPDec.
java.util.DateThe key to understanding Date is that it is not a date class, but a timestamp class. Inside each date is stored the number of milliseconds since 1970 Jan 1 GMT. It does not record the timezone or the timestamp in local time. You choose the timezone when you display the date with code like this:
Date now = new Date(); // initialise to current UTC Date/Time SimpleDateFormat sdf = new SimpleDateFormat( "EEEE yyyy/MM/dd hh:mm:ss aa zz : zzzzzz" ); sdf.setTimeZone( TimeZone.getDefault() ); // local time String dateString = sdf.format( now );
With Date, you can't have a date without a time. Keep in mind that when you display the same Date with different timezones you will sometimes get Dec 24, 1999 and sometimes Dec 25, 1999.
In contrast GregorianCalendar objects do contain a TimeZone reference along with the date and time. Like Dates, they are initialised with the current date and time. Unfortunately, if they are not sure about the user's timezone, the Sun classes quietly revert to GMT, and in early versions of GregorianCalendar, seem to revert to PST if you so much as breathe on them. DateFormats also have TimeZone that is similarly erratic. For code to work, your clients must have configured the TimeZone correctly in the OS. You can't help them configure it.
DateFormat.setTimeZone( TimeZone.getDefault() );
To get UTC, without any daylight saving correction, you can use:
DateFormat.setTimeZone( TimeZone.getTimeZone("UTC") );
You must configure your TimeZone property in your OS (for example, in the Windows control panel) for TimeZone.getDefault to work. The default timezone will be automatically configured in Java once you configure the correct timezone in your OS. However, selecting the right timezone is more than just selecting how many hours off GMT you are. You have to select the correct set of rules for when daylight savings changes. Java does everything internally in GMT and converts for display, so it is not enough just to manually reset your PC clock in the spring and fall. The configuring process can fail for exotic parts of the earth because the timezone tables either in the OS or Java are imperfect. Unfortunately, if you actually live in the GMT timezone, Java will think you want GMT standard time without any daylight savings changes. The best solution to this I know so far when your timezone is not properly supported is to roll your own TimeZone object with the proper daylight savings switchover dates.
// London time changes at 1 AM the last Sunday in March and the last Sunday in October, by one hour. TimeZone bstTimeZone = new SimpleTimeZone(0 , "UK" , Calendar.MARCH , -1, Calendar.SUNDAY, 1 * 60* 60* 1000, Calendar.OCTOBER, -1, Calendar.SUNDAY, 1 * 60* 60* 1000, 1 * 60* 60* 1000);
Alternatively, for applications, you can override the timezone on the java.exe command line with something like this:
java.exe -Duser.timezone=Europe/London HelloWorld
Make sure the -D goes before the classname, or it will be interpreted as an arg
to your HelloWorld class. You may have to do this if the JDK does not properly
recognise the name of the timezone that your OS calls it. The names for
timezones used in Java comes from a list maintained at NIH by Arthur David Olson.
Unfortuntately, the common names such as EST can mean
Eastern Standard Time: GMT+10 (in Australia)
Eastern Standard Time: GMT-5 (in the US)
so they are not suitable for international use. Pacific Time is called America/Los_Angeles
instead of America/PST. You have to get out an atlas to find out if you are
north of Los Angeles or South of Alaska. The idea is America/Los_Angeles means
PST/PDT, as appropriate. PDT would imply a daylight saving correction even in
winter. It would have made more sense to call it America
Pacific Time or America PST/PDT. America/Los_Angeles
only has meaning for Californians. When testing timezone-sensitive code, keep in
mind that the switchover between daylight and standard time does not happen at
midnight.
Here is a list of available timezones:
Add column 1 in hours to UTC to get local standard time.
Add column 2 in hours to UTC to get local daylight saving time.
Use UTC when you want no timezone at all.
gc = new GregorianCalendar(); gc.setLenient( false ); /* Bug only manifests if lenient set false */ gc.set( 2001, 1, 1, 1, 0, 0 ); nYear = gc.get ( Calendar.YEAR ); /* throws exception */
The bug disappears at 7AM on 2001/01/01 for MST.
java.awt.Graphics.drawRect( int x, int y , int width , int height )
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. The rationale is that you specify the path of an idealised box drawn with infinitely thin lines, and the pen hangs down and to the right, at least one pixel thick.
g.drawString( "Hello World" , 0, getFontMetrics(getFont()).getAscent() );
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 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.
If you want to fix the size of some element, you must use all three methods: setMinimumSize, setMaxiumSize and setPreferredSize.
Make sure you supply some non-zero x and y weights in your GridBag. Otherwise when you squeeze the frame down too small the components will act as if they had infinite space and will scoot madly offscreen to the right.
If you use GridBagConstraints.BOTH, forcing fill, that will override any setMaximumSize you may have specified on your panel.
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 code 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.
int BufferedInputStream.read( byte[] m, int offset, int len )
is advertised to block until some input is available. It returns the number of bytes read, or -1 for EOF. You might erroneouslypresume that it blocks either:
int BufferedReader.read ( char[] m, int offset, int len )
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 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:
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.
public void addNotify() { super.addNotify(); setBackground( Color.red ); }
Add notify creates the peer object. You are hooking your code in right after the peer gets created.
You also need to explicitly control the background of each component. Inheriting it from the Dialog will just give gray.
One further warning. In JDK 1.0, the modal feature of dialogs does not work.
Until nio, there was no select() like functionality in sockets (or to be more flexible, in InputStreams) and there was no timeout in the InputStream.read() method. This makes it impossible to program a Socket server having a number of threads less than the number of users. However, I am told that if you use the available() method cleverly you can fudge it.
| getKeyStroke or VK_xxxx | getKeyText |
|---|---|
| ACCEPT | "Accept" |
| ADD | "NumPad +" |
| AGAIN | "Again" |
| ALL_CANDIDATES | "All Candidates" |
| ALPHANUMERIC | "Alphanumeric" |
| ALT | "Alt" |
| ALT_GRAPH | "Alt Graph" |
| AMPERSAND | "Ampersand" |
| ASTERISK | "Asterisk" |
| AT | "At" |
| BACK_QUOTE | "Back Quote" |
| BACK_SLASH | "\" |
| BACK_SPACE | "Backspace" |
| BRACELEFT | "Left Brace" |
| BRACERIGHT | "Right Brace" |
| CANCEL | "Cancel" |
| CAPS_LOCK | "Caps Lock" |
| CIRCUMFLEX | "Circumflex" |
| CLEAR | "Clear" |
| CLOSE_BRACKET | "]" |
| CODE_INPUT | "Code Input" |
| COLON | "Colon" |
| COMMA | "," |
| COMPOSE | "Compose " |
| CONTROL | "Ctrl" |
| CONVERT | "Convert" |
| COPY | "Copy" |
| DEAD_ABOVEDOT | "Dead Above Dot" |
| DEAD_ABOVERING | "Dead Above Ring" |
| DEAD_ACUTE | "Dead Acute" |
| DEAD_BREVE | "Dead Breve" |
| DEAD_CARON | "Dead Caron" |
| DEAD_CEDILLA | "Dead Cedilla" |
| DEAD_CIRCUMFLEX | "Dead Circumflex" |
| DEAD_DIAERESIS | "Dead Diaeresis" |
| DEAD_DOUBLEACUTE | "Dead Double Acute" |
| DEAD_GRAVE | "Dead Grave" |
| DEAD_IOTA | "Dead Iota" |
| DEAD_MACRON | "Dead Macron" |
| DEAD_OGONEK | "Dead Ogonek" |
| DEAD_SEMIVOICED_SOUND | "Dead Semivoiced Sound" |
| DEAD_TILDE | "Dead Tilde" |
| DEAD_VOICED_SOUND | "Dead Voiced Sound" |
| DECIMAL | "NumPad ." |
| DELETE | "Delete" |
| DIVIDE | "NumPad /" |
| DOLLAR | "Dollar" |
| DOWN | "Down" |
| END | "End" |
| ENTER | "Enter" |
| EQUALS | "=" |
| ESCAPE | "Escape" |
| EURO_SIGN | "Euro" |
| EXCLAMATION_MARK | "Exclamation Mark" |
| FINAL | "Final" |
| FIND | "Find" |
| FULL_WIDTH | "Full-Witdh" |
| GREATER | "Greater" |
| HALF_WIDTH | "Half-Witdh" |
| HELP | "Help" |
| HIRAGANA | "Hiragana" |
| HOME | "Home" |
| INPUT_METHOD_ON_OFF | "Input Method On/Off" |
| INSERT | "Insert" |
| INVERTED_EXCLAMATION_MARK | "Inverted Exclamation Mark" |
| JAPANESE_HIRAGANA | "Japanese Hiragana" |
| JAPANESE_KATAKANA | "Japanese Katakana" |
| JAPANESE_ROMAN | "Japanese Roman" |
| KANA | "Kana" |
| KANA_LOCK | "Kana Lock" |
| KANJI | "Kanji" |
| KATAKANA | "Katakana" |
| KP_DOWN | "Down" |
| KP_LEFT | "Left" |
| KP_RIGHT | "Right" |
| KP_UP | "Up" |
| LEFT | "Left" |
| LEFT_PARENTHESIS | "Left Parenthesis" |
| LESS | "Less" |
| META | "Meta" |
| MINUS | "Minus" |
| MODECHANGE | "Mode Change" |
| MULTIPLY | "NumPad *" |
| NONCONVERT | "No Convert" |
| NUM_LOCK | "Num Lock" |
| NUMBER_SIGN | "Number Sign" |
| NUMPAD0 | "NumPad-0" |
| NUMPAD1 | "NumPad-1" |
| NUMPAD2 | "NumPad-2" |
| NUMPAD3 | "NumPad-3" |
| NUMPAD4 | "NumPad-4" |
| NUMPAD5 | "NumPad-5" |
| NUMPAD6 | "NumPad-6" |
| NUMPAD7 | "NumPad-7" |
| NUMPAD8 | "NumPad-8" |
| NUMPAD9 | "NumPad-9" |
| OPEN_BRACKET | "[" |
| PAGE_DOWN | "Page Down" |
| PAGE_UP | "Page Up" |
| PASTE | "Paste" |
| PAUSE | "Pause" |
| PERIOD | "." |
| PLUS | "Plus" |
| PREVIOUS_CANDIDATE | "Previous Candidate" |
| PRINTSCREEN | "Print Screen" |
| PROPS | "Props" |
| QUOTE | "Quote" |
| QUOTEDBL | "Double Quote" |
| RIGHT | "Right" |
| RIGHT_PARENTHESIS | "Right Parenthesis" |
| ROMAN_CHARACTERS | "Roman Characters" |
| SCROLL_LOCK | "Scroll Lock" |
| SEMICOLON | ";" |
| SEPARATER | "NumPad ," |
| SHIFT | "Shift" |
| SLASH | "/" |
| SPACE | "Space" |
| STOP | "Stop" |
| SUBTRACT | "NumPad -" |
| TAB | "Tab" |
| UNDERSCORE | "Underscore" |
| UNDO | "Undo" |
| UP | "Up" |
If you use an include for boilerplate headers and footers then you may get a ClassNotFoundException if the included JSPs uses a custom class.
ASP developers are used to source code being dropped in by the ASP preprocessor and interpretted as one big file. According to this logic an import statement would be expected to appear at the top of the "page", where most ppl declare variables and define functions are in ASP. However in JSP, things tend to be split up and stay split up into many small files. You must thus make sure the import goes in each file that uses it, not just at the beginning.
You hook up a TableCellRenderer with either TableColumn.setCellRenderer to control rendering a specific column or Jtable.setDefaultRenderer to control rendering of all objects of a given class. You use TableColumn.setHeaderRenderer to control rendering of the header for a specific column. If you hook up two cell renderers to the same column, they casade (the output of the first becomes the input to the second), and are both applied. The second does not replace the first.
// hooking up a custom cell renderer // given TableCellRenderer renderer, JTable jTable and Class renderClass if ( renderClass == Object. class || renderClass == null ) { /* ignore renderClass and install on each column */ TableColumnModel tm = jTable.getColumnModel(); int columns = tm.getColumnCount(); for ( int i=0; i<columns; i++ ) { TableColumn tc = tm.getColumn( i ); tc.setCellRenderer( renderer ); } } else { /* install on just renderClass */ jTable.setDefaultRenderer( renderClass, renderer ); }
If you write a TableCellRenderer for headings, make sure you call Jtable.setAutoCreateColumnsFromModel( false ); otherwise your renderer will be ignored, even though you called TableColumn.setHeaderRenderer.
If in your TableCellRenderer you create your own JLabels to return, rather than using the one from DefaultTableCellRenderer, make sure you call JLabel.setOpaque( true ) or else your JLabel.setBackground will be ignored.
You will probably also want to call JLabel.setBorder( new EmptyBorder(1, 1, 1, 1) ) to keep the division lines between columns and JLabel.setHorizontalAlignment( SwingConstants.CENTER ) to ensure the labels are centred.
If you call JTable.showGrid( false ), you must also call JTable.setIntercellSpacing( new Dimension(0, 0) ); to butt the cells right up against each other.
An alternative approach is to override the prepareRenderer method.
See the table of error messages, now a separate document.
| 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 | junco@premier1.net |
| 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 |
| Chad Loder | cloder@acm.org |
| Savas Alparslan | alparsla@hotmail.com |
| Simon Gibbs | mail@simongibbs.co.uk |
| Laurence Vanhelsuwe | lv2@clara.co.uk |
| Norman Paterson | norman@dcs.st-and.ac.uk |
| Jonathan Finn | jfinn@sibelius.com |
home |
Canadian Mind Products | |||
| mindprod.com IP:[24.87.56.253] | ||||
| Your IP:[80.134.30.163] | ||||
| You are visitor number 187261. | ||||
| Please send errors, omissions and suggestions | ||||
| to improve this page to Roedy Green. | ||||
| You can get a fresh copy of this page from: | or possibly from your local J: drive mirror: | |||
| http://mindprod.com/jgloss/gotchas.html | J:\mindprod\jgloss\gotchas.html | |||