سؤال

وبالنظر إلى هذا الرمز، هل يمكنني أن أكون كذلك متأكد تماما أن 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");
}
هل كانت مفيدة؟

المحلول

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

الأوقات الوحيدة finally لن يتم استدعاؤها هي:

  1. إذا استدعيت System.exit()
  2. إذا تعطل JVM أولاً
  3. إذا وصل JVM إلى حلقة لا نهائية (أو بعض العبارات الأخرى غير القابلة للمقاطعة وغير المنتهية) في try أو catch حاجز
  4. إذا أنهى نظام التشغيل عملية JVM بالقوة؛على سبيل المثال، kill -9 <pid> على يونيكس
  5. إذا مات النظام المضيف؛على سبيل المثال، انقطاع التيار الكهربائي، خطأ في الأجهزة، الذعر في نظام التشغيل، وما إلى ذلك
  6. إذا finally سيتم تنفيذ الكتلة بواسطة خيط خفي وستخرج جميع سلاسل الرسائل الأخرى غير الخفي من قبل finally يسمى

نصائح أخرى

رمز المثال:

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

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

try { return true; } finally { return false; }

نفس الشيء مع رمي الاستثناءات من الكتلة النهائية.

إليك الكلمات الرسمية من مواصفات لغة Java.

14.20.2.تنفيذ المحاولة النهائية ومحاولة الالتقاط أخيرًا

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

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

المواصفات ل return في الواقع يوضح هذا:

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

ReturnStatement:
     return Expression(opt) ;

أ return بيان مع لا Expression محاولات لنقل التحكم إلى مستحضر الطريقة أو المُنشئ الذي يحتوي عليها.

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

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

بالإضافة إلى الاستجابات الأخرى، من المهم الإشارة إلى أن كلمة "أخيرًا" لها الحق في تجاوز أي استثناء/قيمة تم إرجاعها بواسطة كتلة Try..catch.على سبيل المثال، الكود التالي يرجع 12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

وبالمثل، فإن الطريقة التالية لا تطرح استثناءً:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

بينما تقوم الطريقة التالية برميها:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

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

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 بيان try يتم تنفيذ الكتلة مرة أخرى.ولكن هذا ليس هو الحال.إنها ببساطة ترجع القيمة 2.

وهنا تفصيل ل إجابة كيفن.من المهم معرفة أن التعبير المراد إرجاعه يتم تقييمه من قبل finally, ، حتى لو عاد بعد ذلك.

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

public static int printX() {
    System.out.println("X");
    return 0;
}

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

انتاج:

X
finally trumps return... sort of
0

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

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

الطريقة المنطقية للتفكير في هذا هي:

  1. يجب تنفيذ التعليمات البرمجية الموضوعة في الكتلة النهائية مهما حدث داخل كتلة المحاولة
  2. لذا، إذا حاول الكود الموجود في كتلة المحاولة إرجاع قيمة أو طرح استثناء، فسيتم وضع العنصر "على الرف" حتى يمكن تنفيذ الكتلة النهائية
  3. نظرًا لأن الكود الموجود في الكتلة الأخيرة له (بحكم التعريف) أولوية عالية، فيمكنه إرجاع أو رمي ما يحلو له.وفي هذه الحالة يتم التخلص من أي شيء متبقي على الرف.
  4. الاستثناء الوحيد لذلك هو إذا تم إيقاف تشغيل الجهاز الافتراضي بالكامل أثناء كتلة المحاولة، على سبيل المثال.بواسطة "System.exit"

كما أن العودة أخيرًا سوف تتخلص من أي استثناء. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html

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

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

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

لا ، ليست دائمًا حالة استثناء واحدة هي // system.exit (0) ؛قبل أن تمنع الكتلة الأخيرة تنفيذها أخيرًا.

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

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

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

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

سيولد وقت التشغيل شيئًا مثل هذا:

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

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

وذلك لأنك قمت بتعيين قيمة i على أنها 12، ولكنك لم تقم بإرجاع قيمة i إلى الدالة.الكود الصحيح هو كما يلي:

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

لأنه سيتم دائمًا استدعاء الكتلة النهائية ما لم تتصل بها System.exit() (أو تعطل الخيط).

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

مدخل:

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

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

بإيجاز، في وثائق جافا الرسمية (انقر فوق هنا)، مكتوب أن -

إذا تم خروج JVM أثناء تنفيذ المحاولة أو الرمز ، فقد لا يتم تنفيذ الكتلة أخيرًا.وبالمثل ، إذا تم مقاطعة أو قتل مؤشر الترابط الذي ينفذ رمز التجربة أو القتل ، فقد لا يتم تنفيذ الكتلة أخيرًا على الرغم من أن التطبيق ككل يستمر.

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

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

نعم.الحالة الوحيدة التي لن تكون هي خروج JVM أو تعطله

خذ بعين الاعتبار البرنامج التالي:

public class SomeTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
        System.out.println("---PRINT THE RESULT---");
        System.out.println(sb.toString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

اعتبارًا من Java 1.8.162، تعطي كتلة التعليمات البرمجية أعلاه المخرجات التالية:

-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz

وهذا يعني أن استخدام finally يعد تحرير الكائنات ممارسة جيدة مثل الكود التالي:

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null; // Just an example, but you can close streams or DB connections this way.
    }
}

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

finally سيتم التنفيذ وهذا أمر مؤكد.

finally لن يتم التنفيذ في الحالات التالية:

حالة 1 :

عندما تقوم بالتنفيذ System.exit().

الحالة 2 :

عندما يتعطل JVM / Thread.

الحالة 3 :

