답변: 구성 방법 없 이도 대상 을 만 들 수 있 습 니 다.

원본:
구성 방법 없 이도 대상 을 만 들 수 있다.
이전에 내 가 인용 한 단락 도 붙 여 라.
RednaxelaFX 적 혀 있 습 니 다.
네, Effective Java, Second Edition 74 조 를 추천 합 니 다.
인용 하 다.
A second cost of implementing Serializable is that it increases the likelihood
of bugs and security holes.
Normally, objects are created using constructors; serialization is an extralinguistic mechanism for creating objects. Whether you accept the default behavior or override it, deserialization is a “hidden constructor” with all of the same issues as other constructors. Because there is no
explicit constructor associated with deserialization, it is easy to forget that you
must ensure that it guarantees all of the invariants established by the constructors
and that it does not allow an attacker to gain access to the internals of the object
under construction. Relying on the default deserialization mechanism can easily
leave objects open to invariant corruption and illegal access (Item 76).
자바 언어 차원 에서 볼 때 자바 류 의 구조 기 는 두 가지 방식 으로 만 호출 될 수 있 습 니 다. 하 나 는 new 표현 식 을 통 해, 다른 하 나 는 반사 호출 구조 기 를 통 해 호출 됩 니 다.이 두 가지 방식 은 자바 프로그래머 에 게 모두 '전체' 이지 만 실제 새 대상 의 동작 은 두 단계 로 나 뉜 다.
1. 빈 대상 만 들 기 (이 때 형식 이 올 바 름), 대응 하 는 바이트 코드 는 new
2. 특정한 버 전의 구조 기 를 호출 하고 해당 바이트 코드 는 invokespecial '< init' 입 니 다.
기본 자바 반 직렬 화 체 제 는 똑 같이 두 단계 로 나 뉘 지만 다음 과 같 습 니 다.
1. 빈 대상 을 만 듭 니 다.
2. 사용자 가 정의 하 는 반 직렬 화 방법 (readObject, 있 으 면) 을 호출 하거나 기본 반 직렬 화 방법 을 호출 합 니 다.
이것 이 바로 반 서열 화 를 '숨겨 진 구조 기' 로 볼 수 있 는 이유 다.
빈 대상 을 만 들 고 싶 지만 구조 기 를 사용 하지 않 는 다 면 sun. misc. Unsafe. allocate Instance () 를 사용 해 보 세 요.
Groovy 콘 솔 로 보 여 주세요.
Groovy Shell (1.7.2, JVM: 1.6.0_23)
Type 'help' or '\h' for help.
----------------------------------------------------------------
groovy:000> class Foo {
groovy:001>   int value = 12345;
groovy:002>   Foo() { println "foo ctor!" }
groovy:003>   int getValue() { println "getValue"; value }
groovy:004> }
===> true
groovy:000> f1 = new Foo()
foo ctor!
===> Foo@10f0625
groovy:000> f1.value
getValue
===> 12345
groovy:000> f2 = sun.misc.Unsafe.theUnsafe.allocateInstance(Foo)
===> Foo@38fff7
groovy:000> f2.value
getValue
===> 0
groovy:000> quit

f2 가 가리 키 는 Foo 대상 을 만 들 때 구조 기 는 호출 되 지 않 았 습 니 다 ("foo ctor!" 를 출력 하지 않 았 습 니 다). 인 스 턴 스 상태 (value) 도 사용자 가 지정 한 값 으로 초기 화 되 지 않 았 습 니 다 (12345). 전체 대상 의 모든 필드 는 기본 상태 (0 또는 null 또는 false 등) 에 있 습 니 다.
다만 이 화 제 를 빌려 Unsafe 예 를 들 어 자바 대상 의 생 성 은 두 단계 로 나 뉘 고 구조 기 를 호출 하 는 것 이 그 중의 한 단계 라 는 것 을 설명 한다.반 서열 화 할 때 꼭 언 세 이 프 를 썼 다 는 건 아 닙 니 다. 이 건 잘 구분 해 주세요 ^ ^
실제로 Sun JDK 의 실현 에서 자바 차원 의 반사 라 이브 러 리 와 JVM 차원 의 반사 가 서로 협력 하여 반 서열 화 를 완성 한다.java. io. Object StreamClass 는 반사 방법 / 구조 기 와 유사 한 체 제 를 호출 하여 이른바 '직렬 화 구조 기' 를 얻 고 반 직렬 화 할 때 이 버 전의 구조 기 를 호출 합 니 다.
이 '직렬 화 구조 기' 를 만 들 때 계승 체인 에서 가장 구체 적 이 고 추상 적 인 방향 으로 검색 하여 첫 번 째 직렬 화 할 수 없 는 클래스 (Serializable 인 터 페 이 스 를 실현 하지 않 은 클래스) 를 찾 아 무 참 구조 기 를 찾 아 호출 해 야 합 니 다.즉, 반 직렬 화 할 때 사용자 코드 에 명 시 된 구조 기 를 전혀 호출 하지 않 는 것 이 아니 라 Serializable 클래스 를 호출 하지 않 는 것 일 뿐이다.
구조 기 에 대해 서 는 참고 할 만 한 다른 토론 이 있 습 니 다.
인 스 턴 스 구조 기 는 정적 방법 입 니까?
======================================================================
쓰다
4. 567915. 원본 의 예 를 가지 고 실험 을 하면 문 제 를 더욱 구체 적 으로 설명 할 수 있다.
원본 코드 (조금 수정 하여 가방 이름 을 지 웠 습 니 다):
import java.io.ByteArrayInputStream;   
import java.io.ByteArrayOutputStream;   
import java.io.ObjectInputStream;   
import java.io.Serializable;

