문제
간단한 테스트 케이스부터 시작하겠습니다.
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());
}
}
누구든지 출력으로 인쇄 될 내용을 추측하는 데 관심이 있습니다 (즉시 놀라움을 망치지 않도록 하단에 표시).
질문은 다음과 같습니다.
- 원시와 포장 된 정수가 왜 다르게 행동합니까?
- 반사와 직접 액세스가 다른 결과를 반환하는 이유는 무엇입니까?
- 나를 가장 괴롭히는 사람 - 왜 끈이 원시처럼 행동합니까?
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");