سؤال

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

import java.net.*;
import java.io.*;

class main{

public void process(PrintWriter out, BufferedReader in, Socket echoSocket){
//....
}

public void processData() {
    Socket echoSocket;
    PrintWriter out;
    BufferedReader in;
    try{
        echoSocket = new Socket("server.company.com", 8081);
        out = new PrintWriter(echoSocket.getOutputStream(), true);
        in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
    }
    catch (Exception e) {
        System.err.println("Exception has occured");
        return;
    }
    process(out, in, echoSocket);
    try {
        out.close();
        in.close();
        echoSocket.close();
    }
    catch(IOException e) {
        System.err.println("IOException has occurred.");
    }
  }
}
هل كانت مفيدة؟

المحلول

على الرغم من الأخطاء المطبعية، لا يمكن للمرء إلا أن يخمن ما هو "الخطأ" الفعلي، إلا أن هذا الرمز به مشكلة في معالجة الأخطاء.على وجه التحديد، في التصرف في الموارد.

مناقشة حول الموارد

ما هي الموارد؟

أساسًا :أي كائن Java يعتمد على موارد مستوى نظام التشغيل الأساسي.خاصة :موارد الإدخال والإخراج (تدفقات الإدخال والإخراج، القنوات)، المقابس.ولكن الأهم من ذلك :إذا كان "الشيء" الذي تستخدمه يحتوي على close, dispsose, shutdown أو أي شيء من هذا القبيل، فإنه بالتأكيد يحتفظ بالموارد داخليًا.
هناك بعض الاستثناءات (أبرزها ByteArrayInputStream لا يحمل أي مورد سوى الذاكرة)، ولكن هذه هي تفاصيل التنفيذ:إذا التزمت بواجهتهم (ويجب عليك ذلك، فهذا "عقد")، فيجب إغلاق كل دفق.
منذ Java 7، تقوم معظم هذه الكائنات في Java API بتنفيذ AutoCloseable الواجهة، ولكن العديد من الجهات الخارجية لم تقم بالضرورة بنقل هذا إلى التعليمات البرمجية الخاصة بها (وربما لا يستطيع البعض القيام بذلك لأسباب أخرى).

باعتباري أحد مراجعي الكود في شركتي:أتوقف عن القراءة وأرفض أي رمز بمجرد عدم رؤية مكالمة آمنة لـ close طريقة الموارد.أعني بالتأمين وجود جملة نهائية مضمونة التنفيذ.

القواعد الأساسية حول الموارد

أي يجب تحرير الموارد التي حصل عليها برنامجك في ملف finally جملة (حتى أن البعض يضيف:خاصة بها).

ما هي دورة الحياة النموذجية للموارد بئر:

  1. تحصل عليه
  2. استخدامه
  3. أنت تطلق سراحه

في التعليمات البرمجية الخاصة بك، وهذا هو

ResourceObject myObject = null;
try {
    myObject = getResource();
    processResource(myObject);
} finally {
    if(myObject != null) {
        try {
            myObject.close();
        } catch (Exception e) {
            // Usually there is nothing one can do but log
        }
    }
}

منذ Java 7، إذا تم تنفيذ كائن المورد AutoCloseableلديك طريقة جديدة لكتابة ذلك، تسمى "المحاولة باستخدام الموارد".

try(ResourceObject myObject = getResource()) {
    process(myObject);
}

أنت لا ترى النهاية، ولكنها موجودة، ويكتب المترجم جملة النهاية لك في هذه الحالة.

ماذا عن الموارد المتعددة؟

حسنًا :موارد متعددة، نهايات متعددة.والفكرة هي فصل أسباب الفشل في عبارات نهائية مختلفة.لنفترض أنك تريد نسخ ملف ...

public void myCopy() throws IOException {
InputStream source = null;
    try {
    source = new FileInputStream("yourInputFile");
        // If anything bad happens, I have a finally clause that protects this now   
        OutputStream destination = null;
    try {
        destination = new FileOutputStream("yourOurputFile"); // If fails, my Input will be closed thanks to its own finally
            performCopy(source, destination); // If this fail, my destination will also be closed thanks to its own finally
        } finally {
            if(destination!=null) { try { destination.close(); } catch (Exception e) {/* log*/ }}
        }
    } finally {
        if(source!=null) { try { source.close(); } catch (Exception e) {/* log*/ }}
    }
}

أو، باستخدام صيغة Java 7، لدينا النص الأقصر (إخلاء المسؤولية:ليس لدي Java7 في الوقت الحالي، لذا لا يمكنني حقًا التحقق مما إذا كان هذا يتم تجميعه):

