سؤال

واحدة من الأشياء التي دائما يزعجني حول استخدام القراء و تيارات في جاوة هو أن close() طريقة رمي استثناء.منذ انها فكرة جيدة لوضع أسلوب إغلاق في النهاية كتلة ، وهذا يتطلب قليلا من وضع حرج.أنا عادة استخدام هذا البناء:

FileReader fr = new FileReader("SomeFile.txt");
try {
    try {
        fr.read();
    } finally {
        fr.close();
    }
} catch(Exception e) {
    // Do exception handling
}

ولكن رأيت أيضا هذا البناء:

FileReader fr = new FileReader("SomeFile.txt");
try {
    fr.read() 
} catch (Exception e) {
    // Do exception handling
} finally {
    try {
        fr.close();
    } catch (Exception e) {
        // Do exception handling
    }
}

أنا أفضل أول البناء لأن هناك الصيد واحد فقط كتلة يبدو أكثر أناقة.هل هناك سبب في الواقع يفضلون الثانية أو بديل البناء ؟

تحديث:من شأنه أن يحدث فرقا إذا أشرت إلى أن كل read و close فقط رمي IOExceptions?لذا يبدو لي أنه إذا قراءة فشل قريبا سوف تفشل لنفس السبب.

هل كانت مفيدة؟

المحلول

وكنت أذهب دائما للمثال الأول.

إذا كان بالقرب من رمي استثناء (في الواقع ان ذلك لن يحدث أبدا لFileReader)، لن الطريقة القياسية للتعامل مع أن يكون لرمي استثناء المناسب إلى المتصل؟ باستثناء وثيقة ينسخ يكاد يكون من المؤكد أي مشكلة كان لديك باستخدام الموارد. الأسلوب الثاني هو على الارجح أكثر ملاءمة إذا لديك فكرة معالجة الاستثناء هو الدعوة System.err.println.

وهناك مسألة كيفية استثناءات المدى الذي يجب أن يلقى. ThreadDeath ينبغي أن يكون دائما rethrown، ولكن أي استثناء في النهاية يتوقف ذلك. خطأ مشابه لابد ان تقذف أبعد من RuntimeException وRuntimeException أبعد من التحقق من الاستثناءات. إذا كنت تريد حقا أن يمكن كتابة التعليمات البرمجية لاتباع هذه القواعد، ومن ثم مجردة عليه مع تعبير "تنفيذ حوالي".

نصائح أخرى

وأخشى هناك مشكلة كبيرة مع المثال الأول، وهو أنه إذا استثناء يحدث في أو بعد قراءة وتنفيذ كتلة finally. حتى الان جيدة جدا. ولكن ماذا لو كان fr.close() ثم يسبب استثناء آخر إلى أن يلقى؟ هذا سوف "ورقة رابحة" الاستثناء الأول (قليلا مثل وضع return في كتلة finally) و ستفقد جميع المعلومات حول ما تسبب في الواقع مشكلة لتبدأ.

ويجب أن تستخدم لديك أخيرا كتلة:

IOUtil.closeSilently(fr);

وحيث هذه الطريقة فائدة يفعل ذلك بالضبط:

public static void closeSilently(Closeable c) {
    try { c.close(); } catch (Exception e) {} 
} 

وأنا أفضل ثانية واحدة. لماذا ا؟ إذا كان كل من read() وclose() رمي الاستثناءات، واحد منهم يمكن أن تضيع. في البناء الأول، واستثناء من close() يتجاوز استثناء من read()، بينما في ثانية واحدة، يتم التعامل مع استثناء من close() بشكل منفصل.


<القوي> اعتبارا من جافا 7، و محاولة-مع- موارد بناء يجعل هذا أبسط من ذلك بكثير لقراءة دون أي اهتمام حول الاستثناءات:.

try (FileReader fr = new FileReader("SomeFile.txt")) {
    fr.read();
    // no need to close since the try-with-resources statement closes it automatically
}

ومع معالجة الاستثناء:

try (FileReader fr = new FileReader("SomeFile.txt")) {
    fr.read();
    // no need to close since the try-with-resources statement closes it automatically
} catch (IOException e) {
    // Do exception handling
    log(e);
    // If this catch block is run, the FileReader has already been closed.
    // The exception could have come from either read() or close();
    // if both threw exceptions (or if multiple resources were used and had to be closed)
    // then only one exception is thrown and the others are suppressed
    // but can still be retrieved:
    Throwable[] suppressed = e.getSuppressed(); // can be an empty array
    for (Throwable t : suppressed) {
        log(suppressed[t]);
    }
}

