Saturday, November 19, 2005

Some nice Java puzzles

I came across these set of interesting java puzzles and for many of them I got trapped. See how do you fare ;) If you have a good explanation, don't hesitate to share it.

  1. public class StringTest
    {
    public static void main(String[] args){
    foo();
    bar();
    }
    public static void foo(){
    String s = "str4";
    String s1 = "str" + s.length();
    System.out.println("(s==s1) = " + (s1==s));
    }
    public static void bar(){
    String s = "str4";
    String s1 = "str" + "4";
    System.out.println("(s==s1) = " + (s1==s));
    }
    }



  2. public class Assignment {
    public static void main(String[] a){
    int count = 0;
    for (int i = 0; i < 3; i++)
    count += count++;
    System.out.println(count);
    }
    }



  3. class StaticTest {
    static { initIfNecessary(); }

    private static int sum;

    public static int getSum() {
    initIfNecessary();
    return sum;
    }

    private static boolean initialized = false;

    private static synchronized void initIfNecessary() {
    if (!initialized) {
    for (int i = 0; i < 100; i++)
    sum += i;
    initialized = true;
    }
    }
    public static void main(String[] args) {
    System.out.println(getSum());
    }
    }



  4. package click;
    public class CodeTalk {
    public void doIt() { printMessage(); }
    void printMessage() { System.out.println("Click"); }
    }
    ------------------
    package hack;
    import click.CodeTalk;
    public class TypeIt {
    private static class ClickIt extends CodeTalk {
    void printMessage() { System.out.println("Hack"); }
    }
    public static void main(String[] args) {
    new ClickIt().doIt();
    }
    }



  5. public class Lazy {
    private static boolean initialized = false;
    static {
    Thread t = new Thread(new Runnable() {
    public void run() {
    initialized = true;
    }
    });
    t. start();
    try {
    t.join();
    } catch (InterruptedException e) {
    throw e.printStackTrace();
    }
    }

    public static void main(String[] args) {
    System.out.println(initialized);
    }
    }


Monday, November 14, 2005

Timestamp bugs..

I hit upon two JDK 1.5 bugs today both related to Timestamp.

  1. Till JDK 1.4.x, Timestamp.compareTo(Date) worked. With JDK1.5, it results into ClasscastException. Since Timestamp extends Date, the caller can always call date1.compareTo(date2) where date1 and date2 can be date or any of its subclass. Now with JDK1.5, if date1 is Timestamp and date2 is Date, it will result into ClassCastException. The code should have always been backward compatible. Try the code below

    Date date1 = new Date ();
    Date date2 = new Timestamp(System.currentTimeMillis());
    int result = date2.compareTo(date1);

  2. In some special cases, two different dates differing only in millisecond values are treated to be equal. You don't believe it?
    Lets take the code sample below, where you create date1 using the value only till second, and compare that with a timestamp date2 that was created with the entire value. Since date1 does not contain millisecond part, timestamp should be greater than date here but date.compareTo() treats them equal.

    long millis = System.currentTimeMillis();
    Date date1 = new Date(millis/1000*1000);
    Timestamp date2 = new Timestamp(millis);
    int i = date1.compareTo(date2);

    This used to work till JDK1.4. It got broken in JDK 1.5 because date now uses getMillis() to compare the time instead of comparing getTime() values. This change must have been made with good intention as getMillis() didn't need any computation and they would have thought that it will become faster. However Timestamp played spilsport here. When you create Timestamp object passing long millisecond value, it breaks it up in two parts - 'millis' in which it stores values till second and 'nanos' in which it stores everything after second. The Timestamp constructior looks like

    public Timestamp(long time) {
    super((time/1000)*1000);
    nanos = (int)((time%1000) * 1000000);
    if (nanos < 0) {
    nanos = 1000000000 + nanos;
    super.setTime(((time/1000)-1)*1000);
    }
    }

    Thus the millis value in this object will be only till second. hence the comparison of the two dates above will make them equal.

The lesson is that don't use compareTo() with dates. Write your own comparator for it in which you compare using date.getTime() :)