java - জাভা প্রতিফলন ব্যবহার করে ব্যক্তিগত স্ট্যাটিক চূড়ান্ত ক্ষেত্র পরিবর্তন করুন




reflection static (7)

Assuming No SecurityManager আপনাকে এটি করতে বাধা setAccessible , আপনি setAccessible ব্যবহার করতে পারেন private setAccessible এবং final পরিত্রাণ পেতে মডিফায়ার রিসেট করতে, এবং প্রকৃতপক্ষে একটি private static final ক্ষেত্র সংশোধন করতে পারেন।

এখানে একটি উদাহরণ:

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" প্রিন্ট করে।

আসলে এখানে কি করা হয় তা হল:

  • আদি boolean মানগুলি main এবং true মিথ্যাগুলি রেফারেন্স টাইপ Boolean.TRUE " Boolean.TRUE " Boolean.TRUE এবং Boolean.FALSE
  • প্রতিফলন public static final Boolean.FALSE পরিবর্তন Boolean দ্বারা উল্লিখিত Boolean উল্লেখ করতে ব্যবহৃত হয়। Boolean.TRUE
  • ফলস্বরূপ, পরবর্তীতে যখনই Boolean.FALSE Boolean অটোবক্স করা হয়। Boolean.FALSE , এটি একই Boolean বোঝায় যেটি Boolean দ্বারা প্রদান করা হয়েছে। Boolean.TRUE
  • সবকিছু যে "false" এখন "true"

সম্পর্কিত প্রশ্নাবলী

আদেশ সহকারে

আপনি যখন ভালো কিছু করবেন তখন চরম যত্ন নেওয়া উচিত। এটি কাজ করতে পারে না কারণ একটি SecurityManager মঞ্জুর উপস্থিত থাকতে পারে, কিন্তু এটি ব্যবহার না করলেও, এটি ব্যবহার প্যাটার্নের উপর নির্ভর করে, এটি কাজ করে নাও হতে পারে।

JLS 17.5.3 চূড়ান্ত ক্ষেত্রের পরবর্তী পরিবর্তন

কিছু ক্ষেত্রে, যেমন deserialization, সিস্টেম নির্মাণের পরে একটি বস্তুর final ক্ষেত্র পরিবর্তন করতে হবে। final ক্ষেত্র প্রতিফলন এবং অন্যান্য বাস্তবায়ন নির্ভর উপায় মাধ্যমে পরিবর্তন করা যেতে পারে। একমাত্র প্যাটার্ন যার মধ্যে যুক্তিসঙ্গত সেমেটিক্স রয়েছে, এটি একটি বস্তু তৈরি করা হয় এবং তারপরে বস্তুর final ক্ষেত্রগুলি আপডেট করা হয়। অবজেক্টের final ক্ষেত্রগুলিতে সমস্ত আপডেট সম্পূর্ণ না হওয়া পর্যন্ত বস্তুর অন্যান্য থ্রেডগুলিতে দৃশ্যমান করা উচিত নয় এবং final ক্ষেত্রগুলি পড়তে হবে না। final ক্ষেত্রের ফ্রিজগুলি কন্সট্রকটরের শেষে final ক্ষেত্র সেট করা হয় এবং প্রতিফলন বা অন্যান্য বিশেষ পদ্ধতির মাধ্যমে final ক্ষেত্রের প্রতিটি সংশোধন করার পরেই ঘটে।

এমনকি তারপর, অনেক জটিলতা আছে। একটি final ক্ষেত্র যদি ক্ষেত্র ঘোষণায় কম্পাইল-টাইম ধ্রুবক থেকে শুরু হয় তবে final ক্ষেত্রের পরিবর্তনগুলি লক্ষ্য করা যাবে না, কারণ সেই final ক্ষেত্রের ব্যবহারগুলি কম্পাইল-সময় ধ্রুবক সহ কম্পাইল সময়তে প্রতিস্থাপিত হয়।

আরেকটি সমস্যা স্পেসিফিকেশন final ক্ষেত্রের আক্রমনাত্মক অপ্টিমাইজেশান করতে পারবেন। একটি থ্রেডের মধ্যে, এটি একটি final ক্ষেত্রের পুনর্বিবেচনার অনুমতি দেয় যা একটি final ক্ষেত্রের সংশোধন যা কনস্ট্রাক্টারে সংঘটিত হয় না।

আরো দেখুন

  • জেএলএস 15.28 কনস্ট্যান্ট এক্সপ্রেশন
    • এটি অসম্ভাব্য যে এই কৌশলটি একটি প্রাথমিক private static final boolean সাথে কাজ করে, কারণ এটি কম্পাইল-সময় ধ্রুবক হিসাবে ইনলাইনযোগ্য এবং এইভাবে "নতুন" মান পর্যবেক্ষণযোগ্য হতে পারে না

পরিশিষ্ট: বিটwise ম্যানিপুলেশন উপর

মূলত,

field.getModifiers() & ~Modifier.FINAL

field.getModifiers() থেকে field.getModifiers() সংশ্লিষ্ট বিট বন্ধ সক্রিয়। & bitwise- এবং, এবং ~ bitwise- পরিপূরক হয়।

আরো দেখুন