وهناك حاجة واحدة فقط محاولة المصيد العرضي وجميع الاستثناءات يمكن التعامل معها بأمان. لا يزال بإمكانك إضافة كتلة finally إذا أردت، ولكن ليست هناك حاجة لإغلاق الموارد.

إذا كان كل قراءة و قريب رمي استثناء الاستثناء من قراءة سوف تكون مخفية في الخيار 1.لذا الخيار الثاني لا أكثر معالجة الخطأ.

ومع ذلك, في معظم الحالات, الخيار الأول سوف يكون لا يزال المفضل.

  1. في كثير من الحالات, لا يمكنك التعامل مع الاستثناءات في الطريقة التي يتم إنشاؤها ، ولكن لا يزال يجب أن تغلف تيار التعامل في تلك العملية.
  2. حاول إضافة الكاتب إلى رمز ونرى كيف مطول النهج الثاني يحصل.

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

والفرق، بقدر ما أستطيع أن أرى، هو أن هناك استثناءات المختلفة وأسبابها في اللعب على مستويات مختلفة، و

والصيد (استثناء ه)

ويحجب ذلك. النقطة الوحيدة من مستويات متعددة في التمييز الاستثناءات الخاصة بك، وما عليك القيام به عنهم:

try
{
  try{
   ...
  }
   catch(IOException e)
  {
  ..
  }
}
catch(Exception e)
{
  // we could read, but now something else is broken 
  ...
}

وأنا عادة القيام بما يلي. أولا، تحديد قالب أسلوب فئة استنادا للتعامل مع حاول / catch الفوضى

import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public abstract class AutoFileCloser {
    private static final Closeable NEW_FILE = new Closeable() {
        public void close() throws IOException {
            // do nothing
        }
    };

    // the core action code that the implementer wants to run
    protected abstract void doWork() throws Throwable;

    // track a list of closeable thingies to close when finished
    private List<Closeable> closeables_ = new LinkedList<Closeable>();

    // mark a new file
    protected void newFile() {
        closeables_.add(0, NEW_FILE);
    }

    // give the implementer a way to track things to close
    // assumes this is called in order for nested closeables,
    // inner-most to outer-most
    protected void watch(Closeable closeable) {
        closeables_.add(0, closeable);
    }

    public AutoFileCloser() {
        // a variable to track a "meaningful" exception, in case
        // a close() throws an exception
        Throwable pending = null;

        try {
            doWork(); // do the real work

        } catch (Throwable throwable) {
            pending = throwable;

        } finally {
            // close the watched streams
            boolean skip = false;
            for (Closeable closeable : closeables_) {
                if (closeable == NEW_FILE) {
                    skip = false;
                } else  if (!skip && closeable != null) {
                    try {
                        closeable.close();
                        // don't try to re-close nested closeables
                        skip = true;
                    } catch (Throwable throwable) {
                        if (pending == null) {
                            pending = throwable;
                        }
                    }
                }
            }

            // if we had a pending exception, rethrow it
            // this is necessary b/c the close can throw an
            // exception, which would remove the pending
            // status of any exception thrown in the try block
            if (pending != null) {
                if (pending instanceof RuntimeException) {
                    throw (RuntimeException) pending;
                } else {
                    throw new RuntimeException(pending);
                }
            }
        }
    }
}

ملحوظة باستثناء "انتظار" - وهذا يعتني حال استثناء القيت خلال مقربة من شأنه أن قناع استثناء أننا قد يهتمون حقا

ويحاول في النهاية إلى إغلاق من الخارج من أي تيار زينت أولا، حتى لو كان لديك BufferedWriter التفاف FileWriter، ونحن نحاول إغلاق BuffereredWriter أولا، وإذا فشل ذلك، لا يزال محاولة إغلاق FileWriter نفسها.

ويمكنك استخدام فئة المذكورة أعلاه كما يلي:

try {
    // ...

    new AutoFileCloser() {
        @Override protected void doWork() throws Throwable {
            // declare variables for the readers and "watch" them
            FileReader fileReader = null;
            BufferedReader bufferedReader = null;
            watch(fileReader = new FileReader("somefile"));
            watch(bufferedReader = new BufferedReader(fileReader));

            // ... do something with bufferedReader

            // if you need more than one reader or writer
            newFile(); // puts a flag in the 
            FileWriter fileWriter = null;
            BufferedWriter bufferedWriter = null;
            watch(fileWriter = new FileWriter("someOtherFile"));
            watch(bufferedWriter = new BufferedWriter(fileWriter));

            // ... do something with bufferedWriter
        }
    };

    // .. other logic, maybe more AutoFileClosers

} catch (RuntimeException e) {
    // report or log the exception
}

وباستخدام هذا النهج لا داعي للقلق حول محاولة / صيد / أخيرا للتعامل مع إغلاق الملفات مرة أخرى.

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

والاتفاقية الموحدة الأول هو استخدام أنه يجب عدم السماح استثناءات الهروب كتلة أخيرا.

وهذا هو لأنه إذا كان استثناء ويضخم بالفعل باستثناء طرد من النهاية منع إرادة رابحة باستثناء الأصلي (وبالتالي تضيع).

في 99٪ من الحالات وهذا ليس ما تريد هي الاستثناء الأصلي هو على الارجح مصدر المشكلة (أي استثناءات الثانوية قد تكون الآثار الجانبية من البداية ولكن سوف تحجب قدرتك على العثور على مصدر الاستثناء الأصلي و وبالتالي فإن المشكلة الحقيقية).

وهكذا رمز الأساسية الخاصة بك ينبغي أن تبدو هذه:

try
{
    // Code
}
// Exception handling
finally
{
    // Exception handling that is garanteed not to throw.
    try
    {
         // Exception handling that may throw.
    }
    // Optional Exception handling that should not throw
    finally()
    {}
}

ونهج 2ND.

وعلى خلاف ذلك، وأنا لا أرى لك اصطياد استثناء من المنشئ FileReader

<وأ href = "http://java.sun.com/j2se/1.5.0/docs/api/java/io/FileReader.html#FileReader(java.lang.String)" يختلط = "نوفولو noreferrer "> http://java.sun.com/j2se/1.5.0/docs/api/java/io/FileReader.html#FileReader (java.lang.String)

وFileReader العام (سلسلة اسم الملف)            يلقي FileNotFoundException

وهكذا، وعادة ما يكون منشئ داخل كتلة المحاولة أيضا. ومنع أخيرا التدقيق لمعرفة ما إذا كان القارئ غير فارغة قبل محاولة للقيام نهايتها.

والنمط نفسه ينطبق على مصدر البيانات، الاتصال، بيان، resultset و.

ومتداخلة أحيانا محاولة اللحاق ليس تفضيل، النظر في هذا:

try{
 string s = File.Open("myfile").ReadToEnd(); // my file has a bunch of numbers
 // I want to get a total of the numbers 
 int total = 0;
 foreach(string line in s.split("\r\n")){
   try{ 
     total += int.Parse(line); 
   } catch{}
 }
catch{}

وربما هذا هو مثالا سيئا، ولكن هناك أوقات ستحتاج متداخلة محاولة-cactch.

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

وأنا دائما استخدام فئة أداة للتعامل مع هذا النوع من الاستثناءات المشتركة، ولكن أود أن جعل هذا يختلف صغيرة إلى إجابته.

وأود أن دائما استخدام مسجل (log4j بالنسبة لي) لتسجيل الأخطاء وغيرها.

IOUtil.close(fr);

وهناك تعديل طفيف على طريقة فائدة:

public static void close(Closeable c) {
    try {
      c.close();
    } catch (Exception e) {
      logger.error("An error occurred while closing. Continuing regardless", e); 
    } 
}

في بعض الحالات متداخلة محاولة اللحاق أمر لا مفر منه. على سبيل المثال عند الخطأ انتعاش القانون نفسه يمكن أن يلقي واستثناء. ولكن من أجل تحسين سهولة قراءة التعليمات البرمجية التي يمكن دائما استخراج كتلة متداخلة إلى أسلوب خاص بها. تحقق من هذا المنصب بلوق لمزيد من الأمثلة على Try- متداخلة كتل اللحاق أخيرا.

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