जावा में एक अलग वर्ग से एक निजी क्षेत्र का मूल्य कैसे पढ़ें?


481

मेरे पास 3-पार्टी में खराब तरीके से डिज़ाइन किया गया वर्ग है JARऔर मुझे इसके निजी क्षेत्रों में से एक का उपयोग करने की आवश्यकता है । उदाहरण के लिए, मुझे निजी क्षेत्र चुनने की आवश्यकता क्यों है यह आवश्यक है?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

मैं मान प्राप्त करने के लिए प्रतिबिंब का उपयोग कैसे कर सकता हूं stuffIWant?

जवाबों:


665

निजी क्षेत्रों तक पहुँचने के लिए, आपको उन्हें कक्षा के घोषित क्षेत्रों से लाना होगा और फिर उन्हें सुलभ बनाना होगा:

Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException

संपादित करें : जैसा कि aperkins द्वारा टिप्पणी की गई है , दोनों क्षेत्र को एक्सेस कर रहे हैं, इसे सुलभ के रूप में सेट कर रहे हैं और मान को पुनः प्राप्त कर सकते हैं Exceptions फेंक सकते हैं , हालांकि केवल चेक किए गए अपवाद जिन्हें आपको ध्यान में रखने की आवश्यकता है, ऊपर टिप्पणी की गई है।

NoSuchFieldExceptionयदि आप एक नाम है जो एक घोषित क्षेत्र के अनुरूप नहीं था द्वारा एक क्षेत्र के लिए कहा फेंक दिया होगा।

obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException

IllegalAccessException, फेंक दिया जाएगा अगर क्षेत्र पहुंच में नहीं था (उदाहरण के लिए अगर यह निजी है और नहीं ले पा के माध्यम से सुलभ बना दिया गया है f.setAccessible(true)लाइन।

RuntimeExceptionरों जो फेंक दिया जा सकता है या तो कर रहे हैं SecurityExceptionरों (यदि JVM के SecurityManagerआप एक क्षेत्र की पहुँच को बदलने के लिए अनुमति नहीं दी जाएगी), या IllegalArgumentExceptionरों, यदि आप क्षेत्र की क्लास के प्रकार की नहीं एक वस्तु पर क्षेत्र की कोशिश और उपयोग:

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type

4
क्या आप अपवाद टिप्पणियों की व्याख्या कर सकते हैं? कोड चलेगा, लेकिन अपवादों को फेंक देगा? या कोड अपवाद फेंक सकता है?
नीर लेवी

1
@Nir - नहीं - सभी संभावना में कोड ठीक चलेगा (डिफ़ॉल्ट SecurityManager की अनुमति देगा के रूप में खेतों 'accesibility परिवर्तित करने की) - लेकिन आप के लिए है संभाल जांचे हुए अपवादों (या तो उन्हें पकड़ने या उन्हें घोषित होने की rethrown )। मैंने अपना उत्तर थोड़ा संशोधित किया है। आपके लिए यह अच्छा हो सकता है कि आप एक छोटा सा टेस्ट केस लिखें, जिसे चारों ओर से देखें और देखें कि क्या होता है
oxbow_lakes

3
क्षमा करें, यह उत्तर मेरे लिए भ्रामक है। शायद विशिष्ट अपवाद हैंडलिंग का एक उदाहरण दिखाते हैं। ऐसा लगता है कि अपवाद तब होंगे जब कक्षाएं गलत तरीके से एक साथ तारांकित होंगी। कोड नमूना बनाता है ऐसा लगता है कि अपवादों को इसी केन्स पर फेंक दिया जाएगा।
लाफेट

2
getDeclaredField()यदि वे मूल कक्षाओं में परिभाषित किए गए हैं, तो फ़ील्ड्स नहीं खोजते हैं - आपको पेरेंट क्लास पदानुक्रम के माध्यम से पुनरावृति भी करनी होगी, getDeclaredField()प्रत्येक पर कॉल करना होगा, जब तक कि आपको एक मैच नहीं मिल जाता है (जिसके बाद आप कॉल कर सकते हैं setAccessible(true)), या आप पहुंच जाते हैं Object
ल्यूक हचिसन

1
@ आप इस तरह की पहुँच को रोकने के लिए एक सुरक्षा प्रबंधक स्थापित कर सकते हैं। जावा 9 के बाद से, इस तरह की पहुंच मॉड्यूल सीमाओं के पार काम करने के लिए नहीं है (जब तक कि एक मॉड्यूल स्पष्ट रूप से खुला नहीं है), हालांकि नए नियमों के प्रवर्तन के बारे में एक संक्रमणकालीन चरण है। इसके अलावा, प्रतिबिंब की तरह अतिरिक्त प्रयास की आवश्यकता हमेशा गैर privateक्षेत्रों के लिए एक फर्क पड़ता है । यह आकस्मिक पहुंच को रोकता है।
होल्गर

159

FieldUtilsApache commons-lang3 से कोशिश करें :

FieldUtils.readField(object, fieldName, true);

76
मुझे विश्वास है कि आप कॉमन्स-लैंग 3 से कुछ तरीकों को एक साथ जोड़कर दुनिया की अधिकांश समस्याओं को हल कर सकते हैं।
कैमरून

@yegor क्यों जावा इसे अनुमति देते हैं? मतलब जावा निजी सदस्यों तक पहुँचने की अनुमति क्यों देता है?
आसिफ मुश्ताक

1
@ yegor256 मैं अभी भी C # और C ++ निजी सदस्यों को भी एक्सेस कर सकता हूँ .. !! फिर? सभी भाषा निर्माता कमजोर हैं?
आसिफ मुश्ताक

3
@UnKnown जावा नहीं करता है। दो अलग-अलग चीजें हैं - जावा भाषा और जावा को जावा वर्चुअल मशीन। बाद वाला बाइटकोड पर काम करता है। और इसमें हेरफेर करने के लिए पुस्तकालय हैं। इसलिए जावा भाषा आपको दायरे से बाहर निजी क्षेत्रों का उपयोग करने की अनुमति नहीं देती है या अंतिम संदर्भों को म्यूट करने की अनुमति नहीं देती है। लेकिन ऐसा करना संभव है कि उपकरण का उपयोग करना। जो सिर्फ मानक पुस्तकालय के अंदर होता है।
evgenii

3
@UnKnown में से एक कारण यह है कि जावा के पास केवल डीआई, ओआरएम या एक्सएमएल या जेएसएन सीरियलाइज़र जैसे बुनियादी ढाँचे द्वारा क्षेत्र तक पहुँच को सक्षम करने के लिए "मित्र" की पहुँच नहीं है। इस तरह के ढांचे को किसी वस्तु की आंतरिक स्थिति को सही ढंग से क्रमबद्ध या आरंभ करने के लिए ऑब्जेक्ट फ़ील्ड तक पहुंच की आवश्यकता होती है, लेकिन आपको अभी भी अपने व्यापार तर्क के लिए उचित संकलन-समय एनकैप्सुलेशन प्रवर्तन की आवश्यकता हो सकती है।
इवान गैमेल

26

प्रतिबिंब आपके मुद्दे को हल करने का एकमात्र तरीका नहीं है (जो किसी वर्ग / घटक के निजी कार्यक्षमता / व्यवहार तक पहुंचना है)

एक वैकल्पिक समाधान है कि .jar से कक्षा को निकाला जाए, इसका उपयोग करते हुए विघटित करें (कहते हैं) Jode या Jad , फ़ील्ड को बदलें (या एक एक्सेसर जोड़ें), और इसे मूल .jar के विरुद्ध पुनः स्थापित करें। फिर नए .class .jarको classpath के आगे रखें , या इसे पुन: स्थापित करें .jar। (जार उपयोगिता आपको मौजूदा .jar को निकालने और पुन: स्थापित करने की अनुमति देती है)

जैसा कि नीचे उल्लेख किया गया है, यह केवल एक क्षेत्र तक पहुँचने / बदलने के बजाय निजी राज्य तक पहुँचने / बदलने के व्यापक मुद्दे को हल करता है।

इसके लिए .jarहस्ताक्षर करने की आवश्यकता नहीं है, निश्चित रूप से।


36
यह तरीका सिर्फ एक साधारण क्षेत्र के लिए काफी दर्दनाक होगा।
वैलेंटाइन रोचर

1
मैं असहमत हूं। यह आपको न केवल अपने क्षेत्र तक पहुंचने की अनुमति देता है, बल्कि यदि आवश्यक हो तो क्षेत्र को बदलने के लिए वर्ग को बदलने के लिए भी पर्याप्त नहीं है।
ब्रायन एग्न्यू

4
फिर आपको फिर से करना होगा। यदि जार को अपडेट मिलता है और आप किसी ऐसे क्षेत्र के लिए प्रतिबिंब का उपयोग करते हैं जो अब मौजूद नहीं है तो क्या होगा? यह वास्तव में एक ही मुद्दा है। आपको बस इसे मैनेज करना है।
ब्रायन एग्न्यू

4
मैं चकित हूं कि इसे कितना दिया जाता है क्योंकि यह एक व्यावहारिक विकल्प के रूप में हाइलाइट किया गया है) यह ऐसे परिदृश्यों के लिए पूरा करता है जिसमें किसी क्षेत्र की दृश्यता को बदलना पर्याप्त नहीं है
ब्रायन एग्न्यू

2
@BrianAgnew शायद यह सिर्फ शब्दार्थ है लेकिन अगर हम सवाल (निजी क्षेत्र को पढ़ने के लिए प्रतिबिंब का उपयोग करते हुए) से चिपके रहते हैं, तो प्रतिबिंब का उपयोग नहीं करना एक आत्म विरोधाभास है। लेकिन मैं मानता हूं कि आप क्षेत्र तक पहुंच प्रदान करते हैं ... लेकिन अभी भी क्षेत्र निजी नहीं है, इसलिए फिर से हम प्रश्न के "एक निजी क्षेत्र को पढ़ने" के लिए छड़ी नहीं करते हैं। दूसरे कोण से, .jar संशोधन कुछ मामले में काम नहीं कर सकता (हस्ताक्षरित जार), हर बार जार को अपडेट करने की आवश्यकता होती है, क्लासपैथ के सावधानीपूर्वक हेरफेर की आवश्यकता होती है (जिसे आप पूरी तरह से नियंत्रित नहीं कर सकते हैं यदि आप एक निष्पादन में हैं आवेदन कंटेनर) आदि
रेमी मोरिन

18

एक अन्य विकल्प जिसका अभी तक उल्लेख नहीं किया गया है: ग्रूवी का उपयोग करें । ग्रूवी आपको भाषा के डिजाइन के साइड इफेक्ट के रूप में निजी इंस्टेंस चर का उपयोग करने की अनुमति देता है। चाहे आपके पास क्षेत्र के लिए कोई गटर हो या न हो, आप बस उपयोग कर सकते हैं

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()

4
ओपी ने विशेष रूप से जावा के लिए कहा
जोहान

3
कई जावा प्रोजेक्ट्स आज ग्रूवी को शामिल करते हैं। यह पर्याप्त है कि परियोजना स्प्रिंग के ग्रूवी डीएसएल का उपयोग करती है और उदाहरण के लिए वे क्लासपाथ पर ग्रूवी होंगे। किस मामले में यह उत्तर उपयोगी है, और ओपी को सीधे जवाब नहीं देते समय, यह कई आगंतुकों के लिए लाभकारी होगा।
आमिर अबीरी

10

जावा में परावर्तन का उपयोग करके आप private/publicएक वर्ग के सभी क्षेत्रों और विधियों का उपयोग दूसरे में कर सकते हैं। लेकिन ओरेकल प्रलेखन के अनुसार खंड की कमियों में वे अनुशंसा करते हैं:

"चूंकि प्रतिबिंब गैर-चिंतनशील कोड में उन कार्यों को करने की अनुमति देता है जो गैर-चिंतनशील कोड में गैरकानूनी होंगे, जैसे कि निजी क्षेत्रों और विधियों तक पहुंच, प्रतिबिंब के उपयोग से अप्रत्याशित दुष्प्रभाव हो सकते हैं, जो कोड की शिथिलता को प्रस्तुत कर सकते हैं और पोर्टेबिलिटी कोड को नष्ट कर सकते हैं। अमूर्त को तोड़ता है और इसलिए मंच के उन्नयन के साथ व्यवहार बदल सकता है "

यहाँ प्रतिबिंब के बुनियादी अवधारणाओं को प्रदर्शित करने के लिए कोड स्नैप का अनुसरण किया जा रहा है

Reflection1.java

public class Reflection1{

    private int i = 10;

    public void methoda()
    {

        System.out.println("method1");
    }
    public void methodb()
    {

        System.out.println("method2");
    }
    public void methodc()
    {

        System.out.println("method3");
    }

}

Reflection2.java

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class Reflection2{

    public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Method[] mthd = Reflection1.class.getMethods(); // for axis the methods 

        Field[] fld = Reflection1.class.getDeclaredFields();  // for axis the fields  

        // Loop for get all the methods in class
        for(Method mthd1:mthd)
        {

            System.out.println("method :"+mthd1.getName());
            System.out.println("parametes :"+mthd1.getReturnType());
        }

        // Loop for get all the Field in class
        for(Field fld1:fld)
        {
            fld1.setAccessible(true);
            System.out.println("field :"+fld1.getName());
            System.out.println("type :"+fld1.getType());
            System.out.println("value :"+fld1.getInt(new Reflaction1()));
        }
    }

}

