मुझे लगता है कि आपने शायद ऐसी स्थिति का अनुभव किया है जहां आप कोड चलाते हैं और NullPointerException , ClassCastException , या इससे भी बदतर ... इसके बाद डिबगिंग, विश्लेषण, गुगलिंग, और इसी तरह की एक लंबी प्रक्रिया होती है। अपवाद इस रूप में अद्भुत हैं: वे समस्या की प्रकृति और यह कहां हुई, इसका संकेत देते हैं। यदि आप अपनी याददाश्त को ताज़ा करना चाहते हैं और थोड़ा और सीखना चाहते हैं, तो इस लेख पर एक नज़र डालें: अपवाद: चेक किया गया, अनचेक किया गया और कस्टम

उस ने कहा, ऐसी परिस्थितियाँ हो सकती हैं जब आपको अपना अपवाद बनाने की आवश्यकता हो। उदाहरण के लिए, मान लीजिए कि आपके कोड को किसी दूरस्थ सेवा से जानकारी का अनुरोध करने की आवश्यकता है जो किसी कारण से अनुपलब्ध है। या मान लीजिए कि कोई व्यक्ति बैंक कार्ड के लिए एक आवेदन भरता है और एक फोन नंबर प्रदान करता है, चाहे गलती से हो या न हो, पहले से ही सिस्टम में किसी अन्य उपयोगकर्ता के साथ जुड़ा हुआ है।

बेशक, यहां सही व्यवहार अभी भी ग्राहक की आवश्यकताओं और सिस्टम की वास्तुकला पर निर्भर करता है, लेकिन मान लें कि आपको यह जांचने का काम सौंपा गया है कि क्या फोन नंबर पहले से ही उपयोग में है और यदि यह है तो अपवाद फेंक रहा है।

आइए एक अपवाद बनाएँ:


public class PhoneNumberAlreadyExistsException extends Exception {

   public PhoneNumberAlreadyExistsException(String message) {
       super(message);
   }
}
    

अगला हम इसका उपयोग तब करेंगे जब हम अपनी जाँच करेंगे:


public class PhoneNumberRegisterService {
   List<String> registeredPhoneNumbers = Arrays.asList("+1-111-111-11-11", "+1-111-111-11-12", "+1-111-111-11-13", "+1-111-111-11-14");

   public void validatePhone(String phoneNumber) throws PhoneNumberAlreadyExistsException {
       if (registeredPhoneNumbers.contains(phoneNumber)) {
           throw new PhoneNumberAlreadyExistsException("The specified phone number is already in use by another customer!");
       }
   }
}
    

हमारे उदाहरण को सरल बनाने के लिए, हम डेटाबेस का प्रतिनिधित्व करने के लिए कई हार्डकोडेड फ़ोन नंबरों का उपयोग करेंगे। और अंत में, आइए हमारे अपवाद का उपयोग करने का प्रयास करें:


public class CreditCardIssue {
   public static void main(String[] args) {
       PhoneNumberRegisterService service = new PhoneNumberRegisterService();
       try {
           service.validatePhone("+1-111-111-11-14");
       } catch (PhoneNumberAlreadyExistsException e) {
           // Here we can write to logs or display the call stack
		e.printStackTrace();
       }
   }
}
    

और अब Shift+F10 दबाने का समय आ गया है (यदि आप आईडिया का उपयोग कर रहे हैं), यानी प्रोजेक्ट को रन करें। कंसोल में आप यही देखेंगे:

अपवाद। क्रेडिट कार्ड
जारी अपवाद। फोन नंबर पहले से ही मौजूद है अपवाद: निर्दिष्ट फोन नंबर पहले से ही किसी अन्य ग्राहक द्वारा उपयोग में है!
अपवाद पर। PhoneNumberRegisterService.validatePhone (PhoneNumberRegisterService.java:11)

अपने आप को देखो! आपने अपना स्वयं का अपवाद बनाया और उसका थोड़ा परीक्षण भी किया। इस उपलब्धि पर बधाई! मैं यह समझने के लिए कोड के साथ थोड़ा प्रयोग करने की सलाह देता हूं कि यह कैसे काम करता है।

