A couple of quick/short Java tips:
- The
Collections.unmodifiable*
just wrap the original object in a facade class which throwsUnsupportedOperationException
when you invoke operations that would modify it (likeadd
orremove
), but the original collection still remains mutable (and the mutations are reflected in facade object). This might be obvious to some, but not to others. The example below also utilizes the “double brace initialization”.
@SuppressWarnings("serial") private static void testROSet() { Setrw = new HashSet () {{ add("foo"); add("bar"); add("baz"); }}; Set ro = Collections.unmodifiableSet(rw); System.out.println(ro); try { ro.add("barfoo"); } catch (UnsupportedOperationException e) { System.err.println("Exception!"); } rw.add("barfoo"); System.out.println(ro); }
- The File class refers to file/path names, not actual files/paths. From the documentation (emphasis added):
An abstract representation of file and directory pathnames.
User interfaces and operating systems use system-dependent pathname strings to name files and directories. This class presents an abstract, system-independent view of hierarchical pathnames.
again, this might be obvious to some, but I feel that the class is a little misleadingly named. It would be more appropriate to name it "PathName" for example (of course there is almost no chance that such change will be made, for compatibility reasons).
- Logging with varargs: unfortunately in the current version of log4j (1.2) there is no support for varargs (possibly because it was released before Java 5). Fortunately it is quite easy to roll your own, using a facade class like the following:
class VarargFaccade extends Logger { Logger log; public VarargFaccade(Logger log) { super(log.getName()); this.log = log; } public void infoVA(String message, Object... arguments) { if (!log.isInfoEnabled()) return; log.info(String.format(message, arguments)); } public void debugVA(String message, Object... arguments) { if (!log.isDebugEnabled()) return; log.debug(String.format(message, arguments)); } ... }
It uses the recommended way to check whether it should log, and if so, it passes the message trough String.format. As an alternative to String.format you could use MessageFormat.format (which also has support for replacing error messages from resource bundles). Here are also some quick benchmark numbers using the NullAppender. These are 2 000 000 iterations for each message, so even the worst performer (MessageFormat) only adds an overhead of 0.0000135s per message (!) – meaning that it shouldn’t make you think that logging is a bottleneck in your application, unless you’ve profiled your application and you are 100% convinced that it is.
Simple string message: 0.58 Concated message: 1.74 Formatted message: 13.06 Passed in with varargs: 12.97 Passed in with vargargs/MF: 26.78 Passed in with varargs, no logging: 0.09
- Finally, there is a discussion about Hidden features of Java on Stackoverflow, which I found very useful. One of my favorite features was VisualVM, a monitoring tool which you can attack to a running JVM on-the-fly to gather some stats about it. Below you can see a short video about it:
Picture taken from june29’s photostream with permission.