কনস্ট্যান্ট এক্সপ্রেশন মনে রাখবেন

এখনও এই সমাধান করতে সক্ষম হচ্ছে না, বিষণ্নতা উপর পতিত হয়েছে যেমন আমি এটা জন্য? আপনার কোড এই মত দেখায়?

public class A {
    private final String myVar = "Some Value";
}

এই উত্তরটির মন্তব্যটি পড়ুন, বিশেষ করে @ ফেমেমোর দ্বারা, এটি আমাকে মনে করিয়ে দেয় যে কনস্ট্যান্ট এক্সপ্রেশনগুলি ভিন্নভাবে পরিচালিত হয় তাই এটি সংশোধন করা অসম্ভব । তাই আপনি এই মত দেখতে আপনার কোড পরিবর্তন করতে হবে:

public class A {
    private final String myVar;

    private A() {
        myVar = "Some Value";
    }
}

যদি তুমি ক্লাসের মালিক নও ... আমি তোমাকে অনুভব করি!

কেন এই আচরণ এই পড়া সম্পর্কে আরো বিস্তারিত জানার জন্য?

আমি একটি private static final ক্ষেত্র সঙ্গে একটি বর্গ আছে, দুর্ভাগ্যবশত, আমি রান সময় পরিবর্তন করতে হবে।

প্রতিফলন ব্যবহার করে আমি এই ত্রুটিটি পেতে java.lang.IllegalAccessException: Can not set static final boolean field : java.lang.IllegalAccessException: Can not set static final boolean field

মান পরিবর্তন করার কোন উপায় আছে?

Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);

JDK 1.8u91 এ স্থাপন না হওয়া পর্যন্ত গৃহীত উত্তরটি আমার জন্য কাজ করে। তারপর আমি এটা field.set(null, newValue); ব্যর্থ field.set(null, newValue); ব্যর্থ হয়েছে field.set(null, newValue); লাইন যখন আমি setFinalStatic পদ্ধতি কলিং আগে প্রতিফলন মাধ্যমে মান পড়া ছিল।

সম্ভবত sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl জাভা প্রতিচ্ছবি অভ্যন্তরীণগুলির যেকোনোভাবে ভিন্ন সেটআপের সৃষ্টি করেছে (যেমন sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl sun.reflect.UnsafeStaticObjectFieldAccessorImpl সফলতার ক্ষেত্রে 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;
    } ...

তবে সাধারণ ক্ষেত্রে এটি যথেষ্ট হবে না।


ইন্টারভিউ প্রশ্নের একমাত্র প্রশ্নটি দেখেছেন, যদি প্রতিফলন বা রানটাইম সহ চূড়ান্ত পরিবর্তনশীল পরিবর্তন করা সম্ভব হয়। সত্যিই আগ্রহী পেয়েছি, যাতে আমি যা দিয়েছি:

 /**
 * @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();
    }
}

চূড়ান্ত স্ট্রিং পরিবর্তনশীল সঙ্গে কিছু সহজ বর্গ। সুতরাং প্রধান শ্রেণীর আমদানি java.lang.reflect.Field;

/**
 * @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


একটি final ক্ষেত্রের পুরো বিন্দু এটি সেট একবার পুনঃনির্ধারণ করা যাবে না। JVM বিভিন্ন স্থানে সামঞ্জস্য বজায় রাখার জন্য এই গারেন্টিকে ব্যবহার করে (যেমন বাইরের ভেরিয়েবলগুলি উল্লেখ করে অভ্যন্তরীণ ক্লাস)। তাই না। তাই করতে সক্ষম হচ্ছে JVM বিরতি!

সমাধান প্রথম স্থানে এটি final ঘোষণা করা হয় না।


নিরাপত্তা ব্যবস্থাপকের উপস্থিতির ক্ষেত্রে, 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"
    }
}

Lambda অভিব্যক্তি, AccessController.doPrivileged , সরলীকৃত করা যেতে পারে:

AccessController.doPrivileged((PrivilegedAction) () -> {
    modifiersField.setAccessible(true);
    return null;
});

যদি আপনার ক্ষেত্র কেবল ব্যক্তিগত হয় তবে আপনি এটি করতে পারেন:

MyClass myClass= new MyClass();
Field aField= myClass.getClass().getDeclaredField("someField");
aField.setAccessible(true);
aField.set(myClass, "newValueForAString");

এবং NoSuchFieldException নিক্ষেপ / হ্যান্ডেল


শীর্ষ স্থানান্তরিত উত্তর বরাবর আপনি একটু সহজতম পদ্ধতি ব্যবহার করতে পারেন। Apache Commons FieldUtils ক্লাস ইতিমধ্যে স্টাফ করতে পারেন যে নির্দিষ্ট পদ্ধতি আছে। দয়া করে FieldUtils.removeFinalModifier পদ্ধতিতে একটি নজর FieldUtils.removeFinalModifier । আপনি লক্ষ্য ক্ষেত্র উদাহরণ এবং অ্যাক্সেসযোগ্যতা পতাকা জোর করা উচিত (যদি আপনি অ-পাবলিক ক্ষেত্রের সাথে খেলেন)। আপনি here জানতে পারেন আরো তথ্য।






final