एक और चेक जोड़ें - उदाहरण के लिए, जांचें कि फ़ोन नंबर में अक्षर शामिल हैं या नहीं। जैसा कि आप शायद जानते हैं, फोन नंबरों को याद रखना आसान बनाने के लिए संयुक्त राज्य अमेरिका में अक्सर अक्षरों का उपयोग किया जाता है, उदाहरण के लिए 1-800-MY-APPLE। आपका चेक यह सुनिश्चित कर सकता है कि फ़ोन नंबर में केवल संख्याएँ हों।

ठीक है, इसलिए हमने एक चेक किया हुआ अपवाद बनाया है। सब ठीक और अच्छा होगा, लेकिन...

प्रोग्रामिंग समुदाय दो खेमों में बंटा हुआ है - वे जो जाँचे गए अपवादों के पक्ष में हैं और वे जो उनका विरोध करते हैं। दोनों पक्ष मजबूत तर्क देते हैं। दोनों में शीर्ष पायदान के डेवलपर्स शामिल हैं: ब्रूस एकेल चेक किए गए अपवादों की आलोचना करते हैं, जबकि जेम्स गोस्लिंग उनका बचाव करते हैं। ऐसा लगता है कि यह मामला कभी स्थायी रूप से नहीं सुलझेगा। उस ने कहा, आइए चेक किए गए अपवादों का उपयोग करने के मुख्य नुकसान देखें।

चेक किए गए अपवादों का मुख्य नुकसान यह है कि उन्हें संभाला जाना चाहिए। और यहां हमारे पास दो विकल्प हैं: या तो कोशिश-कैच का उपयोग करके इसे संभाल लें , या, यदि हम कई जगहों पर एक ही अपवाद का उपयोग करते हैं, तो अपवादों को फेंकने के लिए फेंकता का उपयोग करें, और उन्हें शीर्ष-स्तरीय कक्षाओं में संसाधित करें।

साथ ही, हम "बॉयलरप्लेट" कोड के साथ समाप्त हो सकते हैं, यानी कोड जो बहुत अधिक जगह लेता है, लेकिन बहुत भारी भारोत्तोलन नहीं करता है।

बहुत सारे अपवादों को संभालने के साथ काफी बड़े अनुप्रयोगों में समस्याएं उभरती हैं: एक शीर्ष-स्तरीय पद्धति पर फेंकता सूची एक दर्जन अपवादों को शामिल करने के लिए आसानी से बढ़ सकती है।

पब्लिक आवरकूलक्लास () फर्स्टएक्सेप्शन, सेकेंडएक्सेप्शन, थर्डएक्सेप्शन, एप्लीकेशननेमएक्सेप्शन को फेंकता है ...

डेवलपर्स आमतौर पर इसे पसंद नहीं करते हैं और इसके बजाय एक ट्रिक चुनते हैं: वे अपने सभी चेक किए गए अपवादों को एक सामान्य पूर्वज - ApplicationNameException बनाते हैं । अब उन्हें एक हैंडलर में उस ( चेक किए गए !) अपवाद को भी पकड़ना होगा:


catch (FirstException e) {
    // TODO
}
catch (SecondException e) {
    // TODO
}
catch (ThirdException e) {
    // TODO
}
catch (ApplicationNameException e) {
    // TODO
}
    

यहाँ हमें एक और समस्या का सामना करना पड़ता है - हमें अंतिम कैच ब्लॉक में क्या करना चाहिए? ऊपर, हमने पहले ही सभी अपेक्षित स्थितियों को संसाधित कर दिया है, इसलिए इस बिंदु पर ApplicationNameException का अर्थ हमारे लिए " अपवाद : कुछ समझ से बाहर होने वाली त्रुटि" से अधिक कुछ नहीं है। हम इसे इस तरह से हैंडल करते हैं:


catch (ApplicationNameException e) {
    LOGGER.error("Unknown error", e.getMessage());
}
    

और अंत में पता नहीं क्या हुआ।

लेकिन क्या हम सभी अपवादों को एक साथ नहीं फेंक सकते, इस तरह?


public void ourCoolMethod() throws Exception {
// Do some work
}
    