आशा है कि यह मदद करेगा।


5

जैसा कि oxbow_lakes उल्लेख करता है, आप एक्सेस प्रतिबंधों के आसपास पाने के लिए प्रतिबिंब का उपयोग कर सकते हैं (यह मानते हुए कि आपका SecurityManager आपको जाने देगा)।

उस ने कहा, अगर यह वर्ग इतनी बुरी तरह से डिज़ाइन किया गया है कि यह आपको इस तरह के हैकरी का सहारा लेता है, तो शायद आपको एक विकल्प की तलाश करनी चाहिए। यकीन है कि यह छोटी सी हैक अब आपको कुछ घंटों के लिए बचा सकती है, लेकिन सड़क के नीचे आपको कितना खर्च आएगा?


3
मैं वास्तव में इससे भाग्यशाली हूं, मैं इस कोड का उपयोग कुछ डेटा निकालने के लिए कर रहा हूं, इसके बाद मैं इसे रीसायकल बिन में वापस टॉस कर सकता हूं।
फ्रैंक क्रुएगर

2
खैर उस मामले में, दूर हैक। :-)
लारेंस गोन्साल्वेस

3
यह प्रश्न का उत्तर प्रदान नहीं करता है। किसी लेखक से स्पष्टीकरण मांगने या उसका अनुरोध करने के लिए, उनके पोस्ट के नीचे एक टिप्पणी छोड़ दें।
मूरिनिक

