Thursday, February 21, 2013

Few notes about Java clone() method

Did you know you can create new Java instance without constructor? That's right, and this feature is brought to you by the Java clone() method. I personally don't like to use it much because it has many pitfalls, and it feels very broken in many ways. You probably can write better "clone" by using copy constructor or a static copy factory methods.

Anyrate, here is a unit test that highlights few notes I have gathered about Java clone. Look for the "Note" comments below.

package atest;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class CloneTest {
    @Test
    public void testFooClone() throws Exception {
        Foo a = new Foo();
        Foo b = a.clone();

        assertThat(a != b, is(true));
        assertThat(a.constructorCallsCount, is(1));
        assertThat(a.name, is("foo"));
        assertThat(b.constructorCallsCount, is(1)); // Note1: Wow, a new instance without calling constructor!
        assertThat(b.name, is("foo"));

        a.name = "fooX";
        assertThat(a.name, is("fooX"));
        assertThat(b.name, is("foo"));

        Foo2 c = new Foo2();
        c.ids.add("a");
        Foo2 d = c.clone();
        d.ids.add("b");

        c.ids.clear();
        c.ids.add("A");

        assertThat(c != d, is(true));
        assertThat(c.constructorCallsCount, is(2));
        assertThat(c.name, is("foo"));
        assertThat(d.constructorCallsCount, is(2));
        assertThat(d.name, is("foo"));

        assertThat(c.ids, hasItems("A"));
        assertThat(c.ids.size(), is(1));
        assertThat(d.ids, hasItems("a", "b"));
        assertThat(d.ids.size(), is(2));
    }

    /** A simple clone examples with simple type fields. */
    public static class Foo implements Cloneable {
        static int constructorCallsCount = 0;
        String name = "foo";
        public Foo() {
            constructorCallsCount++;
        }
        public Foo clone() {
            // Note2: Catch checked exception here so client or subclass doesn't need to.
            Foo result = null;
            try {
                result = (Foo)super.clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException("Unable to clone.", e);
            }
            return result;
        }
    }

    /** A clone example that needs to fix field afterward super.clone(). */
    public static class Foo2 extends Foo {
        List<String> ids = new ArrayList();
        public Foo2 clone() {
            Foo2 result = (Foo2)super.clone(); // Note3: Some how, super clone will auto return the correct type!
            result.ids = new ArrayList(ids);   // Note4: need to fix non-simple field.
            return result;
        }
    }
}

Monday, February 4, 2013

Learn how to grep by context will give you better result

I learned that grep command supports few options called "context" searching. Basically it can print lines around the matching string! For example, you can use this feature to perform a quick Java threads stack analysis like this (assuming you have a java process PID=12345).


bash> jstack 12345 | grep --color 'State: BLOCKED' -B 1
"A-Bad-Boy-Thread" prio=5 tid=0x00007fcfbc895800 nid=0xcd03 waiting on condition [0x00000001657cb000]
   java.lang.Thread.State: BLOCKED (on object monitor)

See, without the "-B 1" option, which show one line before match, you won't know which name of the tread that's acting up! Now isn't that a gem!

You may also use "-A 5" to see 5 more lines after the match. Or you may use the "-C 5" to see 5 lines before and after the match.

PS: There is nothing wrong with a "BLOCKED" thread in Java, however if you got one that won't go away, then it's a good indicator that something is fishy about that thread in your application; because in a well designed app, threads should be in BLOCKED state as briefly as possible. Read the javadoc on java.lang.Thread.State for more details.