हाँ, हम कर सकते थे। लेकिन "अपवाद फेंकता है" हमें क्या बताता है? कि कुछ टूट गया है। कारण को समझने के लिए आपको ऊपर से नीचे तक सब कुछ जांचना होगा और लंबे समय तक डीबगर के साथ आराम करना होगा।

आप एक ऐसे निर्माण का भी सामना कर सकते हैं जिसे कभी-कभी "अपवाद निगलने" कहा जाता है:


try {
// Some code
} catch(Exception e) {
   throw new ApplicationNameException("Error");
}
    

स्पष्टीकरण के माध्यम से यहां जोड़ने के लिए बहुत कुछ नहीं है - कोड सब कुछ स्पष्ट करता है, या बल्कि, यह सब कुछ अस्पष्ट बनाता है।

बेशक, आप कह सकते हैं कि आप इसे वास्तविक कोड में नहीं देख पाएंगे। ठीक है, चलिए java.net पैकेज से URL क्लास के बाउल (कोड) में झाँकते हैं। अगर आप जानना चाहते हैं तो मुझे फॉलो करें!

यहाँ URL वर्ग में निर्माणों में से एक है :


public URL(String spec) throws MalformedURLException {
   this(null, spec);
}
    

जैसा कि आप देख सकते हैं, हमारे पास एक दिलचस्प जाँच अपवाद है - MalformedURLException । यहां बताया गया है कि इसे कब फेंका जा सकता है (और मैं उद्धृत करता हूं):
"यदि कोई प्रोटोकॉल निर्दिष्ट नहीं है, या कोई अज्ञात प्रोटोकॉल पाया जाता है, या युक्ति शून्य है, या पार्स किया गया URL संबंधित प्रोटोकॉल के विशिष्ट सिंटैक्स का पालन करने में विफल रहता है।"

वह है:

  1. यदि कोई प्रोटोकॉल निर्दिष्ट नहीं है।
  2. एक अज्ञात प्रोटोकॉल मिला है।
  3. विशिष्टता शून्य है ।
  4. URL संबंधित प्रोटोकॉल के विशिष्ट सिंटैक्स का अनुपालन नहीं करता है।

चलिए एक ऐसा तरीका बनाते हैं जो URL ऑब्जेक्ट बनाता है:


public URL createURL() {
   URL url = new URL("https://codegym.cc");
   return url;
}
    

जैसे ही आप इन पंक्तियों को आईडीई में लिखते हैं (मैं आईडीईए में कोडिंग कर रहा हूं, लेकिन यह ग्रहण और नेटबीन में भी काम करता है), आप इसे देखेंगे:

इसका मतलब यह है कि हमें या तो एक अपवाद फेंकना होगा, या कोड को ट्राइ-कैच ब्लॉक में लपेटना होगा। अभी के लिए, मेरा सुझाव है कि जो हो रहा है उसकी कल्पना करने के लिए दूसरा विकल्प चुनें:


public static URL createURL() {
   URL url = null;
   try {
       url = new URL("https://codegym.cc");
   } catch(MalformedURLException e) {
  e.printStackTrace();
   }
   return url;
}
    

जैसा कि आप देख सकते हैं, कोड पहले से ही वर्बोज़ है। और हमने इसका जिक्र ऊपर किया है। अनियंत्रित अपवादों का उपयोग करने के लिए यह सबसे स्पष्ट कारणों में से एक है।

हम Java में RuntimeException को एक्सटेंड करके एक अनियंत्रित एक्सेप्शन बना सकते हैं।

अनियंत्रित अपवाद त्रुटि वर्ग या रनटाइम अपवाद वर्ग से विरासत में मिले हैं। कई प्रोग्रामर महसूस करते हैं कि इन अपवादों को हमारे कार्यक्रमों में नियंत्रित किया जा सकता है क्योंकि वे उन त्रुटियों का प्रतिनिधित्व करते हैं जिनसे हम कार्यक्रम के चलने के दौरान ठीक होने की उम्मीद नहीं कर सकते।

