जावा: "अंतिम" System.out, System.in और System.err?


80

System.outके रूप में घोषित किया गया है public static final PrintStream out

लेकिन आप System.setOut()इसे पुन: असाइन करने के लिए कॉल कर सकते हैं ।

हुह? यदि यह संभव है तो यह कैसे संभव है final?

(यही बात लागू होती है System.inऔर System.err)

और अधिक महत्वपूर्ण बात, यदि आप सार्वजनिक स्थैतिक अंतिम क्षेत्रों को म्यूट कर सकते हैं, तो इसका क्या मतलब है जहाँ तक आपको गारंटी देता है (यदि कोई है) जो finalआपको देता है? (मैंने महसूस किया है और न ही उम्मीद कभी नहीं System.in/out/err के रूप में व्यवहार किया finalचर)


3
अंतिम क्षेत्र JVM द्वारा बहुत सारे लाभों का आनंद नहीं लेते हैं, हालांकि वे सख्ती से जाँच करते हैं। अंतिम फ़ील्ड को संशोधित करने के तरीके हैं, लेकिन मानक जावा कोड के माध्यम से नहीं (क्योंकि यह सत्यापनकर्ता का विषय है)। यह असुरक्षित के माध्यम से किया जाता है और फील्ड.सेट (सुलभ सत्य की आवश्यकता) के माध्यम से जावा में उजागर होता है, जो कि असुरक्षित सामान का संकलन करता है। इसके अलावा JNI भी कर सकता है, इसलिए JVM अनुकूलन के प्रयास में इतनी उत्सुक नहीं है ... {शायद मुझे उत्तर के रूप में टिप्पणी की संरचना करनी चाहिए, लेकिन meh}
bestsss

जवाबों:


57

JLS 17.5.4 संरक्षित क्षेत्र लिखें :

आम तौर पर, अंतिम स्थिर क्षेत्रों को संशोधित नहीं किया जा सकता है। हालाँकि System.in, System.outऔर System.errअंतिम स्थिर क्षेत्र हैं, जो विरासत के कारणों के लिए, विधियों द्वारा बदलने की अनुमति दी जानी चाहिए System.setIn, System.setOutऔर System.setErr। हम इन क्षेत्रों को साधारण अंतिम क्षेत्रों से अलग करने के लिए लिखने-संरक्षित होने के रूप में संदर्भित करते हैं।

संकलक को इन क्षेत्रों को अन्य अंतिम क्षेत्रों से अलग तरीके से व्यवहार करने की आवश्यकता है। उदाहरण के लिए, एक साधारण अंतिम क्षेत्र का एक पाठ सिंक्रनाइज़ेशन के लिए "प्रतिरक्षा" है: एक लॉक या वाष्पशील रीड में शामिल बाधा को अंतिम फ़ील्ड से क्या मूल्य पढ़ा जाता है, इसे प्रभावित करने की आवश्यकता नहीं है। चूँकि लेखन-संरक्षित क्षेत्रों के मूल्य में परिवर्तन देखा जा सकता है, इसलिए सिंक्रनाइज़ेशन घटनाओं का उन पर प्रभाव होना चाहिए। इसलिए, शब्दार्थ यह निर्धारित करता है कि इन क्षेत्रों को सामान्य फ़ील्ड के रूप में माना जाता है जिसे उपयोगकर्ता कोड द्वारा परिवर्तित नहीं किया जा सकता, जब तक कि उपयोगकर्ता कोड Systemकक्षा में न हो।

वैसे, वास्तव में आप उन पर finalकॉल setAccessible(true)करके (या Unsafeविधियों का उपयोग करके ) प्रतिबिंब के माध्यम से फ़ील्ड को म्यूट कर सकते हैं । इस तरह की तकनीकों का उपयोग डीबेराइजेशन के दौरान, हाइबरनेट और अन्य रूपरेखाओं आदि द्वारा किया जाता है, लेकिन उनकी एक सीमा है: कोड जिसमें संशोधन से पहले अंतिम क्षेत्र का मूल्य देखा गया है, संशोधन के बाद नए मूल्य को देखने की गारंटी नहीं है। विचाराधीन क्षेत्रों के बारे में विशेष बात यह है कि वे संकलक द्वारा विशेष तरीके से व्यवहार किए जाने के बाद से इस सीमा से मुक्त हैं।


4
भविष्य के डिजाइन से समझौता करने वाले प्यारे तरीके के लिए FSM आशीर्वाद विरासत कोड हो सकता है!
स्लेज

1
>> इस तकनीक अक्रमांकन दौरान प्रयोग किया जाता है << यह अब सच नहीं है, desereliazation का उपयोग करता है असुरक्षित (तेज)
bestsss

1
यह समझना महत्वपूर्ण है कि setAccessible(true)केवल गैर- staticफ़ील्ड के लिए काम करता है , जो इसे डीरियलाइज़ेशन या क्लोनिंग कोड की मदद करने के कार्य के लिए उपयुक्त बनाता है, लेकिन static finalफ़ील्ड को बदलने का कोई तरीका नहीं है । इसीलिए उद्धृत पाठ " सामान्य रूप से, अंतिम स्थिर क्षेत्रों को संशोधित नहीं किया जा सकता है " के साथ शुरू होता है ,final static इन क्षेत्रों और तीन अपवादों की प्रकृति का उल्लेख करते हुए । उदाहरण के क्षेत्रों का मामला दूसरी जगह चर्चा में है।
होल्गर

मुझे आश्चर्य है कि उन्होंने केवल finalसंशोधक को क्यों नहीं लिया ; यह सब "लिखने-संरक्षित" सामान की तुलना में सरल लगता है। मुझे पूरा यकीन है कि यह बदलाव नहीं है।
मार्क VY

