如何避免运行静态初始化程序?

时间:2013-01-23 09:35:07

标签: java static-methods

我有一个遗留(不可修改的:-()代码,它有一个静态初始化程序块。 我想创建一个子类 - 但不运行静态块。

有没有办法做到这一点?例如: class.ForName 方法有一个第二个参数,它负责初始化类 - 但遗憾的是,它对我不起作用(可能意味着别的......):http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName(java.lang.String ,boolean,java.lang.ClassLoader)

更新:上面提到的 class.forName 没有触发初始化 - 但是要求 newInstance 会: - (

谢谢, 克里西

3 个答案:

答案 0 :(得分:2)

  

我有一个遗留(不可修改的:-()代码,它有一个静态初始化块。我想创建一个子类 - 但是没有运行静态块。

你可以创建一个子类 - 但你永远不能初始化那个子类,这意味着它可能没用。

section 12.4.2 of the JLS(类初始化):

  

接下来,如果C是类而不是接口,并且其超类SC尚未初始化,则递归执行SC的整个过程。

基本上我认为你需要重新考虑设计。理想情况下,您可以应对静态初始化程序的运行。

答案 1 :(得分:2)

您始终可以使用ASM修补旧课程。通过忽略clinit块从旧字节码生成新类应该很容易。

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);
    }
}

以下是以下课程的前后:

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);
    }
}

之前:

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

}

输出:
测试静电
测试值

后:

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

}

输出:

答案 2 :(得分:0)

您可以执行以下操作:

  • 反编译课程(例如使用JAD)
  • 删除静态初始化代码
  • 编译新班级
  • 将新类复制到旧代码的jar中(删除旧代码)
  • 制作您的子类

我知道它不漂亮,但你几乎没有其他选择。静态初始化程序将在加载类之后立即运行,并且不可能跳过它。