2013-01-23 3 views
2

나는 정적 초기화 블록이 레거시 (안 수정 :-() 코드를 가지고 나는 서브 클래스를 만들고 싶습니다 -..?하지만 정적 블록을 실행하지 않고 정적 이니셜 라이저를 실행하지 않으려면 어떻게해야합니까?

이 일을 어떤 방법이 들어 있나요 예 : class.ForName 메서드는 클래스를 초기화하는 데 필요한 두 번째 인수가 있지만 슬프게도 나를 위해 작동하지 않습니다 (아마도 다른 것을 의미합니다). http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName(java.lang.String, boolean, java.lang.ClassLoader)

업데이트 : 위에 언급 된 클래스 .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 

} 

출력 :
널 (null)

+0

제가 투표를했습니다,하지만 제 생각에는 예제를 제공해야한다고 생각합니다. – CAMOBAP

+0

감사합니다. – krisy

2

:-(내가 정적 초기화 블록이 레거시 (안 수정 :-() 코드를 가지고있다. 내가 하위 클래스를 만들고 싶습니다 -하지만 정적 블록을 실행하지 않고.

하위 클래스를 만들 수는 있지만 그 서브 클래스를 초기화 할 수는 없습니다. 따라서이 클래스는 쓸모가 없습니다.

section 12.4.2 of the JLS (클래스 초기화)에서 : C 클래스가 아닌 인터페이스이고, 그 슈퍼 SC 아직 초기화되지 않은 경우

다음, 재귀 SC이 전체 절차를 수행한다.

기본적으로 디자인을 재고해야한다고 생각합니다. 정적 이니셜 라이저 실행에 대처할 수 있도록 이상적입니다.

+0

글쎄, 정적 초기화자를 서브 클래스에 작성할 수 있습니다. 실제 문제는, 서브 클래스를 생성하면 부모 클래스의 정적 초기화 기가 실행되는 것을 원하지 않습니다 :-) 부모 클래스를 래핑하려고합니다. 다른 클래스 (예 : 데코레이터 패턴)의 필드에 추가하지만 static initializer는 다음과 같이도 실행됩니다 .-- ( – krisy

+1

@krisy : 예 - 기본적으로 이니셜 라이저는 실제로 클래스를 사용하는 방식으로 실행됩니다. 그것을 사용하거나 이니셜 라이저 실행에 대처하는 방법을 찾으십시오. –

0

당신은 다음을 수행 할 수 클래스 디 컴파일

  • 를 (예를 들어,
  • ) JAD와 서브 클래스 확인)
  • 를 이전 한 (이전 코드의 항아리에 새 클래스
  • 복사 새 클래스를 컴파일 제거 정적 초기화 코드
  • 를 제거

나는 그것이 아름답 지 않다는 것을 안다. 그러나 당신은 거의 다른 선택권이 없다. 정적 초기화 프로그램은 클래스가로드 된 직후에 실행되며 건너 뛸 가능성이 없습니다.

관련 문제