क्या जावा अंतिम चर में डिफ़ॉल्ट मान होंगे?


81

मेरा कार्यक्रम इस तरह है:

class Test {

    final int x;

    {
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

अगर मैं इसे निष्पादित करने की कोशिश करता हूं, तो मुझे संकलक त्रुटि मिल रही है: variable x might not have been initializedजावा डिफ़ॉल्ट मानों के आधार पर मुझे नीचे आउटपुट सही मिलना चाहिए ??

"Here x is 0".

क्या अंतिम चर में dafault मान होंगे?

अगर मैं अपना कोड इस तरह बदलूं,

class Test {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

मुझे आउटपुट मिल रहा है:

Here x is 0                                                                                      
Here x is 7                                                                                     
const called

किसी को भी इस व्यवहार की व्याख्या कर सकते हैं ..

जवाबों:


62

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html , अध्याय "प्रारंभिक सदस्यों को प्रारंभ करना":

जावा कंपाइलर हर कंस्ट्रक्टर में इनिशियलाइज़र ब्लॉक्स को कॉपी करता है।

यानी:

{
    printX();
}

Test() {
    System.out.println("const called");
}

बिल्कुल जैसा व्यवहार करता है:

Test() {
    printX();
    System.out.println("const called");
}

जैसा कि आप देख सकते हैं, एक बार एक उदाहरण बन जाने के बाद, अंतिम फ़ील्ड निश्चित रूप से असाइन नहीं की गई है , जबकि ( http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html से # jls-8.3.1.2 ):

एक रिक्त अंतिम उदाहरण चर को निश्चित रूप से उस वर्ग के प्रत्येक निर्माता के अंत में सौंपा जाना चाहिए जिसमें यह घोषित किया गया है; अन्यथा एक संकलन-समय त्रुटि उत्पन्न होती है।

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

डिफ़ॉल्ट मान: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

आपके दूसरे स्निपेट पर, x का निर्माण उदाहरण पर किया जाता है, इसलिए कंपाइलर शिकायत नहीं करता है:

Test() {
    printX();
    x = 7;
    printX();
    System.out.println("const called");
}

यह भी ध्यान दें कि निम्नलिखित दृष्टिकोण काम नहीं करता है। अंतिम चर के डिफ़ॉल्ट मान का उपयोग केवल एक विधि के माध्यम से करने की अनुमति है।

Test() {
    System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
    x = 7;
    System.out.println("Here x is " + x);
    System.out.println("const called");
}

1
यह ध्यान देने योग्य हो सकता है जहां सुपर में () में निहित (या स्पष्ट) कॉल आपके उदाहरणों में से एक में जाता है।
पैट्रिक

2
यह जवाब नहीं देता है कि अंतिम फ़ील्ड को प्रारंभ करने के लिए संकलन त्रुटि क्यों नहीं होती है।
justhalf

@ sp00m अच्छा संदर्भ - मैं इसे बैंक में रखूंगा।
बोहेमियन

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

28

JLS है कह है कि आप चाहिए निर्माता में खाली अंतिम उदाहरण चर के लिए डिफ़ॉल्ट मान असाइन (या में प्रारंभ ब्लॉक सुंदर एक ही है)। यही कारण है कि आपको पहले मामले में त्रुटि मिलती है। हालांकि यह नहीं कहता है कि आप इसे पहले कंस्ट्रक्टर में एक्सेस नहीं कर सकते। थोड़ा अजीब लगता है, लेकिन आप इसे असाइनमेंट से पहले एक्सेस कर सकते हैं और int - 0 के लिए डिफ़ॉल्ट मान देख सकते हैं।

यूपीडी जैसा कि @ I4mpi द्वारा उल्लेख किया गया है, जेएलएस नियम को परिभाषित करता है कि प्रत्येक मूल्य निश्चित रूप से सौंपा जाना चाहिए किसी भी पहुंच से पहले :

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

हालाँकि, इसका एक दिलचस्प नियम भी है निर्माण और क्षेत्रों के संबंध में है:

If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.

तो दूसरे मामले में मूल्य xहै निश्चित रूप से सौंपा , निर्माता की शुरुआत में, क्योंकि यह यह के अंत में काम होता है।


वास्तव में, यह कहता है कि आप असाइनमेंट से पहले इसे एक्सेस नहीं कर सकते हैं : "प्रत्येक स्थानीय चर (.414.4) और प्रत्येक रिक्त अंतिम फ़ील्ड (field4.12.4, .38.3.1.2) के पास निश्चित रूप से असाइन किया गया मान होना चाहिए जब इसके मूल्य की कोई भी पहुंच होती है "
l4mpi

1
यह "निश्चित रूप से सौंपा गया" होना चाहिए, हालांकि इस नियम का निर्माण के मामले में अजीब व्यवहार है, मैंने जवाब अपडेट किया है
udalmik

यदि कोड की एक विधि है, जो कुछ जटिल स्थितियों के आधार पर, finalफ़ील्ड को पढ़ सकती है या नहीं कर सकती है , और यदि फ़ील्ड लिखे जाने से पहले और बाद में उस कोड को चलाया जा सकता है, तो सामान्य मामले में एक संकलक का कोई तरीका नहीं होगा यह जानने से पहले कि क्या यह वास्तव में लिखा जाने से पहले क्षेत्र को पढ़ेगा।
Supercat

7

यदि आप इनिशियलाइज़ नहीं करते हैं xतो आपको एक कंपाइल-टाइम एरर मिलेगा क्योंकि xकभी इनिशियलाइज़ नहीं हुआ है।

xअंतिम के रूप में घोषित करने का मतलब है कि इसे केवल कंस्ट्रक्टर या इनिशियलाइज़र-ब्लॉक में आरंभीकृत किया जा सकता है (क्योंकि यह ब्लॉक कंपाइलर द्वारा प्रत्येक कंस्ट्रक्टर में कॉपी किया जाएगा)।

0वैरिएबल के आरंभ होने से पहले आपके द्वारा प्रिंट किया गया कारण मैनुअल में परिभाषित व्यवहार के कारण होता है (देखें: "डिफ़ॉल्ट मान) अनुभाग:

डिफ़ॉल्ट मान

फ़ील्ड घोषित होने पर हमेशा मान निर्दिष्ट करना आवश्यक नहीं होता है। घोषित किए गए फ़ील्ड, लेकिन आरंभिक नहीं, कंपाइलर द्वारा एक उचित डिफ़ॉल्ट पर सेट किए जाएंगे। सामान्यतया, यह डिफ़ॉल्ट डेटा प्रकार के आधार पर शून्य या शून्य होगा। ऐसे डिफ़ॉल्ट मूल्यों पर भरोसा करना, हालांकि, आमतौर पर खराब प्रोग्रामिंग शैली माना जाता है।

निम्न चार्ट उपरोक्त डेटा प्रकारों के लिए डिफ़ॉल्ट मानों को सारांशित करता है।

Data Type   Default Value (for fields)
--------------------------------------
byte        0
short       0
int         0
long        0L
float       0.0f
double      0.0d
char        '\u0000'
String (or any object)      null
boolean     false

4

पहली त्रुटि कंपाइलर की शिकायत है कि आपके पास एक अंतिम फ़ील्ड है, लेकिन इसे शुरू करने के लिए कोई कोड नहीं है - सरल पर्याप्त।

दूसरे उदाहरण में, आपके पास इसे एक मान निर्दिष्ट करने के लिए कोड है, लेकिन निष्पादन के अनुक्रम का अर्थ है कि आप इसे निर्दिष्ट करने से पहले और बाद में दोनों को संदर्भित करते हैं।

किसी भी क्षेत्र का पूर्व-नियत मान डिफ़ॉल्ट मान है।


2

एक वर्ग के सभी गैर-अंतिम फ़ील्ड एक डिफ़ॉल्ट मान ( 0संख्यात्मक डेटा प्रकारों के लिए, falseबूलियन के लिए और nullसंदर्भ प्रकारों के लिए, कभी-कभी जटिल ऑब्जेक्ट कहलाते हैं) को प्रारंभ करते हैं । ये क्षेत्र एक निर्माणकर्ता (या उदाहरण आरंभीकरण ब्लॉक) से पहले आरंभीकृत करते हैं, जो निर्माणकर्ता के पहले या बाद में घोषित किया गया था कि खेतों को स्वतंत्र किया गया है या नहीं।

किसी वर्ग के अंतिम फ़ील्ड का कोई डिफ़ॉल्ट मान नहीं है और क्लास कंस्ट्रक्टर द्वारा अपना काम पूरा करने से ठीक पहले एक बार स्पष्ट रूप से आरंभ किया जाना चाहिए।

निष्पादन खंड के अंदर के स्थानीय चर (उदाहरण के लिए, एक विधि) का कोई डिफ़ॉल्ट मान नहीं है। इन क्षेत्रों को उनके पहले उपयोग से पहले स्पष्ट रूप से आरंभ किया जाना चाहिए और इससे कोई फर्क नहीं पड़ता कि स्थानीय चर को अंतिम रूप में चिह्नित किया गया है या नहीं।


1

मैं इसे सबसे सरल शब्दों में रख सकता हूं जो मैं कर सकता हूं।

finalचर को आरंभ करने की आवश्यकता है, यह भाषा विनिर्देश द्वारा अनिवार्य है। यह कहते हुए कि, कृपया ध्यान दें कि घोषणा के समय इसे शुरू करना आवश्यक नहीं है।

यह आवश्यक है कि ऑब्जेक्ट को इनिशियलाइज़ करने से पहले उसे इनिशियलाइज़ किया जाए।

हम अंतिम वैरिएबल को इनिशियलाइज़ करने के लिए इनिलाइज़र ब्लॉक का उपयोग कर सकते हैं। अब, इनिलाइज़र ब्लॉक दो प्रकार के होते हैं staticऔरnon-static

आपके द्वारा उपयोग किया गया ब्लॉक एक गैर-स्टैटिक इनिशियलाइज़र ब्लॉक है। इसलिए, जब आप कोई ऑब्जेक्ट बनाते हैं, तो रनटाइम कंस्ट्रक्टर को आमंत्रित करेगा और जो बदले में मूल वर्ग के कंस्ट्रक्टर को आमंत्रित करेगा।

उसके बाद, यह सभी इनिशियलाइज़र (आपके मामले में नॉन-स्टैटिक इनिशियलाइज़र) को इनवॉइस करेगा।

आपके प्रश्न में, केस 1 : इनिशियलाइज़र ब्लॉक पूरा होने के बाद भी अंतिम वैरिएबल अन-इनिशियलाइज़्ड रहता है, जो कि एक एरर कंपाइलर का पता लगाएगा।

में मामला 2 : प्रारंभकर्ता अंतिम चर प्रारंभ हो जाएगा, इसलिए संकलक जानता है कि इससे पहले कि वस्तु आरंभ नहीं हो जाता, अंतिम पहले से ही आरंभ नहीं हो जाता। इसलिए, यह शिकायत नहीं करेगा।

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

यहाँ छवि विवरण दर्ज करें


1

जहां तक ​​मुझे जानकारी है, कंपाइलर हमेशा क्लास वेरिएबल्स को डिफॉल्ट वैल्यूज (यहां तक ​​कि फाइनल वेरिएबल) भी इनिशियलाइज करेगा। उदाहरण के लिए, यदि आप किसी इंट को खुद को इनिशियलाइज़ करना चाहते हैं, तो इंट 0. के डिफ़ॉल्ट पर सेट हो जाएगा: नीचे देखें:

class Test {
    final int x;

    {
        printX();
        x = this.x;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }
}

ऊपर वाला निम्नलिखित छापेगा:

Here x is 0
Here x is 0
const called

1
अंतिम चर x ओपी के कोड में स्थिर नहीं है।
जेम्सबी

मैं आसानी से इस कोड को प्रारंभ करने के लिए आसानी से ओपी के कोड को संशोधित कर सकता हूं और यही बात होगी। इससे कोई फर्क नहीं पड़ता कि यह स्थिर है या नहीं।
माइकल डी।

मैं आपको सुझाव दूंगा कि आप यहां स्थिर सामग्री को हटा दें क्योंकि ऐसा लगता है कि आपने ओपी के प्रश्न को नहीं पढ़ा है।
जेम्स बी बी

क्या यह मदद करता है अगर मैं ओपी के कोड से आधारभूत करता हूं? जैसा कि मैंने कहा, इससे कोई फर्क नहीं पड़ता कि चर स्थिर है या नहीं। मेरा कहना यह है कि किसी चर को अपने आप में आरोपित करना और डिफ़ॉल्ट मान प्राप्त करने का अर्थ है कि स्पष्ट रूप से आरंभिक होने से पहले चर का प्रारंभिक रूप से प्रारंभिक रूप से आरंभ हो जाना।
माइकल डी।

1
यह संकलित नहीं करता है, क्योंकि आप लाइन पर शुरू होने से पहले एक अंतिम चर (सीधे) का उपयोग करने का प्रयास कर रहे हैं, लाइन 6. पर
Luca

1

अगर मैं इसे निष्पादित करने की कोशिश करता हूं, तो मुझे संकलक त्रुटि मिल रही है: चर x जाव डिफ़ॉल्ट मानों के आधार पर आरंभीकृत नहीं किया जा सकता है मुझे नीचे आउटपुट सही मिलना चाहिए?

"यहाँ x 0 है"।

नहीं, आप उस आउटपुट को नहीं देख रहे हैं क्योंकि आपको पहली बार एक संकलन-समय त्रुटि मिल रही है। अंतिम चर को एक डिफ़ॉल्ट मान मिलता है, लेकिन जावा भाषा विनिर्देश (JLS) के लिए आपको उन्हें कंस्ट्रक्टर के अंत तक आरंभ करने की आवश्यकता होती है (LE: मैं यहां आरंभीकरण ब्लॉक शामिल कर रहा हूं), अन्यथा आपको एक संकलन-समय त्रुटि मिलेगी अपने कोड को संकलित और निष्पादित करने से रोकेंगे।

आपका दूसरा उदाहरण आवश्यकता का सम्मान करता है, इसीलिए (1) आपका कोड संकलन करता है और (2) आपको अपेक्षित व्यवहार मिलता है।

भविष्य में जेएलएस के साथ खुद को परिचित करने का प्रयास करें। जावा भाषा के बारे में जानकारी का कोई बेहतर स्रोत नहीं है।

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