3

I have a legacy (not modifiable :-() code, that have a static initializer block. I would like to create a subclass - but without running the static block.

Is there any way doing this? For example: class.ForName method has a second argument which is responsibe for initializing the class - but sadly, it doesn't work for me (probably means something else ..): http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName(java.lang.String, boolean, java.lang.ClassLoader)

UPDATE: the above mentioned class.forName does not trigget initialization - but asking for a newInstance does :-(

Thanks, krisy

3 Answers 3

2

You can always patch the old class with ASM. Generating a new class from the old bytecode by ignoring the clinit block should be easy.

import org.objectweb.asm.*;
import org.objectweb.asm.commons.EmptyVisitor;
import java.io.*;

public class ClinitKiller {
    public static void main (String[] args) {
        final InputStream input = ClinitKiller.class.getResourceAsStream(Test.class.getName() + ".class");
        try {
            final byte[] bytes = instrument(input);
            FileOutputStream out = new FileOutputStream("/tmp/Test.class");
            out.write(bytes);
            out.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static byte[] instrument(InputStream is) throws IOException {
        ClassReader reader = new ClassReader(is);
        ClassWriter writer = new ClassWriter(0);
        ClinitKillerClassAdapter classAdapter = new ClinitKillerClassAdapter(writer);
        reader.accept(classAdapter, 0);
        return writer.toByteArray();
    }
}

class ClinitKillerClassAdapter extends ClassAdapter {
    public ClinitKillerClassAdapter(final ClassVisitor cv) {
        super(cv);
    }

    public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
        if (name.equals("<clinit>")) {
            return new EmptyVisitor();
        }
        return cv.visitMethod(access, name, desc, signature, exceptions);
    }
}

Here is the before and after for the following class:

public class Test {
    private static final String value;
    static {
        System.out.println("Test static");
        value = "test value";
    }
    public static void main(String[] args) {
        System.out.println(value);
    }
}

Before:

javap -c Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
    public Test();
    Code:
    0:  aload_0
    1:  invokespecial   #1; //Method java/lang/Object."<init>":()V
    4:  return

    public static void main(java.lang.String[]);
    Code:
    0:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    3:  getstatic   #3; //Field value:Ljava/lang/String;
    6:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    9:  return

    static {};
    Code:
    0:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    3:  ldc #5; //String Test static
    5:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    8:  ldc #6; //String test value
    10: putstatic   #3; //Field value:Ljava/lang/String;
    13: return

}

Output:
Test static
test value

After:

javap -c Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
    public Test();
    Code:
    0:  aload_0
    1:  invokespecial   #11; //Method java/lang/Object."<init>":()V
    4:  return

    public static void main(java.lang.String[]);
    Code:
    0:  getstatic   #21; //Field java/lang/System.out:Ljava/io/PrintStream;
    3:  getstatic   #23; //Field value:Ljava/lang/String;
    6:  invokevirtual   #29; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    9:  return

}

Output:
null

Sign up to request clarification or add additional context in comments.

Comments

2

I have a legacy (not modifiable :-() code, that have a static initializer block. I would like to create a subclass - but without running the static block.

You can create a subclass - but you can never initialize that subclass, which means it's probably useless.

From section 12.4.2 of the JLS (class initialization):

Next, if C is a class rather than an interface, and its superclass SC has not yet been initialized, then recursively perform this entire procedure for SC.

Basically I think you'll need to rethink the design. Ideally so that you can cope with the static initializer running.

2 Comments

Well, I can write static initializers into the subclass to - the real problem, I don't want the static initializer of the parent class to run, if I create a subclass :-) I was thinking of wrapping the parent class into a field of another class (e.g. decorator pattern), but the static initializer runs this way also :-(
@krisy: Yes - basically the initializer is going to run any way that you actually use the class. So either don't use it, or find some way of coping with the initializer running.
0

You can do the following:

  • Decompile the class (e.g. with JAD)
  • Remove the static initializer code
  • Compile the new class
  • Copy the new class into the jar of the old code (remove the old one)
  • Make your subclass

I know it's not beautiful, but you have hardly another choice. The static initializers will run right after the class is loaded, and there is no possibility to skip it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.