होते हुए भी final
एक क्षेत्र स्थिर इनिशियलाइज़र के बाहर संशोधित किया जा सकता है और (कम से कम जेवीएम हॉटस्पॉट) बिल्टकोड को पूरी तरह से ठीक करेगा।
समस्या यह है कि जावा कंपाइलर इसकी अनुमति नहीं देता है, लेकिन इसका उपयोग करके आसानी से बायपास किया जा सकता है objectweb.asm
। यहाँ पूरी तरह से वैध क्लासफाइल है जो कि बायोटेक सत्यापन से गुजरता है और सफलतापूर्वक JVM HotSpot OpenJDK12 के तहत लोड और आरंभीकृत होता है:
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();
जावा में, वर्ग निम्नानुसार बोल रहा है:
public class Cl{
private static final int fld;
public static void setFinalField1(){
fld = 5;
}
public static void setFinalField2(){
fld = 2;
}
}
जिसे संकलित नहीं किया जा सकता है javac
जा सकता है, लेकिन जेवीएम द्वारा लोड और निष्पादित किया जा सकता है।
जेवीएम हॉटस्पॉट ने इस तरह के वर्गों का विशेष उपचार इस अर्थ में किया है कि यह ऐसे "स्थिरांक" को निरंतर तह में भाग लेने से रोकता है। यह जाँच क्लास इनिशियलाइज़ेशन के बाईटेकोड पुनर्लेखन चरण पर की जाती है :
// 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);
}
}
}
}
}
}
एकमात्र प्रतिबंध जो जेवीएम हॉटस्पॉट चेक करता है, वह यह है कि final
फ़ील्ड को उस वर्ग के बाहर संशोधित नहीं किया जाना चाहिए जिस पर final
फ़ील्ड घोषित की गई है।