هل يتم تنفيذه دائمًا في Java؟


14 Answers

كود المثال:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

انتاج:

finally trumps return. 
0
Question

بالنظر في هذا الكود ، هل يمكنني أن أكون متأكدا تماما من أن الحظر finally ينفذ دائما ، بغض النظر عن something() ؟

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("i don't know if this will get printed out.");
}



هذه هي الفكرة الكاملة لكتلة أخيرة. يتيح لك التأكد من قيامك بالتنظيفات التي قد يتم تخطيها لأنك تعود ، من بين أشياء أخرى ، بالطبع.

وأخيراً يتم استدعاؤه بغض النظر عما يحدث في كتلة المحاولة ( إلا إذا قمت باستدعاء System.exit(int) أو تم تشغيل Java Virtual Machine لسبب آخر).




نعم سوف يطلق عليه. هذا هو بيت القصيد من الكلمة أخيرا. إذا كان القفز من كتلة المحاولة / الالتقاط يمكن أن يتخطى الحظر النهائي ، فهو نفس وضع System.out.println خارج المحاولة / المصيد.







باختصار ، في وثائق جافا الرسمية (اضغط here ) ، هو مكتوب أن -

إذا تم إنهاء JVM أثناء تنفيذ محاولة أو رمز الالتقاط ، فقد لا يتم تنفيذ الحظر النهائي. وبالمثل ، إذا تمت مقاطعة أو تنفيذ سلسلة تنفيذ المحاولة أو رمز الالتقاط ، فقد لا يتم تنفيذ الحظر النهائي حتى إذا استمر التطبيق ككل.




ضع في اعتبارك ذلك في سياق التنفيذ العادي (أي بدون أي استثناء يتم طرحه): إذا لم تكن الطريقة "باطلة" ، فعادةً ما تُرجع شيئًا صريحًا ، ومع ذلك ، دائمًا ما يتم تنفيذها




وأخيرًا ، يتم تنفيذ الحظر دائمًا سواء أكان الاستثناء أم لا. إذا حدث أي استثناء قبل محاولة المنع ، فلن يتم تنفيذ الحظر في النهاية.




جربت المثال أعلاه بتعديل طفيف

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

مخرجات الكود المذكورة أعلاه:

أخيرا ينسخ العودة.
2

هذا لأن عند return i; يتم تنفيذ i له قيمة 2. بعد ذلك يتم تنفيذ كتلة finally حيث يتم تعيين 12 إلى i ثم يتم تنفيذ System.out .

بعد تنفيذ حظر finally ، يُرجع مربع try 2 ، بدلاً من إرجاع 12 ، لأن بيان الإرجاع هذا لم يتم تنفيذه مرة أخرى.

إذا قمت بتصحيح هذه التعليمة البرمجية في Eclipse ، فستحصل على شعور بأنه بعد تنفيذ System.out في finally يتم تنفيذ بيان return الخاص بـ block try مرة أخرى. ولكن هذا ليس هو الحال. ببساطة إرجاع القيمة 2.




ها هي الكلمات الرسمية من مواصفات لغة جافا.

14.20.2. إعدام المحاولة وأخيرًا

يتم تنفيذ عبارة try مع كتلة في finally عن طريق تنفيذ كتلة try أولاً. ثم هناك خيار:

  • إذا اكتمل تنفيذ كتلة try طبيعي ، [...]
  • إذا اكتمل تنفيذ كتلة try فجأة بسبب throw القيمة V ، [...]
  • إذا اكتمل تنفيذ كتلة try فجأة لأي سبب آخر R ، فسيتم تنفيذ الحظر finally . ثم هناك خيار:
    • إذا اكتمل الشكل النهائي بشكل طبيعي ، try عبارة try فجأة للسبب R.
    • في حالة اكتمال كتلة finally فجأة للسبب S ، فإن عبارة try تكتمل فجأة للسبب S ( والسبب R يتم تجاهله ).

مواصفات return الواقع تجعل هذا صريحًا:

JLS 14.17 بيان الإرجاع

ReturnStatement:
     return Expression(opt) ;

يحاول بيان return بدون Expression نقل التحكم إلى invoker من الأسلوب أو منشئ يحتوي عليه.

يحاول بيان return مع Expression نقل التحكم إلى منشئ الأسلوب الذي يحتوي عليه؛ تصبح قيمة Expression قيمة استدعاء الأسلوب.

تشير الأوصاف السابقة إلى " محاولات نقل التحكم " بدلاً من " التحكّم في التحكّم " فقط لأنه إذا كانت هناك أية عبارات try ضمن الأسلوب أو المُنشئ الذي تحتوي كتله المحولة على بيان return ، فسيتم تنفيذ أي عبارات أخيرة من عبارات try هذه ، النظام ، الأعمق إلى الأبعد ، قبل أن يتم نقل السيطرة إلى منشئ الأسلوب أو منشئ. يمكن للانهيار المفاجئ لبند finally أن يعطل نقل السيطرة الذي بدأه بيان return .




هذا صحيح في الواقع في أي لغة ... سوف ينفذ دائما دائما قبل بيان العودة ، بغض النظر عن مكان هذا العائد في جسم الطريقة. إذا لم يكن هذا هو الحال ، فإن الكتلة الأخيرة لن يكون لها معنى كبير.




الجواب بسيط نعم .

إدخال:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

انتاج:

catch
finally



جرب هذا الكود ، ستفهم أن الكود في النهاية يتم تنفيذه بعد بيان الإعادة .

public class TestTryCatchFinally {
    static int x = 0;

    public static void main(String[] args){
        System.out.println(f1() );
        System.out.println(f2() );
    }

    public static int f1(){
        try{
            x = 1;
            return x;
        }finally{
            x = 2;
        }
    }

    public static int f2(){
        return x;
    }
}



نعم. فقط الحالة لن تكون JVM مخارج أو أعطال




وأخيرًا ، يتم دائمًا تشغيل هذا هو بيت القصيد ، لمجرد ظهوره في الشفرة بعد أن لا تعني العودة أن هذه هي الطريقة التي يتم تنفيذها بها. يتحمل وقت تشغيل Java مسؤولية تشغيل هذا الرمز عند الخروج من كتلة try .

على سبيل المثال ، إذا كان لديك ما يلي:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

سيولد وقت التشغيل شيء كالتالي:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

إذا تم طرح استثناء غير محظور فسيتم تشغيل الحظر finally وسيستمر التكرار في الانتشار.




يتم دائمًا تنفيذ الحظر أخيرًا ما لم يكن هناك إنهاء برنامج غير طبيعي ، إما ناتج عن عطل JVM أو من استدعاء System.exit(0) .

علاوة على ذلك ، فإن أي قيمة يتم إرجاعها من داخل الكتلة النهائية ستتجاوز القيمة التي يتم إرجاعها قبل تنفيذ الحظر النهائي ، لذا كن حذرًا من التحقق من جميع نقاط الخروج عند استخدام المحاولة في النهاية.




Related