जब एक अनचेक अपवाद होता है, तो यह आमतौर पर गलत तरीके से कोड का उपयोग करने के कारण होता है, जो शून्य या अन्यथा अमान्य तर्क में गुजरता है।

ठीक है, चलो कोड लिखते हैं:


public class OurCoolUncheckedException extends RuntimeException {
   public OurCoolUncheckedException(String message) {
       super(message);
   }

   public OurCoolUncheckedException(Throwable cause) {
       super(cause);
   }
  
   public OurCoolUncheckedException(String message, Throwable throwable) {
       super(message, throwable);
   }
}
    

ध्यान दें कि हमने अलग-अलग उद्देश्यों के लिए कई कंस्ट्रक्टर बनाए हैं। इससे हम अपने अपवाद को और क्षमताएं दे सकते हैं। उदाहरण के लिए, हम इसे ऐसा बना सकते हैं कि एक अपवाद हमें एक त्रुटि कोड देता है। आरंभ करने के लिए, आइए अपने त्रुटि कोड का प्रतिनिधित्व करने के लिए एक एनम बनाएं :


public enum ErrorCodes {
   FIRST_ERROR(1),
   SECOND_ERROR(2),
   THIRD_ERROR(3);

   private int code;

   ErrorCodes(int code) {
       this.code = code;
   }

   public int getCode() {
       return code;
   }
}
    

अब हम अपने अपवाद वर्ग में एक और कंस्ट्रक्टर जोड़ते हैं:


public OurCoolUncheckedException(String message, Throwable cause, ErrorCodes errorCode) {
   super(message, cause);
   this.errorCode = errorCode.getCode();
}
    

और एक फ़ील्ड जोड़ना न भूलें (हम लगभग भूल गए):


private Integer errorCode;
    

और निश्चित रूप से, इस कोड को प्राप्त करने का एक तरीका:


public Integer getErrorCode() {
   return errorCode;
}
    

आइए पूरी कक्षा को देखें ताकि हम इसकी जांच कर सकें और तुलना कर सकें:

public class OurCoolUncheckedException extends RuntimeException {
   private Integer errorCode;

   public OurCoolUncheckedException(String message) {
       super(message);
   }

   public OurCoolUncheckedException(Throwable cause) {
       super(cause);
   }

   public OurCoolUncheckedException(String message, Throwable throwable) {

       super(message, throwable);
   }

   public OurCoolUncheckedException(String message, Throwable cause, ErrorCodes errorCode) {
       super(message, cause);
       this.errorCode = errorCode.getCode();
   }
   public Integer getErrorCode() {
       return errorCode;
   }
}
    

ता-दा! हमारा अपवाद हो गया है! जैसा कि आप देख सकते हैं, यहाँ विशेष रूप से जटिल कुछ भी नहीं है। आइए इसे क्रिया में देखें:


   public static void main(String[] args) {
       getException();
   }
   public static void getException() {
       throw new OurCoolUncheckedException("Our cool exception!");
   }
    

जब हम अपना छोटा एप्लिकेशन चलाते हैं, तो हम कंसोल में निम्नलिखित जैसा कुछ देखेंगे:

आइए अब हमारे द्वारा जोड़ी गई अतिरिक्त कार्यक्षमता का लाभ उठाएं। हम पिछले कोड में थोड़ा सा जोड़ेंगे:


public static void main(String[] args) throws Exception {

   OurCoolUncheckedException exception = getException(3);
   System.out.println("getException().getErrorCode() = " + exception.getErrorCode());
   throw exception;

}

public static OurCoolUncheckedException getException(int errorCode) {
   return switch (errorCode) {
   case 1:
       return new OurCoolUncheckedException("Our cool exception! An error occurred: " + ErrorCodes.FIRST_ERROR.getCode(), new Throwable(), ErrorCodes.FIRST_ERROR);
   case 2:
       return new OurCoolUncheckedException("Our cool exception! An error occurred: " + ErrorCodes.SECOND_ERROR.getCode(), new Throwable(), ErrorCodes.SECOND_ERROR);
   default: // Since this is the default action, here we catch the third and any other codes that we have not yet added. You can learn more by reading Java switch statement
       return new OurCoolUncheckedException("Our cool exception! An error occurred: " + ErrorCodes.THIRD_ERROR.getCode(), new Throwable(), ErrorCodes.THIRD_ERROR);
}

}
    