@ मुरीनीक - यह प्रश्न का उत्तर देता है, "आप प्रतिबिंब का उपयोग कर सकते हैं" शब्दों के साथ। यह एक उदाहरण या किसी भी अधिक स्पष्टीकरण या कैसे का अभाव है, लेकिन यह एक उत्तर है। अगर आपको यह पसंद नहीं है तो इसे डाउनवोट करें।
आर्टऑफवर्फ

4

बायटकोड को सीधे संशोधित करने के लिए Soot Java ऑप्टिमाइज़ेशन फ्रेमवर्क का उपयोग करें। http://www.sable.mcgill.ca/soot/

सूट पूरी तरह से जावा में लिखा गया है और नए जावा संस्करणों के साथ काम करता है।


3

आपको निम्नलिखित करने की आवश्यकता है:

private static Field getField(Class<?> cls, String fieldName) {
    for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
        try {
            final Field field = c.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (final NoSuchFieldException e) {
            // Try parent
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Cannot access field " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Cannot find field " + cls.getName() + "." + fieldName);
}

2

यदि स्प्रिंग का उपयोग किया जाता है, तो ReflectionTestUtils कुछ आसान उपकरण प्रदान करता है जो न्यूनतम प्रयास के साथ यहां मदद करते हैं। इसे "इकाई और एकीकरण परीक्षण परिदृश्यों में उपयोग के लिए" के रूप में वर्णित किया गया है । इसी तरह का एक वर्ग भी है जिसका नाम ReflectionUtils है लेकिन इसे "केवल आंतरिक उपयोग के लिए अभिप्रेत है" के रूप में वर्णित किया गया है - इसका उत्तर इस अर्थ की व्याख्या के लिए देखें ।

पोस्ट किए गए उदाहरण को संबोधित करने के लिए:

Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");

1
यदि आप स्प्रिंग द्वारा यूटिल्स क्लास का उपयोग करने का निर्णय लेते हैं, तो आपको वास्तव में नॉन-टेस्ट वन ( docs.spring.io/spring-framework/docs/current/javadoc-api/org/// ) के बजाय इसका उपयोग करना चाहिए , जब तक कि निश्चित रूप से, आप वास्तव में इकाई परीक्षणों के लिए इसका उपयोग कर रहे हैं।
सिंक

संभवतः, हालांकि उस वर्ग को "केवल आंतरिक उपयोग के लिए इरादा" के रूप में वर्णित किया गया है। मैंने इस बारे में उत्तर के लिए कुछ जानकारी जोड़ी है। (उदाहरण का उपयोग करके ReflectionTestUtilsमैंने अपने अनुभव के रूप में देखा है कि मुझे केवल एक परीक्षण परिस्थिति में इस तरह का काम करने की आवश्यकता है।)
स्टीव चैम्बर्स

1

प्रतिबिंब के बारे में बस एक अतिरिक्त नोट: मैंने कुछ विशेष मामलों में देखा है, जब एक ही नाम के साथ कई कक्षाएं अलग-अलग पैकेजों में मौजूद हैं, तो शीर्ष उत्तर में उपयोग किया गया प्रतिबिंब ऑब्जेक्ट से सही वर्ग लेने में विफल हो सकता है। इसलिए यदि आप जानते हैं कि ऑब्जेक्ट का पैकेज.क्लास क्या है, तो इसके निजी क्षेत्र मानों तक पहुंचना बेहतर है:

org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);

(यह उदाहरण वर्ग है जो मेरे लिए काम नहीं कर रहा था)


1

आप प्रत्यक्ष, प्रकार-सुरक्षित जावा प्रतिबिंब के लिए मैनिफोल्ड के @ जैलब्रेक का उपयोग कर सकते हैं :

@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;

public class Foo {
    private String stuffIWant;
}

@JailBreakfooसंकलक के Fooपदानुक्रम में सभी सदस्यों तक सीधी पहुँच के लिए स्थानीय चर को अनलॉक करता है ।

इसी प्रकार आप एकबारगी उपयोग के लिए जेलब्रेक () एक्सटेंशन विधि का उपयोग कर सकते हैं:

foo.jailbreak().stuffIWant = "123";

jailbreak()विधि के माध्यम से आप किसी भी सदस्य के Fooपदानुक्रम में पहुँच सकते हैं।

दोनों ही मामलों में कंपाइलर आपके लिए टाइप-सेफ़ के लिए फ़ील्ड एक्सेस को हल करता है, जैसे कि एक सार्वजनिक फ़ील्ड, जबकि मैनिफोल्ड हुड के तहत आपके लिए कुशल प्रतिबिंब कोड उत्पन्न करता है।

कई गुना के बारे में पता चलता है ।


1

यह XrayInterface टूल के साथ काफी आसान है । बस लापता गेटर्स / सेटर को परिभाषित करें, जैसे

interface BetterDesigned {
  Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}

और अपने खराब डिज़ाइन किए गए प्रोजेक्ट को एक्सरे करें:

IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());

आंतरिक रूप से यह प्रतिबिंब पर निर्भर करता है।


0

मामले के लिए समस्या के चारों ओर जाने की कोशिश करें, जिस डेटा को आप सेट / प्राप्त करना चाहते हैं, वह आपकी अपनी कक्षाओं में से एक है।

बस एक public setter(Field f, Object value)और उसके public Object getter(Field f)लिए बनाएं । तुम भी अपने स्वयं के theses सदस्य कार्यों के आधार पर कुछ गोपनीयता जांच कर सकते हैं। सेटर के लिए:

class myClassName {
    private String aString;

    public set(Field field, Object value) {
        // (A) do some checkings here  for security

        // (B) set the value
        field.set(this, value);
    }
}

बेशक, अब आप पता लगाने के लिए है java.lang.reflect.Fieldके लिए sStringफ़ील्ड मान की स्थापना के करने से पहले।

मैं इस तकनीक का उपयोग एक सामान्य परिणाम-से-और-मॉडल-मैपर में करता हूं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.