O'ylaymanki, kodni ishga tushirganingda NullPointerException, ClassCastException yoki undan battar narsalar kelib chiqqan vaziyatlarga duch kelgansan. Keyin uzoq debugging, tahlil, googling va hokazo. Exceptions - zo'r narsa: ular muammo qaerda va qandayligini ko'rsatadi. Agar xotirani yangilashni xohlasang yoki batafsil bilishni istasang, maqolani ko'rib chiq Exceptions: checked, unchecked va o'zimiznikilar (javarush.ru)
Lekin o'zimizning exceptions yaratishga ehtiyoj bo'lgan vaziyatlar ham bo'lishi mumkin. Masalan, koding uzoq servisdankutimdan ma'lumot so'rashi kerak, lekin u qandaydir sabablar bilan mavjud emas. Yoki biror odam bank kartasi uchun ariza to'ldiradi va telefon raqamini kiritadi, lekin bu raqam allaqachon tizimda mavjud va boshqa foydalanuvchiga tegishli bo'lsa.
Albatta, bu buyurtmachining talablaridan va tizim arxitekturasidan ham bog'liq, lekin telefon raqamini tekshirish vazifasi berildi deb faraz qilamiz va raqam allaqachon ishlatilayotgan bo'lsa - exception chiqarish kerak.
Exception yaratamiz:
public class PhoneNumberIsExistException extends Exception {
public PhoneNumberIsExistException(String message) {
super(message);
}
}
So'ng uni tekshirishda ishlatamiz:
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 PhoneNumberIsExistException {
if (registeredPhoneNumbers.contains(phoneNumber)) {
throw new PhoneNumberIsExistException("Ko'rsatilgan telefon raqami boshqa mijoz tomonidan foydalanilmoqda!");
}
}
}
Vazifani yengillashtirish uchun bir nechta telefon raqamlarini "hardkodlaymiz" — let this be our database. Va nihoyat, exceptionni qo'llashga harakat qilamiz:
public class CreditCardIssue {
public static void main(String[] args) {
PhoneNumberRegisterService service = new PhoneNumberRegisterService();
try {
service.validatePhone("+1-111-111-11-14");
} catch (PhoneNumberIsExistException e) {
// logga yozish yoki stack trace chiqarish mumkin
e.printStackTrace();
}
}
}
Xo'sh, Shift+F10 tugmasini bosish vaqti keldi (IDEA ishlatayotgan bo'lsang), ya'ni loyihani ishga tushirish. Konsolda quyidagi narsalarni ko'rasan:
exception.PhoneNumberIsExistException: Ko'rsatilgan telefon raqami boshqa mijoz tomonidan foydalanilmoqda!
at exception.PhoneNumberRegisterService.validatePhone(PhoneNumberRegisterService.java:11)
Xo'sh, sen o'z exceptions yaratding va hatto uni biroz sinab ko'rding. Tabriklayman! Uni yaxshiroq tushunish uchun biroz eksperiment qilib ko'rishni tavsiya qilaman.
Boshqasini qo'sh: masalan, harflar borligini tekshirish uchun. Ehtimol bilasan, AQShda raqamni eslab qolishni osonlashtirish uchun harflardan foydalangan holda, 1-800-MY-APPLE kabi raqamlar ishlatiladi. Shunday qilib, raqamda faqat raqamlar mavjudligiga ishonch hosil qilishing kerak.
Bazaviy, ya'ni, checked exceptions yaratdik. Har narsasi yaxshi, lekin...
Dasturchilar jamiyati ikki guruhga bo'lingan - checked exceptions tarafdorlari va qarshilar. Har ikki tomon jiddiy dalillar keltiradi. Ularning orasida yuqori darajadagi dasturchilar bor: Bruce Eckel checked exceptions kontseptsiyasini tanqid qiladi, James Gosling esa himoya qiladi. Aftidan, bu masala hech qachon yakuniy ravishda hal bo'lmaydi. Shunday bo‘lsa-da, checked exceptionsdan foydalanishni asosiy kamchiliklarini ko‘rib chiqaylik.
Checked exceptionsning asosiy kamchiligi — ular bilan ishlash kerak. Va bizda ikki variant bor: joyida ishlatish try-catch yoki, agar bitta exception ko'p joylarda ishlatiladigan bo'lsa, throws yordamida yuqoriga chiqarish va yuqori darajadagi sinflarda ishlatish.
Shuningdek, bizda ko'p kod, yoki ba'zan "boilerplate" deb nomlanuvchi, ya'ni ko'p joy egallaydigan, lekin semantik yukni ko'tarmaydigan kod paydo bo'lishi mumkin.
Kifoya yetadigan katta ilovalarda muammolar boshlanadi: exceptions ko'payadi va yuqori darajadagi metod ko'p throws bilan to'ldirilishi mumkin.
Ko'pincha dasturchilar buni yoqtirmaydi va hiyla-nayrang foydalanadilar: barcha checked exceptionsni bitta ajdoddan, ApplicationNameExceptiondan meros qilib olish. Endi ular ishlovchi ichida uni ham tutishlari shart (checked bo'lsa!):
catch(FirstException e){
// todo
}
catch(SecondException e){
// todo
}
catch(ThirdException e){
// todo
}
catch(ApplicationNameException e){
// todo
}
Bu bizni yana bir muammoga olib keladi: oxirgi catchda nima qilish kerak? Yuqorida keltirilgan barcha keng tarqalgan vaziyatlar uchun rejalashtirilgan, ammo bu erda ApplicationNameException biz uchun Exceptiondan ko'proq ma'noga ega emas: "nomalum xato". Shunday qilib ishlov beramiz:
catch(ApplicationNameException e){
LOGGER.error("Unknown error", e.getMessage());
}
Natijada, nima sodir bo'lganini bilmaymiz.
Biroq, barcha narsalarni bitta harakat bilan amalga oshirish mumkin deb o'ylaysiz:
public void ourCoolMethod() throws Exception{
// do some
}
Ha, mumkin. Ammo "throws Exception" qanday ma'lumot beradi? Nimadir buzildi. Hammasini boshdan oxirigacha tekshirish kerak bo'ladi, va sababni aniqlash uchun debugger bilan uzoq vaqt do'st bo'lasiz.
Bundan tashqari, dam olish va "tutib turish" deb nomlanuvchi konstruktsiyani topishingiz mumkin:
try{
// some code
} catch(Exception e){
throw new ApplicationNameException("Error");
}
Bu yerda ortiqcha so'zlar kerak emas - kodning o'zi tushunarli, yoki, aniqrog'i, hech narsa tushunarsiz.
Albatta, siz buni haqiqiy kodda uchratmaslikka e'tiroz bildirishingiz mumkin. Yaxshi, java.net paketidagi URL sinfini ko'rib chiqaylik va uning ichki qismiga, kodiga osonlik bilan kirib ko'ring. Follow me if you want to know!
URLning bir konstruktori:
public URL(String spec) throws MalformedURLException {
this(null, spec);
}
Bu yerda qiziqarli checked exceptions MalformedURLException borligiga e'tibor bering. Va bu quyidagi holatda tashlanadi, keltirilgan matnga ko'ra:
agar hech qanday protokol keltirilmagan bo'lsa, yoki noma'lum protokol topilgan bo'lsa, yoki spetsifikatsiya null bo'lsa, yoki URL manzili tegishli protokolning aniq sintaksisiga mos kelmasa.
Yani:
- Agar protokol keltirilmagan bo'lsa.
- Topilgan noma'lum protokol bo'lsa.
- Spetsifikatsiya null qiymatiga ega bo'lsa.
- URL manzili tegishli protokolning aniq sintaksisiga mos kelmasa.
URL sinfining objektini yaratadigan metodni yaratamiz:
public URL createURL() {
URL url = new URL("https://javarush.com");
return url;
}
IDE'ya ushbu qatorlarni yozganingizda (men IDEA'da yozaman, lekin hattoki Eclipse va NetBeansda ham bu ishlaydi), siz bu narsani ko'rasiz:
Bu bizga exceptionsni tashlash yoki "try-catch"ga o'rab olish kerakligini aytadi. Hozircha ikkinchi variantni tanlashni tavsiya qilaman, natijani ko'rib chiqish uchun:
public static URL createURL() {
URL url = null;
try {
url = new URL("https://javarush.com");
} catch(MalformedURLException e){
e.printStackTrace();
}
return url;
}
Ko'rib turganingizdek, u allaqachon ko'pgina so'zlar bilan ifoda etildi. Aslida, yuqorida bu haqida aytilgan. Bu checked exceptionsdan foydalanishni eng muhim sabablardan biri.
Biz Java'da RuntimeException kengaytirib bunday exceptions yaratishimiz mumkin.
Unchecked exceptions Error yoki RuntimeException sinflaridan meros olinadi. Ko'pgina dasturchilar, bu exceptions bizning dasturlarimizda ishlov berishimiz mumkin emas deb hisoblashadi, chunki ular odatda ishlashda kodning qayta tiklanishini kutib bo'lmaydigan xatolar turini ifoda etishadi.
Unchecked exceptions odatda noto'g'ri kod ishlatilishi, null yoki boshqa noan'anaviy argument berilishi natijasida yuzaga keladi.
Xo'sh, kodingni yozamiz:
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);
}
}
E'tibor bering: biz maqsadlar uchun bir nechta konstruktorlarni yaratdik. Bu bizning exceptions imkoniyatlarini kengaytirish imkonini beradi. Masalan, exceptions bizga xato kodini chiqarish imkoniyatiga ega bo'lishi mumkin. Avvalo, enumni yaratamiz, unda aslida bizning xato kodlarimiz yotadi:
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;
}
}
Va endi exceptions klassiga yana bir konstruktor qo'shamiz:
public OurCoolUncheckedException(String message, Throwable cause, ErrorCodes errorCode) {
super(message, cause);
this.errorCode = errorCode.getCode();
}
Va ha, deyarli unutgan edik, xato kodini saqlash uchun maydon qo'shamiz:
private Integer errorCode;
Va albatta, bu kodni olish uchun metod:
public Integer getErrorCode() {
return errorCode;
}
Sinflarning to'liq ko'rinishini ko'rib chiqamiz, toki sinchkovlik bilan ko'rib chiqib va solishtirib olishingiz mumkin bo'lsin:
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;
}
}
Bizning exceptions hozir tayyor! Ko'rib turganingizdek, hech qanday maxsus qiyinchilik yo'q. Uni amalda tekshirib ko'raylik:
public static void main(String[] args) {
getException();
}
public static void getException(){
throw new OurCoolUncheckedException("Bizning ajoyib exceptions!");
}
Bizning kichik ilovamizni ishga tushirib, konsolda quyidagi narsalarni ko'ramiz:
Endi qo'shimcha funksionallikdan foydalanamiz, qo'shganimiz. Oldingi kodni biroz kengaytiramiz:
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("Bizning ajoyib exceptions! Xato kodini oldik: " + ErrorCodes.FIRST_ERROR.getCode(), new Throwable(), ErrorCodes.FIRST_ERROR);
case 2:
return new OurCoolUncheckedException("Bizning ajoyib exceptions! Xato kodini oldik: " + ErrorCodes.SECOND_ERROR.getCode(), new Throwable(), ErrorCodes.SECOND_ERROR);
default: // bu erda uchinchi kodingni va hali qo'shmagan barcha kodlarni ushlab olamiz, ya'ni bu standart amal. Bu haqda ko'proq ma'lumot olish uchun bu yerni ko'rib chiqishingiz mumkin Switch case Java (java'da switch operatori) (javarush.ru)
return new OurCoolUncheckedException("Bizning ajoyib exceptions! Xato kodini oldik: " + ErrorCodes.THIRD_ERROR.getCode(), new Throwable(), ErrorCodes.THIRD_ERROR);
}
}
Exceptionsni obyektlar kabi ishlatish mumkin, lekin, aminmanki, siz Java'da hamma narsa obyekt ekanligini allaqachon bilasiz.
Va qarang, nima qildik. Dastlab, metodni o'zgartirdik, bu endi exceptionsni tashlamaydi, balki qaysi parametr kiritilganiga qarab exceptionsni yaratadi. Keyin switch-case yordamida kerakli xato kod va xabar bilan exceptionsni hosil qilamiz. Va asosiy metodda ishlab chiqilgan exceptions oldik, xato kodini chiqardik va exceptionsni tashladik.
Ishga tushirib, konsolga nima chiqishini ko'ramiz:
Va qarang, exceptionsdan olingan xato kodini ko'rsatishga erishdik, keyin exceptionsni tashladik. Shu bilan birga, exceptions qayerda tashlanganini aniqlash mumkin. Zarurat tug'ilganda, kerakli ma'lumotlarni xabarga qo'shishingiz, kerakli xato kodlarini yaratishingiz, exceptionslaringizni yangi imkoniyatlar bilan to'ldirishingiz mumkin.
Xo'sh, qanday? Umid qilamanki, hammasi muvaffaqiyatli bo'ldi!
Umuman olganda, exceptions mavzusi juda keng va unchalik ham aniq emas. Bu yerda ko'p bahslar bo'ladi va ko'p marta sinovlar qilinadi. Masalan, faqat Java'da checked exceptions mavjud. Ko'p tarqalgan tillardan men ularni qo'llaydigan birortasini ko'rmadim.
Exceptions haqida umuman juda yaxshi yozgan Bruce Eckel o'zining “Java Filosofiyasi” kitobida, 12-bobda, tanishib chiqishni tavsiya qilaman! Shuningdek, birinchi jild “Java. Professional kutubxona” Horstmannning 7-bobida juda qizi'qarli ma'lumotlar bor.
Qisqa xulosa
Hamma narsani logga yoz! Logga exceptionsdan chiqishi mumkin bo'lgan xabarlarni yoz. Aksariyat hollarda bu debuggingga juda ko'p yordam beradi va nima bo'lganini tushunishga imkon beradi. Bo'sh catch blokni qoldirma, aks holda u faqat exceptionsni “yutib yuboradi” va muammolarni qidirish uchun ma'lumotlarga ega bo'lmaysan.
Exceptions bilan bog'liq yomon amaliyot - ularni bir martada ushlash (bir hamkasbim aytganidek, bu pokemon emas — bu Java), shuning uchun catch(Exception e) yoki, undan battar, catch(Throwable t)dan qochishga harakat qiling.
Exceptionni iloji boricha erta tashlang. Bu Java dasturlashda yaxshi amaliyotdir. Agar siz Spring kabi frameworkslarni o'rganayotgan bo'lsangiz, u Failed First printsipiga asoslanganligini ko'rasiz. Ya'ni, xatoni tezroq topish uchun iloji boricha erta “yiqilish”. Bu, albatta, ma'lum bir noqulayliklar keltirib chiqaradi. Shunga qaramay, bu yondashuv ishonchliroq kod yaratishga yordam beradi.
Other code qismlarini chaqirishda eng yaxshisi, alohida exceptionsni ushlab olishdir. Agar chaqirilayotgan kod bir nechta exceptionsni hosil qilsa, dasturlashning yomon amaliyoti faqat bu exceptionsning ota sinflarini ushlab olishdir. Masalan, agar chaqirilayotgan kod FileNotFoundException orqali IOException hosil qilsa. Va sizning bu modulni chaqirayotgan kodingiz, o'rniga ikkita catch blokini yozib, har bir exceptionsni ushlab olish maqulroq.
Exceptionsni ushla faqat foydalanuvchi va debugging uchun samarali ishlatish qulay bo'lganda.
O'z exceptionslaringizni yozishdan uyalma. Ha, Java'da har xil ko'p tayyor exceptions bor, lekin ba'zida o'z "velosipedingiz"ni yaratishingiz kerak bo'ladi. Lekin siz aniq nimani qilayotganingizni bilishingiz kerak va sizga kerak bo'lgan tayyor exceptions mavjud emasligiga ishonchingiz komil bo'lishi kerak.
Exceptions sinflarini yaratganda, nomlanishiga e'tibor bering! Siz, ehtimol, allaqachon bilasiz, to'g'ri sinflar, o'zgaruvchilar, metodlar va paketlar nomlanishi nihoyatda muhim. Exceptionslar - exceptions emas (taftologiya uchun uzr)! Har doim Exception so'zini oxiriga qo'ying va exceptions nomi aniq bir xatoni ifoda etishi kerak. Misol — FileNotFoundException.
Exceptionsni hujjatlang. Javadoc @throws exceptionslar uchun yozish mumkin. Bu ayniqsa, har qanday interfeyslar taqdim qilinadigan vaqtida foydali bo'ladi. Va sizga o'zingizning kodni keyinroq tushunishingiz oson bo'ladi. MalformedURLException nima qilishini qaerdan bilish mumkin? Javadocdan! Ha, hujjat yozish perspektivasi sizni xursand qilmaydi, lekin, ishonch hosil qiling, yarim yil o'tib, o'zingizning kodingizga qaytgandan so'ng, siz o'zingizga rahmat deysiz.
Resourcesni ozod qiling va try-with-resources konstruktsiyasini e'tiborsiz qoldirmang. Agar siz hali nimani bilmasangiz, bu yerni ko'rib chiqing — Java 7 try-with-resources.
Bu, ko'proq, umumiy natija: exceptionslar bilan aqlli foydalaning. Exceptionsni tashlash - bu resurslar tomonidan juda "qimmat" amaliyot. Ko'plab hollarda, exceptionsni qaytarmaslik, masalan, oddiy va "arzon" if-elsedan foydalanib, operatsiya qanday o'tganligini ko'rsatuvchi mantiqiy o'zgaruvchi qaytarish ma'qulroq bo'lishi mumkin.
Shuningdek, ilova logikani exceptionsga bog'lash istagi paydo bo'lishi mumkin, buning oldini olish kerak. Exceptions, maqolaning boshida aytilganidek, bu favqulodda vaziyat, standart emas, va ular uchun "profilaktika" turli xil vositalar mavjud. Xususan, bush qiymatiga ega bo'lishdan oldini olish uchun Optional mavjud, yoki Scanner.hasNext va shunga o'xshashlar IOExceptionsni oldini olish uchun, bu read() metodi exceptionsni tashlashini ko'rsatishi mumkin.
GO TO FULL VERSION