आप अपवादों के साथ उसी तरह काम कर सकते हैं जिस तरह आप वस्तुओं के साथ काम करते हैं। बेशक, मुझे यकीन है कि आप पहले से ही जानते हैं कि जावा में सब कुछ एक वस्तु है।

और देखिए हमने क्या किया। सबसे पहले, हमने विधि को बदल दिया, जो अब नहीं फेंकता है, बल्कि इनपुट पैरामीटर के आधार पर केवल एक अपवाद बनाता है। अगला, स्विच-केस स्टेटमेंट का उपयोग करते हुए, हम वांछित त्रुटि कोड और संदेश के साथ एक अपवाद उत्पन्न करते हैं। और मुख्य विधि में, हम निर्मित अपवाद प्राप्त करते हैं, त्रुटि कोड प्राप्त करते हैं, और इसे फेंक देते हैं।

आइए इसे चलाते हैं और देखते हैं कि हमें कंसोल पर क्या मिलता है:

देखो - हमने उस त्रुटि कोड को प्रिंट किया जो हमें अपवाद से मिला और फिर अपवाद को ही फेंक दिया। इतना ही नहीं, हम यह भी ट्रैक कर सकते हैं कि अपवाद कहां फेंका गया था। आवश्यकतानुसार, आप संदेश में सभी प्रासंगिक जानकारी जोड़ सकते हैं, अतिरिक्त त्रुटि कोड बना सकते हैं और अपने अपवादों में नई सुविधाएँ जोड़ सकते हैं।

खैर, आप इसके बारे में क्या सोचते हैं? मुझे उम्मीद है कि सब कुछ आपके लिए काम कर गया!

सामान्य तौर पर, अपवाद एक व्यापक विषय है और स्पष्ट कटौती नहीं है। इस पर और भी कई विवाद होंगे। उदाहरण के लिए, केवल जावा ने अपवादों की जाँच की है। सबसे लोकप्रिय भाषाओं में से, मैंने ऐसा कोई नहीं देखा जो उनका उपयोग करता हो।

ब्रूस एकेल ने अपनी पुस्तक "थिंकिंग इन जावा" के अध्याय 12 में अपवादों के बारे में बहुत अच्छा लिखा है - मेरा सुझाव है कि आप इसे पढ़ें! होर्स्टमैन के "कोर जावा" के पहले खंड पर भी नज़र डालें - इसमें अध्याय 7 में बहुत सारी रोचक सामग्री भी है।