public class TestClass implements Serializable {   
    private static final long serialVersionUID = 0L;   
    public TestClass() throws Exception {   
        throw new Exception("!!!");   
    }   
  
    public static void main(String[] args) throws Exception {   
        byte[] head = { -84, -19, 0, 5, 115, 114, 0 };   
        byte[] ass = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 120, 112 };   
        String name = TestClass.class.getName();   
        ByteArrayOutputStream baos = new ByteArrayOutputStream();   
        baos.write(head);   
        baos.write(name.length());   
        baos.write(name.getBytes());   
        baos.write(ass);   
        baos.flush();   
        baos.close();   
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));   
        TestClass o = (TestClass) ois.readObject();   
        ois.close();   
        System.out.println("Created: " + o);
        System.in.read(); //             dump class
    }   
}

MyFilter:
import sun.jvm.hotspot.tools.jcore.ClassFilter;
import sun.jvm.hotspot.oops.InstanceKlass;

public class MyFilter implements ClassFilter {
    @Override
    public boolean canInclude(InstanceKlass kls) {
        String klassName = kls.getName().asString();
        return klassName.startsWith("sun/reflect/GeneratedSerializationConstructorAccessor");
    }
}

컴 파일 할 때 사용:
javac -classpath ".:$JAVA_HOME/lib/sa-jdi.jar" MyFilter TestClass

그리고 먼저 실행:
java TestClass

종료 시 키 지 말고 jps 로 프로 세 스 ID 를 찾 은 다음 ClassDump 로 class 파일 을 가 져 옵 니 다.
java -classpath ".:./bin:$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=MyFilter sun.jvm.hotspot.tools.jcore.ClassDump 7566

이렇게 해서... / sun / reflect / Generated SerializationConstructor Accessor 1. class 파일 을 얻 었 습 니 다.그러면 자바 p 로 내용 을 볼 수 있 습 니 다.
Classfile /D:/experiment/test_java_deserialize/sun/reflect/GeneratedSerializationConstructorAccessor1.class
  Last modified 2010-12-23; size 1313 bytes
  MD5 checksum 6d59fc9bb0c7d58458cdc76714829a0f
public class sun.reflect.GeneratedSerializationConstructorAccessor1 extends sun.reflect.SerializationConstructorAccessorImpl
  minor version: 0
  major version: 46
  flags: ACC_PUBLIC

Constant pool: // ...
{
  public sun.reflect.GeneratedSerializationConstructorAccessor1();
    flags: ACC_PUBLIC

    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #36                 // Method sun/reflect/SerializationConstructorAccessorImpl."<init>":()V
         4: return        

  public java.lang.Object newInstance(java.lang.Object[]) throws java.lang.reflect.InvocationTargetException;
    flags: ACC_PUBLIC

    Exceptions:
      throws java.lang.reflect.InvocationTargetException
    Code:
      stack=6, locals=2, args_size=2
         0: new           #6                  // class TestClass
         3: dup           
         4: aload_1       
         5: ifnull        24
         8: aload_1       
         9: arraylength   
        10: sipush        0
        13: if_icmpeq     24
        16: new           #22                 // class java/lang/IllegalArgumentException
        19: dup           
        20: invokespecial #29                 // Method java/lang/IllegalArgumentException."<init>":()V
        23: athrow        
        24: invokespecial #12                 // Method java/lang/Object."<init>":()V
        27: areturn       
        28: invokespecial #42                 // Method java/lang/Object.toString:()Ljava/lang/String;
        31: new           #22                 // class java/lang/IllegalArgumentException
        34: dup_x1        
        35: swap          
        36: invokespecial #32                 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
        39: athrow        
        40: new           #24                 // class java/lang/reflect/InvocationTargetException
        43: dup_x1        
        44: swap          
        45: invokespecial #35                 // Method java/lang/reflect/InvocationTargetException."<init>":(Ljava/lang/Throwable;)V
        48: athrow        
      Exception table:
         from    to  target type
             0    24    28   Class java/lang/ClassCastException
             0    24    28   Class java/lang/NullPointerException
            24    27    40   Class java/lang/Throwable
}

