3

静的初期化ブロックを持つレガシー (変更不可 :-() コードがあります。サブクラスを作成したいのですが、静的ブロックを実行していません。

これを行う方法はありますか?例: class.ForNameメソッドには、クラスの初期化を担当する 2 番目の引数があります。 6/docs/api/java/lang/Class.html#forName(java.lang.String , boolean, java.lang.ClassLoader)

更新:上記のclass.forNameは初期化をトリガーしません-しかし、newInstanceを要求すると:-(

ありがとう、クリシー

4

3 に答える 3

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

}

出力:
null

于 2013-01-23T10:12:03.473 に答える
2

静的初期化ブロックを持つレガシー (変更不可 :-() コードがあります。サブクラスを作成したいのですが、静的ブロックを実行していません。

サブクラスを作成することはできますが、そのサブクラスを初期化することはできません。つまり、おそらく役に立たないということです。

JLS (クラスの初期化)のセクション 12.4.2から:

次に、C がインターフェイスではなくクラスであり、そのスーパークラス SC がまだ初期化されていない場合は、SC に対してこの手順全体を再帰的に実行します。

基本的に、デザインを再考する必要があると思います。理想的には、実行中の静的初期化子に対処できるようにします。

于 2013-01-23T09:38:06.597 に答える
0

次のことができます。

  • クラスを逆コンパイルします (JAD などを使用)
  • 静的初期化コードを削除します
  • 新しいクラスをコンパイルする
  • 新しいクラスを古いコードの jar にコピーします (古いコードを削除します)。
  • サブクラスを作る

美しくないことはわかっていますが、他に選択肢はほとんどありません。静的初期化子は、クラスがロードされた直後に実行され、スキップする可能性はありません。

于 2013-01-23T10:23:32.320 に答える