एक छोटा सारांश

  1. लॉग में सब कुछ लिखें! फेंके गए अपवादों में संदेशों को लॉग करें। यह आमतौर पर डिबगिंग में बहुत मदद करेगा और आपको यह समझने की अनुमति देगा कि क्या हुआ। कैच ब्लॉक को खाली न छोड़ें , अन्यथा यह अपवाद को "निगल" लेगा और आपके पास समस्याओं का पता लगाने में मदद करने के लिए कोई जानकारी नहीं होगी।

  2. जब अपवादों की बात आती है, तो उन सभी को एक साथ पकड़ना बुरा अभ्यास है (जैसा कि मेरे एक सहयोगी ने कहा, "यह पोकेमॉन नहीं है, यह जावा है"), इसलिए कैच (अपवाद ई) या इससे भी बदतर, कैच (थ्रोएबल टी) से बचें ।

  3. जितनी जल्दी हो सके अपवादों को फेंक दें। यह अच्छा जावा प्रोग्रामिंग अभ्यास है। जब आप स्प्रिंग जैसे ढांचे का अध्ययन करते हैं, तो आप देखेंगे कि वे "तेजी से विफल" सिद्धांत का पालन करते हैं। यही है, वे जितनी जल्दी हो सके "विफल" हो जाते हैं ताकि त्रुटि को जल्दी से ढूंढना संभव हो सके। बेशक, यह कुछ असुविधाएँ लाता है। लेकिन यह दृष्टिकोण अधिक मजबूत कोड बनाने में मदद करता है।

  4. कोड के अन्य भागों को कॉल करते समय, कुछ अपवादों को पकड़ना सबसे अच्छा होता है। यदि कॉल किया गया कोड कई अपवादों को फेंकता है, तो यह केवल उन अपवादों के मूल वर्ग को पकड़ने के लिए खराब प्रोग्रामिंग अभ्यास है। उदाहरण के लिए, मान लें कि आप उस कोड को कॉल करते हैं जो FileNotFoundException और IOException को फेंकता है । आपके कोड में जो इस मॉड्यूल को कॉल करता है, प्रत्येक अपवाद को पकड़ने के लिए दो कैच ब्लॉक लिखना बेहतर है, अपवाद को पकड़ने के लिए एक कैच के बजाय ।

  5. अपवादों को तभी पकड़ें जब आप उन्हें उपयोगकर्ताओं के लिए और डिबगिंग के लिए प्रभावी ढंग से संभाल सकें।

  6. अपने स्वयं के अपवाद लिखने में संकोच न करें। बेशक, जावा में बहुत सारे तैयार किए गए हैं, हर अवसर के लिए कुछ है, लेकिन कभी-कभी आपको अभी भी अपने "पहिया" का आविष्कार करने की आवश्यकता होती है। लेकिन आपको स्पष्ट रूप से समझना चाहिए कि आप ऐसा क्यों कर रहे हैं और सुनिश्चित करें कि अपवादों के मानक सेट में वह नहीं है जिसकी आपको आवश्यकता है।

  7. जब आप अपना अपवाद वर्ग बनाते हैं, तो नामकरण के बारे में सावधान रहें! आप शायद पहले से ही जानते हैं कि कक्षाओं, चरों, विधियों और पैकेजों को सही ढंग से नाम देना अत्यंत महत्वपूर्ण है। अपवाद कोई अपवाद नहीं हैं! :) हमेशा शब्द अपवाद के साथ समाप्त करें , और अपवाद का नाम स्पष्ट रूप से त्रुटि के प्रकार को दर्शाता है जो यह दर्शाता है। उदाहरण के लिए, FileNotFoundException

  8. अपने अपवादों का दस्तावेजीकरण करें। हम अपवादों के लिए @throws Javadoc टैग लिखने की सलाह देते हैं। यह विशेष रूप से उपयोगी होगा जब आपका कोड किसी भी प्रकार का इंटरफेस प्रदान करता है। और आपको बाद में अपना खुद का कोड समझने में भी आसानी होगी। आप क्या सोचते हैं, आप कैसे निर्धारित कर सकते हैं कि MalformedURLException किस बारे में है? जावाडोक से! हां, दस्तावेज लिखने का विचार बहुत आकर्षक नहीं है, लेकिन मेरा विश्वास करो, छह महीने बाद जब आप अपने स्वयं के कोड पर लौटेंगे तो आप स्वयं को धन्यवाद देंगे।

  9. संसाधनों को जारी करें और कोशिश-के-संसाधनों के निर्माण की उपेक्षा न करें ।

  10. यहाँ समग्र सारांश है: अपवादों का बुद्धिमानी से उपयोग करें। संसाधनों के मामले में अपवाद फेंकना काफी "महंगा" ऑपरेशन है। कई मामलों में, अपवादों को फेंकने से बचना आसान हो सकता है और बदले में, एक बूलियन वैरिएबल कहें कि क्या ऑपरेशन सफल हुआ, एक सरल और "कम खर्चीला" if-else का उपयोग करके ।

    एप्लिकेशन लॉजिक को अपवादों से जोड़ना भी लुभावना हो सकता है, जो आपको स्पष्ट रूप से नहीं करना चाहिए। जैसा कि हमने लेख की शुरुआत में कहा था, अपवाद असाधारण स्थितियों के लिए हैं, अपेक्षित नहीं हैं, और उन्हें रोकने के लिए विभिन्न उपकरण हैं। विशेष रूप से, NullPointerException , या Scanner.hasNext को रोकने के लिए वैकल्पिक है और IOException को रोकने के लिए पसंद है , जिसे पढ़ने () विधि फेंक सकती है।