대응 하 는 자바 코드 (표시, 안의 논 리 는 자바 로 직접 표시 할 수 없습니다. 자바 에 있 는 new 표현 식 은 생 성 대상 과 호출 구조 기 를 동시에 포함 하고 있 으 며, 이 두 동작 은 같은 유형 을 대상 으로 해 야 합 니 다. 여기 서 TestClass 를 만 든 인 스 턴 스 는 Object 의 무 참 구조 기 를 호출 합 니 다):
package sun.reflect;

public class GeneratedSerializationConstructorAccessor1
         extends SerializationConstructorAccessorImpl {
    public GeneratedMethodAccessor1() {
        super();
    }
    
    public Object newInstance(Object[] args)
            throws InvocationTargetException {
        try {
            // create an unitialized TestClass instance
            TestClass temp = newUnitializedTestClassInstance(); // new           #6  // class TestClass
                                                                // dup
            // check parameters
            if (args.length != 0) throw new IllegalArgumentException();
        } catch (final ClassCastException | NullPointerException e) {
            throw new IllegalArgumentException(e.toString());
        }
        // invoke Object() constructor
        try {
            invokeObjectConstructor(temp);                      // invokespecial #12 // Method java/lang/Object."<init>":()V
            return temp;                                       // areturn
        } catch (Throwable t) {
            throw new InvocationTargetException(t);
        }
    }
}

(비고: 위의 코드 는 편리 하 게 사용 되 었 습 니 다.
전에 소 개 했 던 방법.
재 미 있 는 주석 을 유의 할 수 있다.
package sun.reflect;

/** <P> Java serialization (in java.io) expects to be able to
    instantiate a class and invoke a no-arg constructor of that
    class's first non-Serializable superclass. This is not a valid
    operation according to the VM specification; one can not (for
    classes A and B, where B is a subclass of A) write "new B;
    invokespecial A()" without getting a verification error. </P>

    <P> In all other respects, the bytecode-based reflection framework
    can be reused for this purpose. This marker class was originally
    known to the VM and verification disabled for it and all
    subclasses, but the bug fix for 4486457 necessitated disabling
    verification for all of the dynamically-generated bytecodes
    associated with reflection. This class has been left in place to
    make future debugging easier. </P> */

abstract class SerializationConstructorAccessorImpl
    extends ConstructorAccessorImpl {
}

관련 된 주목 할 만 한 방법 과 유형 은 다음 과 같다.
java.io.ObjectStreamClass.getSerializableConstructor()
sun.reflect.ReflectionFactory.newConstructorForSerialization()
sun.reflect.MethodAccessorGenerator.generateSerializationConstructor()
sun.reflect.SerializationConstructorAccessorImpl
======================================================================
위 에 서 는 Sun JDK 로 역 직렬 화 가능 한 실현 방식 을 보 여 주 었 다.사실 Sun JDK 도 1.4 부터 이런 방식 을 사 용 했 고, 기 존 에는 다른 방식 을 사용 했다.이 방식 은 검증 (verification) 을 통과 할 수 없 는 바이트 코드 시퀀스 를 사 용 했 습 니 다. 억지로 하려 는 말 은 JVM 규범 과 충돌 합 니 다.이 를 실행 할 수 있 도록 HotSpot VM 에 서 는 전문 적 으로 뒷문 을 열 었 다.
'규범' 과 실현 간 의 차 이 를 잘 구분 하 세 요.
규범 은 일반적으로 비교적 빡빡 하 게 정 해 져 있 으 며, 실현 은 많은 곳 에서 '지름길 로' 갈 수 있 습 니 다. 위의 사용자 가 지름길 로 가 는 것 을 감지 하지 못 하 게 하면 문제 없습니다. ∩ ^ ^∩

좋은 웹페이지 즐겨찾기