try { if (choice) { while (true) ; } else { System.exit(1); } } finally { code.to.cleanup(); } |
public class C { public boolean equals(C that) { return id(this) == id(that); } } |
But in order for table.get(c) to work you need to make the equals method take an Object as the argument, not a C:
public class C { public boolean equals(Object that) { return (that instanceof C) && id(this) == id((C)that); } } |
Why? The code for Hashtable.get looks something like this:
public class Hashtable { public Object get(Object key) { Object entry; ... if (entry.equals(key)) ... } } |
Now the method invoked by entry.equals(key) depends upon the actual run-time type of the object referenced by entry, and the declared, compile-time type of the variable key. So when you as a user call table.get(new C(...)), this looks in class C for the equals method with argument of type Object. If you happen to have defined an equals method with argument of type C, that's irrelevent. It ignores that method, and looks for a method with signature equals(Object), eventually finding Object.equals(Object). If you want to over-ride a method, you need to match argument types exactly. In some cases, you may want to have two methods, so that you don't pay the overhead of casting when you know you have an object of the right class:
public class C { public boolean equals(Object that) { return (this == that) || ((that instanceof C) && this.equals((C)that)); } public boolean equals(C that) { return id(this) == id(that); // Or whatever is appropriate for class C } } |
public class C2 extends C { int newField = 0; public boolean equals(Object that) { if (this == that) return true; else if (!(that instanceof C2)) return false; else return this.newField == ((C2)that).newField && super.equals(that); } } |
To allow this to work, you have to be careful about how you treat classes in your definition of C.equals. For example, check for that instanceof C rather than that.getClass() == C.class. See the previous IAQ question to learn why. Use this.getClass() == that.getClass() if you are sure that two objects must be of the same class to be considered equals.
public class LinkedList { Object contents; LinkedList next = null; public boolean equals(Object that) { return (this == that) || ((that instanceof LinkedList) && this.equals((LinkedList)that)); } public boolean equals(LinkedList that) { // Buggy! return Util.equals(this.contents, that.contents) && Util.equals(this.next, that.next); } } |
Here I have assumed there is a Util class with:
public static boolean equals(Object x, Object y) { return (x == y) || (x != null && x.equals(y)); } |
I wish this method were in Object; without it you always have to throw in tests against null. Anyway, the LinkedList.equals method will never return if asked to compare two LinkedLists with circular references in them (a pointer from one element of the linked list back to another element). See the description of the Common Lisp function list-length for an explanation of how to handle this problem in linear time with only two words of extra storge. (I don't give the answer here in case you want to try to figure it out for yourself first.)
/** A version of Hashtable that lets you do * table.put("dog", "canine");, and then have * table.get("dogs") return "canine". **/ public class HashtableWithPlurals extends Hashtable { /** Make the table map both key and key + "s" to value. **/ public Object put(Object key, Object value) { super.put(key + "s", value); return super.put(key, value); } } |
You need to be careful when passing to super that you fully understand what the super method does. In this case, the contract for Hashtable.put is that it will record a mapping between the key and the value in the table. However, if the hashtable gets too full, then Hashtable.put will allocate a larger array for the table, copy all the old objects over, and then recursively re-call table.put(key, value). Now, because Java resolves methods based on the runtime type of the target, in our example this recursive call within the code for Hashtable will go to HashtableWithPlurals.put(key, value), and the net result is that occasionally (when the size of the table overflows at just the wrong time), you will get an entry for "dogss" as well as for "dogs" and "dog". Now, does it state anywhere in the documentation for put that doing this recursive call is a possibility? No. In cases like this, it sure helps to have source code access to the JDK.
Properties defaults = new Properties(); defaults.put("color", "black"); Properties props = new Properties(defaults); System.out.println(props.get("color") + ", " + props.getProperty(color)); // This prints "null, black" |
Is this justified by the documentation? Maybe. The documentation in Hashtable talks about entries in the table, and the behavior of Properties is consistent if you assume that defauls are not entries in the table. If for some reason you thought defaults were entries (as you might be led to believe by the behavior of getProperty) then you will be confused.
For the HashtableWithPlurals example, delegation would give you this (note: as of JDK 1.2, Dictionary is considered obsolete; use Map instead):
/** A version of Hashtable that lets you do * table.put("dog", "canine");, and then have * table.get("dogs") return "canine". **/ public class HashtableWithPlurals extends Dictionary { Hashtable table = new Hashtable(); /** Make the table map both key and key + "s" to value. **/ public Object put(Object key, Object value) { table.put(key + "s", value); return table.put(key, value); } ... // Need to implement other methods as well } |
The Properties example, if you wanted to enforce the interpretation that default values are entries, would be better done with delegation. Why was it done with inheritance, then? Because the Java implementation team was rushed, and took the course that required writing less code.
If you really want only one copy per each time a user invokes Java by starting up a Java virtual machine, then you probably want a static instance variable. For example, you have a MainWindow class in your application, and you want to count the number of windows that the user has opened, and initiate the "Really quit?" dialog when the user has closed the last one. For that, you want: |
// One variable per class (per JVM) public Class MainWindow { static int numWindows = 0; ... // when opening: MainWindow.numWindows++; // when closing: MainWindow.numWindows--; } |
In many cases, you really want a class instance variable. For example, suppose you wrote a web browser and wanted to have the history list as a global variable. In Java, it would make more sense to have the history list be an instance variable in the Browser class. Then a user could run two copies of the browser at once, in the same JVM, without having them step on each other. |
// One variable per instance public class Browser { HistoryList history = new HistoryList(); ... // Make entries in this.history } |
Now suppose that you have completed the design and most of the implementation of your browser, and you discover that, deep down in the details of, say, the Cookie class, inside the Http class, you want to display an error message. But you don't know where to display the message. You could easily add an instance variable to the Browser class to hold the display stream or frame, but you haven't passed the current instance of the browser down into the methods in the Cookie class. You don't want to change the signatures of many methods to pass the browser along. You can't use a static variable, because there might be multiple browsers running. However, if you can guarantee that there will be only one browser running per thread (even if each browser may have multiple threads) then there is a good solution: store a table of thread-to-browser mappings as a static variable in the Browser class, and look up the right browser (and hence display) to use via the current thread: |
// One "variable" per thread public class Browser { static Hashtable browsers = new Hashtable(); public Browser() { // Constructor browsers.put(Thread.currentThread(), this); } ... public void reportError(String message) { Thread t = Thread.currentThread(); ((Browser)Browser.browsers.get(t)) .show(message) } } |
Finally, suppose you want the value of a global variable to persist between invocations of the JVM, or to be shared among multiple JVMs in a network of machines. Then you probably should use a database which you access through JDBC, or you should serialize data and write it to a file. |
Here are some of the options that could be used before Java 1.5:
If you only want a few methods, you can put in calls to them within your own class: |
public static double sin(double x) { return Math.sin(x); } public static double cos(double x) { return Math.cos(x); } ... sin(x) |
Static methods take a target (thing to the left of the dot) that is either a class name, or is an object whose value is ignored, but must be declared to be of the right class. So you could save three characters per call by doing: |
// Can't instantiate Math, so it must be null. Math m = null; ... m.sin(x) |
java.lang.Math is a final class, so you can't inherit from it, but if you have your own set of static methods that you would like to share among many of your own classes, then you can package them up and inherit them: |
public abstract class MyStaticMethods { public static double mysin(double x) { ... } } public class MyClass1 extends MyStaticMethods { ... mysin(x) } |
Peter van der Linden, author of Just Java, recommends against both of the last two practices in his FAQ. I agree with him that Math m = null is a bad idea in most cases, but I'm not convinced that the MyStaticMethods demonstrates "very poor OOP style to use inheritance to obtain a trivial name abbreviation (rather than to express a type hierarchy)." First of all, trivial is in the eye of the beholder; the abbreviation may be substantial. (See an example of how I used this approach to what I thought was good effect.) Second, it is rather presumptuous to say that this is very bad OOP style. You could make a case that it is bad Java style, but in languages with multiple inheritance, this idiom would be more acceptable.
Another way of looking at it is that features of Java (and any language) necessarily involve trade-offs, and conflate many issues. I agree it is bad to use inheritance in such a way that you mislead the user into thinking that MyClass1 is inheriting behavior from MyStaticMethods, and it is bad to prohibit MyClass1 from extending whatever other class it really wants to extend. But in Java the class is also the unit of encapsulation, compilation (mostly), and name scope. The MyStaticMethod approach scores negative points on the type hierarchy front, but positive points on the name scope front. If you say that the type hierarchy view is more important, I won't argue with you. But I will argue if you think of a class as doing only one thing, rather than many things at once, and if you think of style guides as absolute rather than as trade-offs.
// null means not applicable // There is no empty tree. class Node { Object data; Node left, right; void print() { if (left != null) left.print(); System.out.println(data); if (right != null) right.print(); } } | // null means empty tree // Note static, non-static methods class Node { Object data; Node left, right; void static print(Node node) { if (node != null) node.print(); } void print() { print(left); System.out.println(data); print(right); } } | // Separate class for Empty // null is never used interface Node { void print(); } class DataNode implements Node{ Object data; Node left, right; void print() { left.print(); System.out.println(data); right.print(); } } class EmptyNode implements Node { void print() { } } |
static Runtime runtime = Runtime.getRuntime(); ... long start, end; Object obj; runtime.gc(); start = runtime.freememory(); obj = new Object(); // Or whatever you want to look at end = runtime.freememory(); System.out.println("That took " + (start-end) + " bytes."); |
This method is not foolproof, because a garbage collection could occur in the middle of the code you are instrumenting, throwing off the byte count. Also, if you are using a just-in-time compiler, some bytes may come from generating code.
You might be surprised to find that an Object takes 16 bytes, or 4 words, in the Sun JDK VM. This breaks down as follows: There is a two-word header, where one word is a pointer to the object's class, and the other points to the instance variables. Even though Object has no instance variables, Java still allocates one word for the variables. Finally, there is a "handle", which is another pointer to the two-word header. Sun says that this extra level of indirection makes garbage collection simpler. (There have been high performance Lisp and Smalltalk garbage collectors that do not use the extra level for at least 15 years. I have heard but have not confirmed that the Microsoft JVM does not have the extra level of indirection.)
An empty new String() takes 40 bytes, or 10 words: 3 words of pointer overhead, 3 words for the instance variables (the start index, end index, and character array), and 4 words for the empty char array. Creating a substring of an existing string takes "only" 6 words, because the char array is shared. Putting an Integer key and Integer value into a Hashtable takes 64 bytes (in addition to the four bytes that were pre-allocated in the Hashtable array): I'll let you work out why.
In an instance variable initializer for a class (or a superclass). |
| |
In a constructor for a class (or a superclass). |
| |
In an object initializer block. This is new in Java 1.1; its just like a static initializer block but without the keyword static. |
|
The order of evaluation (ignoring out of memory problems) when you say new C() is:
Here's another example:
Program:
|
Output:
|
Active use is defined as the first time you do any one of the following:
Here is an example:
Program:
|
Output:
|
public class C { int a,b,c,d,e,f; } |
Here are some things you can do for constructors:
public C setA(int val) { a = val; return this; } ... new C().setA(1).setC(3).setE(5); |
Pro: This is a reasonably simple and efficient approach. A similar idea is discussed by Bjarne Stroustrop on page 156 of The Design and Evolution of C++. Con: You need to write all the little setters, they aren't JavaBean-compliant (since they return this, not void), they don't work if there are interactions between two values.
new C() {{ a = 1; c = 3; e = 5; }} |
Pro: Very concise; no mess with setters. Con: The instance variables can't be private, you have the overhead of a sub-class, your object won't actually have C as its class (although it will still be an instanceof C), it only works if you have accessible instance variables, and many people, including experienced Java programmers, won't understand it. Actually, its quite simple: You are defining a new, unnamed (anonymous) subclass of C, with no new methods or variables, but with an initialization block that initializes a, c, and e. Along with defining this class, you are also making an instance. When I showed this to Guy Steele, he said "heh, heh! That's pretty cute, all right, but I'm not sure I would advocate widespread use..." As usual, Guy is right. (By the way, you can also use this to create and initialize a vector. You know how great it is to create and initialize, say, a String array with new String[] {"one", "two", "three"}. Now with inner classes you can do the same thing for a vector, where previously you thought you'd have to use assignement statements: new Vector(3) {{add("one"); add("two"); add("three")}}.)
class C { public: C(int a=1, int b=2, int c=3, int d=4, int e=5); } ... new C(10); // Construct an instance with defaults for b,c,d,e |
Common Lisp and Python have keyword arguments as well as optional arguments, so you can do this:
C(a=10, c=30, e=50) # Construct an instance; use defaults for b and d. |
public Number numberFactory(String str) throws NumberFormatException { try { long l = Long.parseLong(str); if (l >= 0 && l < cachedLongs.length) { int i = (int)l; if (cachedLongs[i] != null) return cachedLongs[i]; else return cachedLongs[i] = new Long(str); } else { return new Long(l); } } catch (NumberFormatException e) { double d = Double.parseDouble(str); return d == 0.0 ? ZERO : d == 1.0 ? ONE : new Double(d); } } private Long[] cachedLongs = new Long[100]; private Double ZERO = new Double(0.0); private Double ONE = new Double(1.0); |
public class PointPool { /** Allocate a pool of n Points. **/ public PointPool(int n) { x = new double[n]; y = new double[n]; z = new double[n]; next = 0; } public double x[], y[], z[]; /** Initialize the next point, represented as in integer index. **/ int point(double x1, double y1, double z1) { x[next] = x1; y[next] = y1; z[next] = z1; return next++; } /** Initialize the next point, initilized to zeros. **/ int point() { return point(0.0, 0.0, 0.0); } /** Initialize the next point as a copy of a point in some pool. **/ int point(PointPool pool, int p) { return point(pool.x[p], pool.y[p], pool.z[p]); } public int next; } |
PointPool pool = new PointPool(1000000); PointPool results = new PointPool(100); ... int pos = pool.next; doComplexCalculation(...); pool.next = pos; ... void doComplexCalculation(...) { ... int p1 = pool.point(x, y, z); int p2 = pool.point(p, q, r); double diff = pool.x[p1] - pool.x[p2]; ... int p_final = results.point(pool,p1); ... } |
Allocating a million points took half a second for the PointPool approach, and 6 seconds for the straightforward approach that allocates a million instances of a Point class, so that's a 12-fold speedup.
Wouldn't it be nice if you could declare p1, p2 and p_final as Point rather than int? In C or C++, you could just do typedef int Point, but Java doesn't allow that. If you're adventurous, you can set up make files to run your files through the C preprocessor before the Java compiler, and then you can do #define Point int.
for(;;) { ... String str = ... match(str, compile("a*b*c*")); ... } |
Since Java has no macros, and little control over time of execution, your choices are limited here. One possibility, although not very pretty, is to use an inner interface with a variable initializer:
for(;;) { ... String str = ... interface P1 {FSA f = compile("a*b*c*);} match(str, P1.f); ... } |
The value for P1.f gets initialized on the first use of
P1, and is not changed, since variables in interfaces are
implicitly static final.
Bad. Most Java books are written by people who couldn't get a
job as a Java programmer (since programming almost always pays more
than book writing; I know because I've done both). These books are
full of errors, bad advice, and bad programs. These books are
dangerous to the beginner, but are easily recognized and rejected by a
programmer with even a little experience in another language.
Excellent.
There are a small number of excellent Java books. I like the official
specification and the books by Arnold and
Gosling, Marty Hall,
and Peter van
der Linden.
For reference I like the Java in a Nutshell series and
the online references at Sun (I copy
the javadoc APIs and
the language
specification and its amendments to my local disk and bookmark them in my browser so I'll always have fast access.)
Iffy. In between these two extremes is a collection of sloppy
writing by people who should know better, but either haven't taken the
time to really understand how Java works, or are just rushing to get
something published fast. One such example of half-truths is Edward Yourdon's Java
and the new Internet programming paradigm from Rise and
Resurrection of the American Programmer [footnote on Yourdon]. Here's what Yourdon says
about how different Java is:
(loop
...
(match str #.(compile "a*b*c*"))
...)
Q: What other operations are
surprisingly slow?
Where do I begin? Here are a few that are most useful to know
about. I wrote a timing utility that runs snippets of code in a loop,
reporting the results in terms of thousands of iterations per second
(K/sec) and microseconds per iteration (uSecs). Timing was done on a
Sparc 20 with the JDK 1.1.4 JIT compiler. I note the following:
K/sec uSecs Code Operation
========= ======= ==================== ===========
147,058 0.007 a = a & 0x100; get element of int bits
314 3.180 bitset.get(3); get element of Bitset
20,000 0.050 obj = objs[1]; get element of Array
5,263 0.190 str.charAt(5); get element of String
361 2.770 buf.charAt(5); get element of StringBuffer
337 2.960 objs2.elementAt(1); get element of Vector
241 4.140 hash.get("a"); get element of Hashtable
336 2.970 bitset.set(3); set element of Bitset
5,555 0.180 objs[1] = obj; set element of Array
355 2.810 buf.setCharAt(5,' ') set element of StringBuffer
308 3.240 objs2.setElementAt(1 set element of Vector
237 4.210 hash.put("a", obj); set element of Hashtable
Q: Can I get good advice from
books on Java?
There are a lot of Java books out there, falling into three classes:
int[] a = {0, 1, 2};
int[] b = a;
b[0] = 99;
then a[0] is 99 because a and b are
pointers (or references) to
the same object.
Translations:
Polish translation provided by Marina Stepanenko.
Peter Norvig