Java 리플렉션을 사용하여 개인 정적 최종 필드 변경
는 ㅇㅇㅇㅇㅇㅇㅇㅇ라는 수업을 .private static final
불행히도 런타임에 변경해야 하는 필드입니다.
을 하다 보면 '하다'라는가 나옵니다.java.lang.IllegalAccessException: Can not set static final boolean field
값을 변경할 수 있는 방법이 있나요?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
'아니오'라고 했을 경우SecurityManager
수 되어 있기 때문에 하면 .setAccessible
private
'수식자'를 합니다.final
를 실제로 private static final
syslog.syslog.syslog.
다음은 예를 제시하겠습니다.
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
'아니오'라고 했을 경우SecurityException
가 출력됩니다."Everything is true"
.
여기서 실제로 실행되는 것은 다음과 같습니다.
- 인 시 the the the
boolean
「」을 참조해 주세요.true
★★★★★★★★★★★★★★★★★」false
…에main
타입 「」에 되어 있습니다.Boolean
" "filename"Boolean.TRUE
★★★★★★★★★★★★★★★★★」Boolean.FALSE
- Reflection은 를 참조하기 위해 사용됩니다.
Boolean
Boolean.TRUE
- 그 후, 「」, 「」, 「」가 될 마다,
false
is is is is is is is is is is is is is is is is is is is에 자동 되어 있습니다.Boolean.FALSE
.Boolean
에 참조된 바와Boolean.TRUE
- ★★★★★★★★★★★★★★★★★★★★★★★의 모든 것
"false"
은 ★★★★★★★★★★★★★★★★★."true"
관련 질문
- 변경
static final File.separatorChar
- setAccessible을 "합법적" 사용으로만 제한하는 방법
- 를 조작하는 예를 가지고 있다.
Integer
의 캐시,, " " "String
,syslogs
- 를 조작하는 예를 가지고 있다.
주의사항
안 될 수도 요. 안 될 수도 요.SecurityManager
을 사용하다사용 패턴에 따라서는 동작하거나 동작하지 않을 수도 있습니다.
은 「」를 가 있습니다.
final
★★★★★★★★★★★★★★★★★★★★★★★★★★final
필드는 반영 및 기타 구현 종속 수단을 통해 변경할 수 있습니다.를 갖는 으로 오브젝트가 구성되는 입니다.final
오브젝트의 필드가 갱신됩니다.스레드에 할 수 또, 이 오브젝트를 다른 스레드에 표시할 도 없습니다.final
읽혀집니다.final
이치노의final
합니다. 여기서 는 생성자 의 필드입니다.final
되고, 「」의 각에 「」가 표시됩니다.final
반사 또는 기타 특수 메커니즘을 통한 필드.그럼에도 불구하고, 많은 합병증이 있습니다.「」의 '
final
되며, 선언에서 컴파일 시간 상수로 됩니다.final
를 사용할 해당 볼 수 .final
필드는 컴파일 시 컴파일 시간 상수로 대체됩니다.는 이 에 따라 으로 최적화될 수 있다는 입니다.
final
[ ]이렇게 하다할 수 .final
필드에는 생성자에서 발생하지 않는 최종 필드의 수정이 포함되어 있습니다.
「 」를 참조해 주세요.
- JLS 15.28 상수식
- It's unlikely that this technique works with a primitive 이 기술이 원시인과는 통하지 않을 것 같다.
private static final boolean
, 컴파일 시간 상수로서, "새로운" 값은 간 수 라 할 관 " 없 습 기 값 때 은" because- it" may constantable the문new에 not as
- It's unlikely that this technique works with a primitive 이 기술이 원시인과는 통하지 않을 것 같다.
부록: 비트 조작에 대하여
기본적으로는
field.getModifiers() & ~Modifier.FINAL
turns off the bit corresponding to 대응하는 비트를 끄다Modifier.FINAL
부에서field.getModifiers()
. . .&
약간과 비트, 비트, 리 그리고 고, 그, is, the~
조금 더 좋은 아침이에요.비트 단위입니다.
「 」를 참조해 주세요.
상수 표현 기억하기
아직도 이 문제를 해결할 수 없는가? 나처럼 우울증에 빠진 적이 있는가?당신의 코드는 이렇게 생겼나요?
public class A {
private final String myVar = "Some Value";
}
이 답변, 특히 @Pshemo의 코멘트를 읽고, 상수 표현은 다르게 취급되기 때문에 수정할 수 없다는 것을 깨달았습니다.따라서 다음과 같이 코드를 변경해야 합니다.
public class A {
private final String myVar;
private A() {
myVar = "Some Value";
}
}
당신이 반의 주인이 아니라면...난 널 이해해!
이 동작의 이유에 대한 자세한 내용은 다음을 참조하십시오.
If the value assigned to a 에 할당된 값이static final boolean
필드는 컴파일 시 알 수 있으며 상수입니다.원시 필드 또는String
type can be compile-time constants.type은 컴파일 시간 상수입니다.필드를 참조하는 코드에서 일관되게 됩니다.필드를 참조하는 모든 코드에 상수가 삽입됩니다.실제 런타임에서 읽을 수 없기 때문에 효과가 없습니다.실행 시 필드를 실제로 읽지 않기 때문에 필드를 변경해도 아무런 영향이 없습니다.
Java 언어 사양에는 다음과 같이 기재되어 있습니다.
필드가 상수 변수('4.12.4)인 경우 키워드 final을 삭제하거나 값을 변경해도 기존 바이너리와의 호환성이 저하되지 않습니다.다만, 필드를 재컴파일하지 않는 한, 기존의 바이너리와의 새로운 값은 표시되지 않습니다.이는 사용 자체가 컴파일 시간 상수식(§15.28)이 아닌 경우에도 해당됩니다.
다음은 예를 제시하겠습니다.
class Flag {
static final boolean FLAG = true;
}
class Checker {
public static void main(String... argv) {
System.out.println(Flag.FLAG);
}
}
했을 Checker
를 하는 것이 '이렇게'를 참조하면 됩니다.Flag.FLAG
값(즉, 1의 값)을만 하면 됩니다true
3번입니다.
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokevirtual #3; //Method java/io/PrintStream.println:(Z)V
7: return
Java Language Specification, 17장, 섹션 17.5.4 "쓰기 금지 필드:
일반적으로 최종적이고 정적인 필드는 변경할 수 없습니다.단, System.in, System.out 및 System.err은 기존의 이유로 System 메서드로 변경할 수 있어야 하는 정적 최종 필드입니다.setin, 시스템setOut 및 시스템.setErr. 이러한 필드는 일반적인 최종 필드와 구별하기 위해 쓰기 금지되어 있습니다.
출처 : http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
Joor 라이브러리와도 통합했습니다.
그냥 사용하다
Reflect.on(yourObject).set("finalFieldName", finalFieldValue);
이 를 수정했습니다.override
이전 솔루션들이 놓치고 있는 것 같습니다.그러나 다른 좋은 해결책이 없을 때만 신중하게 사용하십시오.
상위 답변과 함께 가장 간단한 접근 방식을 사용할 수 있습니다. 커먼즈FieldUtils
을 할 수 가 있습니다.class class 、 class class 。 봐주세요FieldUtils.removeFinalModifier
방법.대상 필드 인스턴스와 접근성 강제 플래그를 지정해야 합니다(비공개 필드를 사용하는 경우).자세한 내용은 여기를 참조하십시오.
그렇다 해도 even even even even even even even even evenfinal
필드는 정적 이니셜라이저 외부에서 수정할 수 있으며 (최소한 JVM HotSpot은) 바이트 코드를 완벽하게 실행합니다.
이를 , 이는 "이것"을 사용하면 쉽게 할 수 입니다. 그러나 이는 다음 명령을 사용하여 쉽게 우회할 수 있습니다.objectweb.asm
JVMS 사양의 관점에서 볼 때 비활성 클래스 파일은 p̶e̶r̶f̶t̶y̶v̶a̶i̶d̶c̶l̶c̶f̶i̶e인데 바이트 코드 검증을 통과하여 JVM HotDKSpot에서 정상적으로 로드되어 초기화됩니다.
ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Cl", null, "java/lang/Object", null);
{
FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "fld", "I", null, null);
fv.visitEnd();
}
{
// public void setFinalField1() { //... }
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "setFinalField1", "()V", null, null);
mv.visitMaxs(2, 1);
mv.visitInsn(Opcodes.ICONST_5);
mv.visitFieldInsn(Opcodes.PUTSTATIC, "Cl", "fld", "I");
mv.visitInsn(Opcodes.RETURN);
mv.visitEnd();
}
{
// public void setFinalField2() { //... }
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "setFinalField2", "()V", null, null);
mv.visitMaxs(2, 1);
mv.visitInsn(Opcodes.ICONST_2);
mv.visitFieldInsn(Opcodes.PUTSTATIC, "Cl", "fld", "I");
mv.visitInsn(Opcodes.RETURN);
mv.visitEnd();
}
cw.visitEnd();
Java에서 클래스는 대략 다음과 같이 표시됩니다.
public class Cl{
private static final int fld;
public static void setFinalField1(){
fld = 5;
}
public static void setFinalField2(){
fld = 2;
}
}
할 수 .javac
JVM에서 및 할 수
JVM HotSpot은 이러한 "상수"가 지속적인 폴딩에 참여하지 못하도록 하는 의미에서 이러한 클래스를 특별 취급합니다.이 체크는 클래스 초기화 바이트코드 개서 단계에서 실행됩니다.
// Check if any final field of the class given as parameter is modified
// outside of initializer methods of the class. Fields that are modified
// are marked with a flag. For marked fields, the compilers do not perform
// constant folding (as the field can be changed after initialization).
//
// The check is performed after verification and only if verification has
// succeeded. Therefore, the class is guaranteed to be well-formed.
InstanceKlass* klass = method->method_holder();
u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1);
constantPoolHandle cp(method->constants());
Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index));
if (klass->name() == ref_class_name) {
Symbol* field_name = cp->name_ref_at(bc_index);
Symbol* field_sig = cp->signature_ref_at(bc_index);
fieldDescriptor fd;
if (klass->find_field(field_name, field_sig, &fd) != NULL) {
if (fd.access_flags().is_final()) {
if (fd.access_flags().is_static()) {
if (!method->is_static_initializer()) {
fd.set_has_initialized_final_update(true);
}
} else {
if (!method->is_object_initializer()) {
fd.set_has_initialized_final_update(true);
}
}
}
}
}
}
은 JVM HotSpot의 입니다.final
에서는 할 수 .final
필드는 에 선언됩니다.
Manager가 Manager를 할 수 .AccessController.doPrivileged
위의 승인된 답변에서 동일한 예를 들어 보겠습니다.
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
// wrapping setAccessible
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
modifiersField.setAccessible(true);
return null;
}
});
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
"하다"는AccessController.doPrivileged
는 다음과 같이할 수
AccessController.doPrivileged((PrivilegedAction) () -> {
modifiersField.setAccessible(true);
return null;
});
인터뷰 질문 중 하나에서 방금 그 질문을 보았습니다. 가능하다면 반영 또는 실행 시 최종 변수를 변경할 수 있습니다.정말 관심이 많아져서 내가 알게 된 건
/**
* @author Dmitrijs Lobanovskis
* @since 03/03/2016.
*/
public class SomeClass {
private final String str;
SomeClass(){
this.str = "This is the string that never changes!";
}
public String getStr() {
return str;
}
@Override
public String toString() {
return "Class name: " + getClass() + " Value: " + getStr();
}
}
최종 String 변수가 있는 단순 클래스입니다.메인 클래스에서는 java.lang.reflect를 Import합니다.필드
/**
* @author Dmitrijs Lobanovskis
* @since 03/03/2016.
*/
public class Main {
public static void main(String[] args) throws Exception{
SomeClass someClass = new SomeClass();
System.out.println(someClass);
Field field = someClass.getClass().getDeclaredField("str");
field.setAccessible(true);
field.set(someClass, "There you are");
System.out.println(someClass);
}
}
출력은 다음과 같습니다.
Class name: class SomeClass Value: This is the string that never changes!
Class name: class SomeClass Value: There you are
Process finished with exit code 0
매뉴얼에 따르면 https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
JDK 1.8u91에 도입될 때까지, 받아들여진 회답은 유효했습니다. 나서 나는 했다는 것을 .field.set(null, newValue);
를 setFinalStatic
★★★★★★ 。
으로 인해 이 다소 이 있습니다( 리플렉션의 내부 설정).sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
했을 sun.reflect.UnsafeStaticObjectFieldAccessorImpl
더 이상 자세히 설명하지 않았습니다.
일시적으로 오래된 값을 바탕으로 새로운 값을 설정하고 나중에 오래된 값을 다시 설정해야 했기 때문에 외부에서 계산 기능을 제공하고 오래된 값을 반환하도록 시그니처를 조금 변경했습니다.
public static <T> T assignFinalField(Object object, Class<?> clazz, String fieldName, UnaryOperator<T> newValueFunction) {
Field f = null, ff = null;
try {
f = clazz.getDeclaredField(fieldName);
final int oldM = f.getModifiers();
final int newM = oldM & ~Modifier.FINAL;
ff = Field.class.getDeclaredField("modifiers");
ff.setAccessible(true);
ff.setInt(f,newM);
f.setAccessible(true);
T result = (T)f.get(object);
T newValue = newValueFunction.apply(result);
f.set(object,newValue);
ff.setInt(f,oldM);
return result;
} ...
그러나 일반적인 경우, 이것은 충분하지 않을 것이다.
에는 도움이 것이 이 중 않습니다.Android
특히.심지어 제가 꽤 많이 사용하는 사용자이기도 합니다.Reflect
확실히, 그것도 아파치의 것도 아니다.FieldUtils
.-알겠습니다. -알겠습니다.그렇게 해 주세요.
Android의 문제
에는 안드로이드라는 것이입니다.modifiers
의 Field
된 제안(무용지물입니다.class: 이 、 : 、 : 、 : 、 : 、 : 、 class class class class class 。
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
실,에서 하자면, 용 in in in in.FieldUtils.removeFinalModifier()
:
// Do all JREs implement Field with a private ivar called "modifiers"?
final Field modifiersField = Field.class.getDeclaredField("modifiers");
그래서 대답은 '아니오'야
솔루션
매우 간단합니다.modifiers
은 필음음음 , , ,음 , , 。accessFlags
됩니다.
Field accessFlagsField = Field.class.getDeclaredField("accessFlags");
accessFlagsField.setAccessible(true);
accessFlagsField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
주의사항 #1: 이것은 필드가 클래스의 정적인지 여부에 관계없이 기능합니다.
필드 일 수 필드 통해 합니다. 접근은 필드 자체에서 사용할 수 있습니다.
field.setAccessible(true)
에)accessFlagsField.setAccessible(true)
.
Java 12 이후로는 답변이 작동하지 않습니다.
는 하다를 하는 예시입니다.private static final
Java 12 이후의 필드(이 답변에 기초함)입니다.
private Object modifyField(Object newFieldValue, String fieldName, Object classInstance) throws NoSuchFieldException, IllegalAccessException {
Field field = classInstance.getClass().getDeclaredField(fieldName);
VarHandle MODIFIERS;
field.setAccessible(true);
var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class);
int mods = field.getModifiers();
if (Modifier.isFinal(mods)) {
MODIFIERS.set(field, mods & ~Modifier.FINAL);
}
Object previousValue = field.get(classInstance);
field.set(null, newFieldValue);
return previousValue;
}
자세한 내용은 이 스레드를 참조하십시오.
에서는, JDK 18 에서의 때문에, 은 더 이상 invokedynamic
★★★★★★★★★★★★★★★★★」MethodHandle
s는 JEP-416(PR)의 일부입니다.
이 훌륭한 작품의 주 저자인 Mandy Chung의 말을 다음 코멘트로 인용합니다.강조는 내 것이다.
필드인 " " " 입니다.
Field
오브젝트는 다음과 같은 경우에만 쓰기 액세스 권한을 가집니다.
setAccessible(true)
에 성공했습니다Field
오브젝트- 필드는 정적이지 않습니다.
- 필드의 선언 클래스가 숨겨진 클래스가 아닙니다.
- 필드의 선언 클래스가 레코드 클래스가 아닙니다.
단순히 개인 필드일 경우 다음을 수행할 수 있습니다.
MyClass myClass= new MyClass();
Field aField= myClass.getClass().getDeclaredField("someField");
aField.setAccessible(true);
aField.set(myClass, "newValueForAString");
및 No Such Field Exception을 던지거나 처리합니다.
★★★★★★★★★★★★★★★★★★★★의 포인트final
필드는 한 번 설정하면 재할당할 수 없습니다.JVM은 이 가렌티지를 사용하여 다양한 장소(예: 외부 변수를 참조하는 내부 클래스)에서 일관성을 유지합니다.!, JVM이 망가져!
은 그것을 이다.final
★★★★★★★★★★★★★★★★★★.
언급URL : https://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection
'IT' 카테고리의 다른 글
자바에서는 시간이 변합니까? (0) | 2022.10.30 |
---|---|
Java에서 루프가 1개 이상 증가하도록 하는 방법 (0) | 2022.10.30 |
원격 연결 Mysql Ubuntu (0) | 2022.10.29 |
for-of-loop 내부 ES6 어레이 요소 인덱스 액세스 (0) | 2022.10.29 |
설치된 판다 버전을 찾는 방법 (0) | 2022.10.29 |