@ होलजर स्थिर अंतिम फ़ील्ड (जावा 8 तक) को बदलने का एक तरीका है। सार्वजनिक वर्ग OnePrinter {निजी स्थिर अंतिम पूर्णांक एक = 1; सार्वजनिक स्थैतिक शून्य प्रिंटऑन () {System.out.println (ONE); }} और उसके बाद आप फ़ील्ड z = OnePrinter.class.getDeclaredField ("ONE") कर सकते हैं; z.setAccessible (सत्य); फ़ील्ड f = Field.class.getDeclaredField ("संशोधक"); int modifiers = z.getModifiers (); f.setAccessible (सत्य); f.set (z, संशोधक और ~ संशोधक। FINAL); z.set (अशक्त, 2); OnePrinter.printOne ();
पीटर वेरहास

30

जावा लागू करने के लिए एक देशी विधि का उपयोग करता है setIn(), setOut()और setErr()

मेरे JDK1.6.0_20 पर, setOut()इस तरह दिखता है:

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}

...

private static native void setOut0(PrintStream out);

आप अभी भी सामान्य रूप से " finalचर " को पुन: असाइन नहीं कर सकते हैं , और इस मामले में भी, आप सीधे क्षेत्र को पुन: असाइन नहीं कर रहे हैं (अर्थात आप अभी भी " System.out = myOut" संकलित नहीं कर सकते हैं )। मूल तरीकों से कुछ चीजें हैं जो आप बस नियमित रूप से जावा में नहीं कर सकते हैं, जो बताता है कि देशी तरीकों के साथ प्रतिबंध क्यों हैं जैसे कि आवश्यकता है कि मूल पुस्तकालयों का उपयोग करने के लिए एक एपलेट पर हस्ताक्षर किए जाएं।


1
ठीक है, इसलिए यह शुद्ध जावा शब्दार्थ के चारों ओर एक पिछला दरवाजा है ... क्या आप मेरे द्वारा जोड़े गए प्रश्न के भाग का उत्तर दे सकते हैं, अर्थात् यदि आप धाराओं को पुन: सौंप सकते हैं, तो क्या finalवास्तव में यहाँ कोई अर्थ है?
जेसन एस

1
यह शायद अंतिम है ताकि कोई System.out = new SomeOtherImp () जैसा कुछ न कर सके। लेकिन आप अभी भी मूल दृष्टिकोण का उपयोग करके बसने वालों का उपयोग कर सकते हैं जैसा कि आप ऊपर देखते हैं।
अमीर रामिनफर

मुझे लगता है कि इस मामले में देशी setIn0 और setOut0 विधियों के लिए कॉल वास्तव में एक अंतिम चर के मूल्य को संशोधित करेंगे, देशी तरीके शायद ऐसा कर सकते हैं ... यह खेल में धोखा कोड का उपयोग करने की तरह है: S
Danilo Tommasina

@Danilo, हाँ इसे संशोधित करता है :)
bestsss

@ जेसन, इसे सेट करने में अक्षमता के लिए सेट / कॉल सेट करते समय सुरक्षा जांच की आवश्यकता होती है। सभी निष्पक्ष और वर्ग। उदाहरण जहां जावा अंतिम क्षेत्रों को संशोधित करता है: java.util.Random (क्षेत्र बीज)
bestsss

7

एडम ने जो कहा, उस पर विस्तार करने के लिए, यहाँ निहित है:

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}

और setOut0 को इस प्रकार परिभाषित किया गया है:

private static native void setOut0(PrintStream out);

6

कार्यान्वयन पर निर्भर करता है। अंतिम कोई कभी नहीं बदल सकता है लेकिन यह वास्तविक आउटपुट स्ट्रीम के लिए एक प्रॉक्सी / एडेप्टर / डेकोरेटर हो सकता है, उदाहरण के लिए सेटऑउट एक सदस्य सेट कर सकता है जो वास्तव में बाहर का सदस्य लिखता है। हालांकि यह मूल रूप से निर्धारित किया जाता है।


1

outजो प्रणाली कक्षा में अंतिम के रूप में घोषित किया गया है एक वर्ग स्तर चर रहा है। जहां से नीचे की विधि में जो है वह स्थानीय चर है। हम वह नहीं हैं जहाँ कक्षा स्तर को पास किया जा रहा है जो वास्तव में इस पद्धति में अंतिम है

public static void setOut(PrintStream out) {
  checkIO();
  setOut0(out);
    }

उपरोक्त विधि का उपयोग निम्नानुसार है:

System.setOut(new PrintStream(new FileOutputStream("somefile.txt")));

अब डेटा को फ़ाइल में बदल दिया जाएगा। आशा है कि यह स्पष्टीकरण समझ में आता है।

तो अंतिम खोजशब्द के बदलते उद्देश्य में यहाँ देशी तरीकों या प्रतिबिंबों की कोई भूमिका नहीं है।


1
setOut0 वर्ग चर को संशोधित कर रहा है, जो अंतिम है।
fgb

0

जहाँ तक कैसे, हम स्रोत कोड पर एक नज़र डाल सकते हैं java/lang/System.c:

/*
 * The following three functions implement setter methods for
 * java.lang.System.{in, out, err}. They are natively implemented
 * because they violate the semantics of the language (i.e. set final
 * variable).
 */
JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

...

दूसरे शब्दों में, जेएनआई "धोखा" दे सकता है। ; )


-2

मुझे लगता setout0है कि यह स्थानीय स्तर के चर को संशोधित कर रहा है out, यह वर्ग स्तर चर को संशोधित नहीं कर सकता है out

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