Java Tips & Tricks
CONTENTS
Scaling an image
Determining the method file name
Building CLASSPATH in Windows
Capturing an exception's stack trace
Merging hash tables
Formatting dates
Parsing dates
Handling time zones
File output
File input
Writing to a string
Getting system properties
Getting an object's class name
Using reflection to call methods
Sockets
JDBC Drivers
JDBC - Multiple Result Sets
MessageFormat
The equals() method
Cloning
Comparable
Using standard Exceptions
Collections

Scaling an image

javaio provides lots of useful image manipulation functionality, but it's hard to find decent documentation. Here's how to scale an image, at least:
Image image = ...; BufferedImage scaled = new BufferedImage((int) (w * scale), (int) (h * scale), image.getType()); AffineTransform at = AffineTransform.getScaleInstance(scale, scale); Graphics2D g2d = (Graphics2D) scaled.getGraphics(); g2d.drawImage(image, new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR), 0, 0); image = scaled;
to top

Determining the method file name

This is useful in log messages to output the name of the current method. Note that it only works in Java 5.
String getCurrentMethodName() { return Thread.currentThread().getStackTrace()[3].getMethodName(); } int myMethod() { System.err.println( getCurrentMethodName() + ": message"); // prints "myMethod: message" }
to top

Building CLASSPATH in Windows

If all the jars for your application are in a single directory, then the following technique using batch files can be used to dynamically build the CLASSPATH. This beats typing them out by hand!
Suppose that the jars in the "lib" directory, and that you have a batch file that launches your application from the sister "bin" directory. Create one batch file, called "addjar.bat", with contents:
@set CLASSPATH=%CLASSPATH%;%1
and a second file, called "setcp.bat", containing:
@set CLASSPATH= @FOR %%f in (..\lib\*.*) do call addjar %%f
Put both these batch files in the bin directory, and call setcp from your launch script before starting the application. The CLASSPATH variable will be set up by setcp, and does not need to be set on the line where the JVM is started.
to top

Capturing an exception's stack trace

import java.io.*; Exception e = ...; java.io.StringWriter sw = new java.io.StringWriter(); e.printStackTrace(new java.io.PrintWriter(sw)); String trace = sw.getBuffer().toString();
to top

Merging hash tables

import java.util.*; Map m1 = ...; Map m2 = ...; m2.putAll(m1); // adds all elements of m1 to m2
to top

Formatting dates

import java.io.*; import java.util.*; import java.text.*; SimpleDateFormat formatter = new SimpleDateFormat ("yyyy.MM.dd H:mm"); Date now = new Date(); System.out.println(formatter.format(now)); // 2001.3.19 13:21 formatter.applyPattern("dd MMM yyyy"); System.out.println(formatter.format(now)); // 19 Mar 2001
The usable codes are:
pattern meaning example
d day in month 4
dd day in month 04
D day in year 216
F day of week in month 2 (e.g. 2nd Wednesday)
E day in week Tues
EEEE day in week Tuesday
w week in year 27
W week in month 2
MM month 04
MMM month Jul
MMMM month July
yy year 01
yyyy year 2001
G era AD
K hour 0-11 10
hh hour 0-23 19
k hour in day 1-24 24
H hour in day 0-23 0
m minute in hour 59
s second in minute 59
S millisecond 879
a AM/PM PM
z timezone PST
zzzz timezone Pacific Standard Time
to top

Parsing dates

import java.io.*; import java.util.*; import java.text.*; String s = "2001/09/23 14:39"; SimpleDateFormat formatter = new SimpleDateFormat ("yyyy/MM/dd H:mm"); Date d = formatter.parse(s, new ParsePosition(0));
to top

Handling time zones

The TimeZone class provides all of the necessary functionality here, and even handles daylight savings time. The general idea is to determine the appropriate time zone id from the list supplied by the static method:
String[] tzids = TimeZone.getAvailableIDs(); This list includes the standard timezones (e.g. "EST", "PST"), and also includes specialized ids for regions where the daylight savings time rules are unusual in some respect (e.g. "America/Montreal").
Once the time zone id is used, the following methods can be used to convert between a Date object (which is always relative to UTC (similar to GMT - Greenwich Mean Time)), and a date/time string epxressed relative to a particular location, taking into account the offset and the daylight saving time rules for that location:
public static String GMTDateToLocalString(Date d, String tzid) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm"); sdf.setTimeZone(TimeZone.getTimeZone(tzid)); return sdf.format(d); } public static Date LocalStringToGMTDate(String s, String tzid) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm"); sdf.setTimeZone(TimeZone.getTimeZone(tzid)); try { return sdf.parse(s); } catch (Exception e) { return null; } }
These methods can be stuck in a utility class somewhere. Note: resist the temptation to re-use the SimpleDateFormat object - I found that that technique doesn't work too well, though I may just be stupid.
The code shown is pretty simple; you might like to improve on it by using locales, or by implementing a better error handling technique when parsing a date/time string.
to top

File output

