2013-01-23 85 views
2

我有一箇舊的(不能修改:-()碼,有一個靜態初始化塊 我想創建一個子類 - 。但沒有運行靜態塊如何避免運行靜態初始化程序?

有沒有辦法這樣做呢?例如:class.ForName方法有第二個參數是初始化類的響應 - 但遺憾的是,它不適用於我(可能意味着別的..):http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName(java.lang.String,布爾,java.lang.ClassLoader)

UPDATE:上面提到的class.forName不會觸發初始化 - 但要求newIn立場做:-(

感謝, krisy

回答

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 

} 

輸出:

+0

我投票,但我認爲你應該提供示例 – CAMOBAP

+0

會試試看,謝謝! – krisy

2

我有一箇舊的(不能修改:-()碼,有一個靜態初始化塊。我想創建一個子類 - 但不運行靜態塊。

您可以創建一個子類 - 但是您永遠無法初始化該子類,這意味着它可能無用。

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

接下來,如果C是一個類而不是一個接口,並且它的超類SC還沒有被初始化,然後遞歸用於SC執行此整個過程。

基本上我認爲你需要重新考慮設計。理想情況下,你可以應付靜態初始化器的運行。

+0

嗯,我可以寫靜態初始化器到子類中 - 真正的問題,我不希望父類的靜態初始化器運行,如果我創建一個子類:-)我正在考慮包裝父類到另一個類(例如裝飾器模式)的字段中,但是靜態初始化器也以這種方式運行:-( – krisy

+1

@krisy:是的 - 基本上初始化器將以您實際使用該類的任何方式運行。使用它,或找到一些應對初始化程序運行的方法。 –

0

你可以做到以下幾點:

  • 反編譯的類(如與JAD)
  • 刪除靜態初始化代碼
  • 編譯新類
  • 複製新類到舊代碼的jar(刪除舊)
  • 讓你的子類

我知道這不是很美,但你別無選擇。靜態初始化器將在類加載後立即運行,並且不可能跳過它。