java - application - error click for details مشكلة




هل يتم تنفيذ حظر نهائي دائمًا في جافا؟ (20)

بالنظر إلى هذا الكود ، هل يمكنني أن أكون متأكداً تمامًا من أن الجزء finally تنفيذه دائمًا ، بغض النظر عن something() ما something() ؟

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

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

إدخال:

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

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

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

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


إذا تم طرح استثناء ، يدير أخيرا. إذا لم يتم طرح استثناء ، يدير أخيرا. إذا تم القبض على الاستثناء ، يدير أخيرا. إذا لم يتم اكتشاف الاستثناء ، يدير أخيرا.

المرة الوحيدة التي لا يتم تشغيلها هي عندما تخرج JVM.


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

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


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


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

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;
    }
}

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

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());
    }

    private static String someString() {

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

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

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

-abc-
---AGAIN---
-abc-xyz-abc-

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

private static String someString() {

    StringBuilder sb = new StringBuilder();

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

    } finally {
        sb = null;
    }
}

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


كود المثال:

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

لأنه سيتم دوماً استدعاء كتلة أخيراً إلا إذا قمت باستدعاء System.exit() (أو تعطل مؤشر الترابط).


لا ، ليست دائمًا حالة استثناء واحدة // 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");
        }
    }
}

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

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

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


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


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


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

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 .


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


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

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


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


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

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

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

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

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

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





try-catch-finally