문제

간단한 테스트 케이스부터 시작하겠습니다.

import java.lang.reflect.Field;

public class Test {
  private final int primitiveInt = 42;
  private final Integer wrappedInt = 42;
  private final String stringValue = "42";

  public int getPrimitiveInt()   { return this.primitiveInt; }
  public int getWrappedInt()     { return this.wrappedInt; }
  public String getStringValue() { return this.stringValue; }

  public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException {
    Field field = Test.class.getDeclaredField(name);
    field.setAccessible(true);
    field.set(this, value);
    System.out.println("reflection: " + name + " = " + field.get(this));
  }

  public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
    Test test = new Test();

    test.changeField("primitiveInt", 84);
    System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());

    test.changeField("wrappedInt", 84);
    System.out.println("direct: wrappedInt = " + test.getWrappedInt());

    test.changeField("stringValue", "84");
    System.out.println("direct: stringValue = " + test.getStringValue());
  }
}

누구든지 출력으로 인쇄 될 내용을 추측하는 데 관심이 있습니다 (즉시 놀라움을 망치지 않도록 하단에 표시).

질문은 다음과 같습니다.

  1. 원시와 포장 된 정수가 왜 다르게 행동합니까?
  2. 반사와 직접 액세스가 다른 결과를 반환하는 이유는 무엇입니까?
  3. 나를 가장 괴롭히는 사람 - 왜 끈이 원시처럼 행동합니까? int 그리고 좋아하지 않습니다 Integer?

결과 (Java 1.5) :

reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42
도움이 되었습니까?

해결책

컴파일 타임 상수는 (JAVAC 컴파일 타임에서) 인쇄됩니다. JLS를 참조하십시오. 특히 15.28은 일정한 표현을 정의하고 13.4.9는 이진 호환성 또는 최종 필드 및 상수에 대해 설명합니다.

필드를 비정치로 만들거나 비 컴파일 타임을 할당하면 값이 상환되지 않습니다. 예를 들어:

개인 최종 문자열 stringValue = null! = null? "": "42";

다른 팁

내 생각에 이것은 더 나쁘다 : 동료는 다음과 같은 재미있는 것을 지적했다.

@Test public void  testInteger() throws SecurityException,  NoSuchFieldException, IllegalArgumentException, IllegalAccessException  {      
    Field value = Integer.class.getDeclaredField("value");      
    value.setAccessible(true);       
    Integer manipulatedInt = Integer.valueOf(7);      
    value.setInt(manipulatedInt, 666);       
    Integer testInt = Integer.valueOf(7);      
    System.out.println(testInt.toString());
}

이렇게하면 실행중인 전체 JVM의 동작을 변경할 수 있습니다. (물론 -127과 127 사이의 값에 대한 값 만 변경할 수 있습니다)

반사의 set(..) 방법이 작동합니다 FieldAccessor에스.

을 위한 int 그것은 얻는다 UnsafeQualifiedIntegerFieldAccessorImpl, 슈퍼 클래스가 readOnly 필드가있는 경우에만 재산이 사실입니다 둘 다 static 그리고 final

따라서 먼저 끊임없는 질문에 대답하려면 다음과 같습니다. final 예외없이 변경됩니다.

모든 서브 클래스 UnsafeQualifiedFieldAccessor 사용 sun.misc.Unsafe 값을 얻기 위해 클래스. 모든 방법이 있습니다 native, 그러나 그들의 이름은입니다 getVolatileInt(..) 그리고 getInt(..) (getVolatileObject(..) 그리고 getObject(..) 각기). 앞서 언급 한 액세서는 "휘발성"버전을 사용합니다. 비 휘발성 버전을 추가하면 다음은 다음과 같습니다.

System.out.println("reflection: non-volatile primitiveInt = "
     unsafe.getInt(test, (long) unsafe.fieldOffset(getField("primitiveInt"))));

(어디 unsafe 반사에 의해 인스턴스화됩니다 - 다른 방법으로 허용되지 않습니다) (그리고 나는 getObject ~을 위한 Integer 그리고 String)

흥미로운 결과를 제공합니다.

reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: non-volatile primitiveInt = 84
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: non-volatile wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42
reflection: non-volatile stringValue = 84

이 시점에서 나는 기억합니다 javaspecialists.eu의 기사 관련 문제에 대해 논의합니다. 따옴표 JSR-133:

필드 선언에서 최종 필드가 컴파일 타임 상수로 초기화되면 최종 필드의 사용이 컴파일 시간에 컴파일 타임 상수로 교체되므로 최종 필드에 대한 변경 사항은 관찰되지 않을 수 있습니다.

9 장에서는이 질문에서 관찰 된 세부 사항에 대해 설명합니다.

그리고이 행동은 예상치 못한 일이 아니라는 것이 밝혀졌습니다. final 필드는 객체의 초기화 직후에 발생해야합니다.

이것은 답이 아니지만 또 다른 혼란을 불러 일으 킵니다.

문제가 컴파일 타임 평가인지 또는 반사가 실제로 Java가 final 예어. 테스트 프로그램이 있습니다. 내가 추가 한 것은 또 다른 getter 호출 세트 였으므로 각각 전후에 하나가 있습니다. changeField() 전화.

package com.example.gotchas;

import java.lang.reflect.Field;

public class MostlyFinal {
  private final int primitiveInt = 42;
  private final Integer wrappedInt = 42;
  private final String stringValue = "42";

  public int getPrimitiveInt()   { return this.primitiveInt; }
  public int getWrappedInt()     { return this.wrappedInt; }
  public String getStringValue() { return this.stringValue; }

  public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException {
    Field field = MostlyFinal.class.getDeclaredField(name);
    field.setAccessible(true);
    field.set(this, value);
    System.out.println("reflection: " + name + " = " + field.get(this));
  }

  public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
    MostlyFinal test = new MostlyFinal();

    System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());
    test.changeField("primitiveInt", 84);
    System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());

    System.out.println();

    System.out.println("direct: wrappedInt = " + test.getWrappedInt());
    test.changeField("wrappedInt", 84);
    System.out.println("direct: wrappedInt = " + test.getWrappedInt());

    System.out.println();

    System.out.println("direct: stringValue = " + test.getStringValue());
    test.changeField("stringValue", "84");
    System.out.println("direct: stringValue = " + test.getStringValue());
  }
}

다음은 내가 얻는 출력입니다 (Eclipse, Java 1.6)

direct: primitiveInt = 42
reflection: primitiveInt = 84
direct: primitiveInt = 42

direct: wrappedInt = 42
reflection: wrappedInt = 84
direct: wrappedInt = 84

direct: stringValue = 42
reflection: stringValue = 84
direct: stringValue = 42

도대체가 getwrappedint ()를 직접 호출하는 이유는 무엇입니까?

이것을위한 작업이 있습니다. 정적 {} 블록에 제출 된 개인 정적 최종의 값을 설정하면 fileld가 인화되지 않기 때문에 작동합니다.

private static final String MY_FIELD;

static {
    MY_FIELD = "SomeText"
}

...

Field field = VisitorId.class.getDeclaredField("MY_FIELD");

field.setAccessible(true);
field.set(field, "fakeText");
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top