try(
    InputStream input = new FileInputStream("in");
    OutputStream output = new FileOutputStream("out")) {
    performCopy(input, output);
} catch(IOException e) {
    // You still have to deal with it of course.
}

هذا كثير جدًا!

نعم إنه كذلك.لهذا السبب لدينا مكتبات.يمكن للمرء أن يجادل بأنه لا ينبغي عليك كتابة مثل هذا الرمز.استخدم مكتبات قياسية جيدة التصرف مثل commons IO، أو استخدم إحدى طرق الأداة المساعدة الخاصة بها.أو أساليب JDK الأحدث مثل Files API، وانظر كيف يعمل هذا.

كومنز IO مفيد IOUtils.closeQuietly() مجموعة من الأساليب لإغلاق التدفقات.

حاول باستخدام الموارد Gotchas

هناك تداعيات في استدعاء "المحاولة باستخدام الموارد" أعمق قليلاً من ذلك.وتشمل هذه:ماذا لو كنت أرغب في القيام بشيء ما مع الاستثناءات التي تحدث في الجملة الأخيرة؟كيف يمكنني التمييز بين ذلك وبين الاستثناء الذي كان سيحدث أثناء ذلك performCopy؟حالة أخرى هي :ماذا يحدث هنا :

try(Reader reader = new InputStreamReader(new FileInputStream("in"), "an encoding that is not supported")) {
  // Whatever
}

يحدث أن UnsupportedEncodingException يتم طرح ولكن بعد ال FileInputStream تم مثيله.ولكن كما FileInputStream ليس موضوع جملة المحاولة، ولن يتم إغلاقه.لديك تسرب واصف الملف.حاول ذلك ألف مرة، ولن يتمكن JVM الخاص بك من فتح الملفات بعد الآن، وسيخبرك نظام التشغيل لديك "تم تجاوز الحد الأقصى لعدد الملفات المفتوحة" (ulimit بشكل عام يفعل ذلك في UNIX)

العودة إلى مآخذ الخاص بك

إذن ما هي مواردك؟

حسنًا، أولاً، يمكننا أن نلاحظ أن لديك موردًا حقيقيًا واحدًا فقط، وهو مثيل المقبس الخاص بك، لأن المقبس javadoc يقول (javadoc):

 * <p> Closing this socket will also close the socket's
 * {@link java.io.InputStream InputStream} and
 * {@link java.io.OutputStream OutputStream}.

لذا فإن تدفقات الإدخال والإخراج الخاصة بك مرتبطة بالمقبس الخاص بك، وهذا يكفي.

ما هو الخطأ في التعليمات البرمجية الخاصة بك

إضافة تعليقات على الكود الأصلي الخاص بك:

try{
    echoSocket = new Socket("server.company.com", 8081);
    out = new PrintWriter(echoSocket.getOutputStream(), true); // This can throw IOException
    in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); // Ditto
}
catch (Exception e) {
    // If an exception was thrown getting any of the streams, we get there
    System.err.println("Exception has occured");
    // And you return without closing the socket. It's bad !
    return;
}
// Let's assume everything worked, no exception.
process(out, in, echoSocket); // This may throw an exception (timeout, socket closed by peer, ...) 
                              // that is uncaught (no catch clause). Your socket will be left unclosed as a result.
try {
    out.close();              // This can fail
    in.close();               // This too
    echoSocket.close();       // And this too - although nothing you can do about it
}
catch(IOException e) {
    // if out.close fails, we get here, and in.close and socket.close 
    // never got a chance to be called. You may be leaking resources 
    System.err.println("IOException has occurred.");
}

تنفيذ آمن

Socket echoSocket = null;
try {
    // open socket, 
    echoSocket = new Socket("server.company.com", 8081); // protected by finally
    out = new PrintWriter(echoSocket.getOutputStream(), true); // protected
    in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); // protected
     process(out, in, echoSocket); // Still protected
} catch (Exception e) {
    // Your current error handling
} finally {
    // Anyway, this close will be called if needs be.
    if(echoSocket != null) { 
        try { echoSocket.close(); } catch (Exception e) { /* log */}
        // See javadoc, this has closed the in & out streams too.
    }
}

نصائح أخرى

giveacodicetagpre. يجب أن يكون

giveacodicetagpre.

ويبدو أن كل شيء يبدو جيدا بالنسبة لي

جرب هذا أعتقد أنك فاتتك فاصلة منقوطة

giveacodicetagpre.

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