عندما يتم إيقاف التنفيذ يدويًا.

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

  1. عن طريق التسبب في خطأ فادح يؤدي إلى إحباط العملية.

  2. إنهاء البرنامج بسبب تلف الذاكرة.

  3. عن طريق استدعاء System.exit()

  4. إذا ذهب البرنامج إلى حلقة اللانهاية.

نعم لأن لا يوجد بيان السيطرة يمكن أن تمنع finally من إعدامه.

فيما يلي مثال مرجعي، حيث سيتم تنفيذ جميع كتل التعليمات البرمجية:

| x | Current result | Code 
|---|----------------|------ - - -
|   |                |     
|   |                | public static int finallyTest() {
| 3 |                |     int x = 3;
|   |                |     try {
|   |                |        try {
| 4 |                |             x++;
| 4 | return 4       |             return x;
|   |                |         } finally {
| 3 |                |             x--;
| 3 | throw          |             throw new RuntimeException("Ahh!");
|   |                |         }
|   |                |     } catch (RuntimeException e) {
| 4 | return 4       |         return ++x;
|   |                |     } finally {
| 3 |                |         x--;
|   |                |     }
|   |                | }
|   |                |
|---|----------------|------ - - -
|   | Result: 4      |

في البديل أدناه ، return x; سيتم تخطي.النتيجة لا تزال 4:

public static int finallyTest() {
    int x = 3;
    try {
        try {
            x++;
            if (true) throw new RuntimeException("Ahh!");
            return x; // skipped
        } finally {
            x--;
        }
    } catch (RuntimeException e) {
        return ++x;
    } finally {
        x--;
    }
}

المراجع، بطبيعة الحال، تتبع حالتها.يُرجع هذا المثال مرجعًا بـ value = 4:

static class IntRef { public int value; }
public static IntRef finallyTest() {
    IntRef x = new IntRef();
    x.value = 3;
    try {
        return x;
    } finally {
        x.value++; // will be tracked even after return
    }
}

try- catch- finally هي الكلمات الأساسية لاستخدام حالة معالجة الاستثناء.
كتفسير عادي

try {
     //code statements
     //exception thrown here
     //lines not reached if exception thrown
} catch (Exception e) {
    //lines reached only when exception is thrown
} finally {
    // always executed when the try block is exited
    //independent of an exception thrown or not
}

الكتلة النهائية تمنع التنفيذ ...

  • عندما قمت بالاتصال System.exit(0);
  • إذا خرج JVM.
  • أخطاء في JVM

اضافة الى إجابة @vibhash حيث لا توجد إجابة أخرى تشرح ما يحدث في حالة وجود كائن قابل للتغيير مثل الموجود أدناه.

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

public static StringBuffer test() {
    StringBuffer s = new StringBuffer();
    try {
        s.append("sb");
        return s;
    } finally {
        s.append("updated ");
    }
}

سوف الإخراج

sbupdated 

لقد جربت هذا ، فهو واحد مترابط.

class Test {
    public static void main(String args[]) throws Exception {
       Object obj = new Object();
       try {
            synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
           }
       } catch (Exception e) {
       } finally {
           System.out.println("finally");
       }
   }
}

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

لذلك لن يقوم إخراج وحدة التحكم بطباعة السلسلة: after wait() أو finally

متفق عليه مع @Stephen C، المثال أعلاه هو أحد الحالات الثالثة المذكورة هنا:

إضافة المزيد من إمكانيات الحلقة اللانهائية في الكود التالي:

// import java.util.concurrent.Semaphore;
class Test {
    public static void main(String[] args) {
        try {
            // Thread.sleep(Long.MAX_VALUE);
            // Thread.currentThread().join();
            // new Semaphore(0).acquire();
            // while (true){}
            System.out.println("after sleep join semaphore exit infinite while loop");
        } catch (Exception e) {
        } finally {
            System.out.println("finally");
        }
    }
}

الحالة 2:إذا تعطل JVM أولاً

import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Test {
    public static void main(String args[]) {
        try {
            unsafeMethod();
//            Runtime.getRuntime().halt(123);
            System.out.println("After Jvm Crash!");
        } catch (Exception e) {
        } finally {
            System.out.println("finally");
        }
    }

    private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        unsafe.putAddress(0, 0);
    }
}

المرجع: كيف يمكنك تعطل JVM؟

الحالة 6:إذا تم تنفيذ الحظر أخيرًا بواسطة مؤشر ترابط خفي وستخرج جميع سلاسل الرسائل الأخرى غير الخفي قبل أن يتم استدعاؤها أخيرًا.

class Test {
    public static void main(String args[]) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    printThreads("Daemon Thread printing");
                    // just to ensure this thread will live longer than main thread
                    Thread.sleep(10000);
                } catch (Exception e) {
                } finally {
                    System.out.println("finally");
                }
            }
        };
        Thread daemonThread = new Thread(runnable);
        daemonThread.setDaemon(Boolean.TRUE);
        daemonThread.setName("My Daemon Thread");
        daemonThread.start();
        printThreads("main Thread Printing");
    }

    private static synchronized void printThreads(String str) {
        System.out.println(str);
        int threadCount = 0;
        Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
        for (Thread t : threadSet) {
            if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
                System.out.println("Thread :" + t + ":" + "state:" + t.getState());
                ++threadCount;
            }
        }
        System.out.println("Thread count started by Main thread:" + threadCount);
        System.out.println("-------------------------------------------------");
    }
}

انتاج:هذا لا يطبع كلمة "أخيرًا" مما يعني عدم تنفيذ "الحظر أخيرًا" في "خيط البرنامج الخفي".

main Thread Printing  
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED  
Thread :Thread[main,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE   
Thread count started by Main thread:3  
-------------------------------------------------  
Daemon Thread printing  
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE  
Thread count started by Main thread:2  
-------------------------------------------------  

Process finished with exit code 0
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top