import java.io.*; String filename = "/tmp/xxx.html"; try { PrintWriter out = new PrintWriter(new FileWriter(filename)); out.print(new Date()); out.println(new Date()); out.println(5); out.println(5L); out.close(); } catch (IOException e) { // handle the exception }
to top

File input

import java.io.*; String filename = "/tmp/xxx.html"; try { BufferedReader in = new BufferedReader(new FileReader(filename)); String line; while ((line = in.readLine()) != null) { // use the line } in.close(); } catch (IOException e) { // handle the exception }
to top

Writing to a string

import java.io.*; StringWriter out = new StringWriter(); out.write("A string"); out.write('c'); String s = out.toString(); // out.close() does nothing
to top

Getting system properties

Many system properties are available by default:
Other system properties may be created via command line arguments, as in:
java -Dprop=val class
Example:
String prop = "myproperty"; String default = "0"; String val = System.getProperty(prop); String val = System.getProperty(prop, default); String sep = System.getProperty("line.separator");
to top

Getting an object's class name

Object o = ...; String className = o.getClass().getName(); also note... Class c1 = String.class; Class c2 = java.util.Vector.class; Class c3 = int.class; Class c4 = int[].class; Class c5 = int[][].class; Class c6 = String[].class; and... Class c7 = Class.forName("java.lang.String");
to top

Using reflection to call methods

import java.lang.reflect.*; Class c = ...; // see above Method[] methods = c.getMethods(); Method m = methods[i]; String s = m.getName(); Class r = m.getReturnType(); // returns void.class if void Class[] p = m.getParameterTypes(); // to invoke a method, use: Object invoke(Object o, Object[] args);
to top

Sockets

This ridiculous example implements a server which returns the double of a double. Here's the client code:
import java.io.*; import java.net.*; public class Client { public static void main(String[] args) throws IOException { int n = Integer.parseInt(args[0]); Socket sock = null; PrintWriter out = null; BufferedReader in = null; try { sock = new Socket("localhost", 4444); out = new PrintWriter(sock.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(sock.getInputStream())); } catch (UnknownHostException e) { System.err.println("Don't know about host: localhost."); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for the connection to: localhost."); System.exit(1); } for (double d = 0; d < n; d++) { out.println(d); String s = in.readLine(); } out.close(); in.close(); sock.close(); } }
and here's the server:
import java.net.*; import java.io.*; import java.math.*; public class Server { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(4444); } catch (IOException e) { System.err.println("Could not listen on port: 4444."); System.exit(1); } Socket clientSocket = null; try { clientSocket = serverSocket.accept(); } catch (IOException e) { System.err.println("Accept failed."); System.exit(1); } PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String line; while ((line = in.readLine()) != null) { try { double d = Double.parseDouble(line); out.println(d * d); } catch (NumberFormatException e) { out.println("Error on input: " + line); } } out.close(); in.close(); clientSocket.close(); serverSocket.close(); } }
to top

JDBC Drivers

Personally, I can never remember the class names for drivers or the formats for the URLs, so just for the sake of reference, here's a few that I've used:
Oracle oracle.jdbc.driver.OracleDriver
jdbc:oracle:thin:@server:1521:dbinstance
MySQL org.gjt.mm.mysql.Driver
jdbc:mysql://localhost/database?user=user&password=password
Interbase interbase.interclient.Driver
jdbc:interbase://localhost/c:/dir/dir/dir/file.gdb
Postgres org.postgresql.Driver
jdbc:postgresql://server[:port]/database[?user=user&password=password]
Sybase com.sybase.jdbc.SybDriver or
com.sybase.jdbc3.jdbc.SybDriver jdbc:sybase:[protocol]:[server]:[port]/[database]
to top

JDBC - Multiple Result Sets

It's a bit tricking handling multiple result sets... you have to request the result sets and the status values in the correct sequence.
import java.sql.*; import java.text.*; import java.util.*; public class Demo { private static String DRIVER = "driver class"; private static String URL = "db url"; private static String USER = "user"; private static String PASSWORD = "password"; public static void main(String[] args) { try { test(); } catch (SQLException e) { System.out.println(e.getMessage()); } catch (Exception e) { System.out.println(e.getMessage()); } } private static void test() throws Exception { Class.forName(DRIVER); Properties prop = new Properties(); prop.setProperty("user", USER); prop.setProperty("password", PASSWORD); Connection conn = DriverManager.getConnection(URL, prop); CallableStatement cs = conn.prepareCall("{ ? = call stored_procedure }"); cs.registerOutParameter(1, Types.INTEGER); boolean hasMoreResultSets = cs.execute(); int rowsAffected = 0; do { if (hasMoreResultSets) { ResultSet rs = cs.getResultSet(); printResultSet(rs); } else { rowsAffected = cs.getUpdateCount(); } hasMoreResultSets = cs.getMoreResults(); } while (hasMoreResultSets || (rowsAffected != 1)); int retVal = cs.getInt(1); System.out.println("Return value was " + retVal); } private static void printResultSet(ResultSet rs) throws Exception { ResultSetMetaData md = rs.getMetaData(); int n = md.getColumnCount(); for (int i = 0; i < n; i++) { System.out.print(md.getColumnLabel(i+1)); } System.out.println(); while (rs.next()) { for (int i = 0; i < n; i++) { System.out.print(rs.getString(i+1)); } System.out.println(); } } }
to top

MessageFormat

The MessageFormat class can be used quite nicely to compose messages. For example, the following:
import java.text.*; public class Test { public static void main(String[] args) { String message = "The opposite of {0} is {1}."; Object values[] = { "yes", "no" }; String s = MessageFormat.format(message, values); System.out.println(s); } }
prints:
The opposite of yes is no.
Note that {, } or ' characters that are part of the plain text must be quoted, as '{', '}' and '', respectively. For example:
import java.text.*; public class Test { public static void main(String[] args) { String message = "Here''s your choices: '{'{0}, {1}'}'."; Object values[] = { "yes", "no" }; String s = MessageFormat.format(message, values); System.out.println(s); } }
produces:
Here's your choices: {yes, no}.
to top

The equals() method

Should do the following:
public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TheType)) { return false; } TheType that = (TheType) o; if (field1 != that.field2) { // for primitives return false; } if ((field2 != that.field2) && ((field2 == null) || !field2.equals(that.field2))) { // for references return false; } ... return true; }
This version tests for object identity both at the calling level, and for member of reference type.
Also: always override hashCode() when overriding equals().
to top

Cloning

If a class implements Cloneable, then the default implementation of clone() in Object will return an object of the same runtime class, containing a field-by-field copy of the object. No constructor for the class is used.
If one calls a constructor from the clone() method, then subclasses will not be able to use super.clone() to initialize superclass fields. Consequently, one should always use super.clone().
The normal approach, therefore, is to call super.clone(), and then to adjust the contents appropriately. This would involve cloning member fields which are mutable objects, for example. However, this fails if any of the member fields are final!
to top

Comparable

The compareTo() method return a negative, zero or positive value according as the object in question should compare before, equal to or after the argument passed in. It's not necessary to return -1 or 1; any negative or positive value is acceptable.
The compareTo() method should throw a ClassCastException if the argument passed in is not of the appropriate type, and it should throw NullPointerException if the argument passed in is null. Consequently, it's not necessary to test for these conditions!
public class Person implements Comparable { int age; ... public int compareTo(Object o) { return age - ((Person) o).age; } }
to top

Using standard Exceptions

In general, it's best to use exceptions from the standard library to indicate problems in one's own code, if the semantics match up. Here are some exceptions that can commonly be used in this way:
IllegalArgumentException Signals an invalid value for a method parameter
IllegalStateException Signals that the object is not in a valid state when a method is called
NullPointerException Signals that a parameter is null when it is not allowed to be so
IndexOutOfBoundsException An index parameter is out of bounds
to top

Collections

The following are defined in the Collection interface:
boolean containsAll(Collection<?> c) Check if the collection contains all members of another collection
boolean addAll(Collection<? extends E>) Adds all elements of the provided collection
boolean removeAll(Collection<?>) Removes all elements that are in the given collection
boolean retainAll(Collection<?>) Removes all elements which are not in the given collection
void clear() Empties the collection
addAll(), removeAll() and retainAll() return true if the collection was modified.
The following removes all occurrences of a particular element e from a Collection c:
c.removeAll(Collections.singleton(e));
Sets come in three varieties:
HashSet implemented as a hash table, most efficient
TreeSet implemented as a tree, iterator provides elements in sorted order
LinkedHashSet implemented as a hash table, iterator provides elements in order of insertion
The following removes all duplicates from a Collection c:
Collection<T> c1 = new HashSet<T>(c);
For sets, addAll() performs a union, removeAll() performs a difference, retainAll() peforms intersction, and containsAll() checks the subset relationship.
List supports the subList(i, j) method, which gives a view onto the list, using the same backing store. It can be useful, for example, to remove part of a list:
list.subList(from, to).clear(); // clears elements from, from+1, ..., to-1
The Collections class provides various additional fuctionality.
Synchronized wrapper classes:
Collection<E> sc = Collections.synchronizedCollection(Collection<E> c); Set<E> ss = Collections.synchronizedSet(Set<E> s); List<E> sl = Collections.synchronizedList(List<E> l); Map<K, V> sm = Collections.synchronizedMap(Map<K, V> m);
Unmodifiable wrapper classes:
Collection<E> sc = Collections.unmodifiableCollection(Collection<E> c); Set<E> ss = Collections.unmodifiableSet(Set<E> s); List<E> sl = Collections.unmodifiableList(List<E> l); Map<K, V> sm = Collections.unmodifiableMap(Map<K, V> m);
Immutable list containing multiple copies of an element:
List<T> list = new ArrayList<T>(Collections.nCopies(500, t));
Immutable singleton:
Collection<T> c = Collections.singleton(t);
Empty collections:
Set<T> s = Collections.emptySet(); List<T> l = Collections.emptyList(); Map<T> m = Collections.emptyMap();
to top