زبان برنامهنویسی Java برای رسیدگی به خطاها و سایر رویدادهای استثنایی از «استثنا»ها استفاده میکند.
استثنا رویدادی است که در حین اجرای یک برنامه رخ میدهد و جریان عادی دستورها را مختل میکند. استثناها در Java بهصورت صریح ایجاد میشوند و به عمل ایجاد یک استثنا «پرتاب استثنا» میگویند. به عمل رسیدگی به یک استثنا «گرفتن استثنا» میگویند.
در Java، همهی استثناها زیرکلاسهای کلاس Exception هستند که خودش زیرکلاسی از Throwable است.
Java دو نوع استثنا را از هم تفکیک میکند:
«استثناهای بررسیشده» شرایط استثناییای هستند که یک برنامه باید آنها را پیشبینی کند و از آنها بهبود یابد.
نمونهای از استثنای بررسیشده، FileNotFoundException است که وقتی رخ میدهد که یک متد در تلاش است فایلی را بخواند که وجود ندارد.
این نوع استثنا در زمان کامپایل بررسی میشود: متدهایی که استثنای بررسیشده پرتاب میکنند باید این را در امضای متد خود مشخص کنند، و کدی که متدی را فراخوانی میکند که ممکن است استثنای بررسیشده پرتاب کند، ملزم است آن را رسیدگی کند، وگرنه کد کامپایل نمیشود.
همهی استثناهای بررسیشده زیرکلاسهای Exception هستند که RuntimeException را گسترش نمیدهند.
«استثناهای بررسینشده» شرایط استثناییای هستند که یک برنامه معمولاً نمیتواند آنها را پیشبینی کند یا از آنها بهبود یابد.
نمونهای از استثنای بررسینشده، NullPointerException است که وقتی رخ میدهد که متدی در انتظار مقداری غیر از null است اما null دریافت میکند.
این نوع استثنا در زمان کامپایل بررسی نمیشود: متدهایی که استثنای بررسینشده پرتاب میکنند ملزم نیستند این را در امضای متد خود مشخص کنند، و کدی که متدی را فراخوانی میکند که ممکن است استثنای بررسینشده پرتاب کند، ملزم نیست آن را رسیدگی کند.
همهی استثناهای بررسینشده از RuntimeException ارث میبرند که خودش گسترشی از Exception است.
یک متد در Java میتواند با استفاده از دستور throw یک استثنا پرتاب کند.
وقتی استثنای بررسیشدهای از یک متد پرتاب میشود، لازم است این در امضای متد با استفاده از کلیدواژهی throws مشخص شود، همانطور که در مثال زیر نشان داده شده است.
این کار کد فراخوان را مجبور میکند پیشبینی کند که ممکن است استثنایی پرتاب شود و بر همان اساس آن را رسیدگی کند.
public class InsufficientBalanceException extends Exception {
}
public class BankAccount {
public void withdraw(double amount) throws InsufficientBalanceException {
if (balance < amount) {
throw new InsufficientBalanceException();
}
// rest of the method implementation
}
}
وقتی استثنای بررسینشدهای از یک متد پرتاب میشود، لازم نیست این در امضای متد مشخص شود، هرچند پشتیبانی میشود.
public class BankAccount {
public void withdraw(double amount) {
if (amount < 0) {
throw new IllegalArgumentException("Cannot withdraw a negative amount");
}
// rest of the method implementation
}
}
رسیدگی به استثناها در Java با کلیدواژههای try، catch و finally انجام میشود.
try پیچیده شوند.try یک یا چند بلوک catch میآید که استثناهای پرتابشده در بلوک try را میگیرند.catch بهصورت اختیاری یک بلوک finally میآید که همیشه پس از بلوک try اجرا میشود، صرفنظر از اینکه استثنایی پرتاب شده باشد یا نه.مثال زیر نشان میدهد که این کلیدواژهها چگونه کار میکنند:
public class ATM {
public void withdraw(BankAccount bankAccount, double amount) {
try {
System.out.println("Withdrawing " + amount);
bankAccount.withdraw(amount);
System.out.println("Withdrawal succeeded");
} catch (InsufficientBalanceException) {
System.out.println("Withdrawal failed: insufficient balance");
} catch (RuntimeException e) {
System.out.println("Withdrawal failed: " + e.getMessage());
} finally {
System.out.println("Current balance: " + bankAccount.getBalance());
}
}
}
در این مثال، وقتی هیچ استثنایی پرتاب نمیشود، این خروجی چاپ میشود:
Withdrawing 10.0
Withdrawal succeeded
Current balance: 5.0
اما اگر دستور bankAccount.withdraw(amount) یک InsufficientBalanceException پرتاب کند، این خروجی چاپ میشود:
Withdrawing 10.0
Withdrawal failed: insufficient balance
Current balance: 5.0
یا اگر استثنای بررسینشدهای توسط bankAccount.withdraw(amount) پرتاب شود، این خروجی چاپ میشود:
Withdrawing -10.0
Withdrawal failed: Cannot withdraw a negative amount
Current balance: 5.0
Java دستهی جداگانهای هم دارد به اسم «خطا»ها که مسائل جدیای خارج از یک برنامه هستند.
نمونهای از خطا، OutOfMemoryError است که وقتی رخ میدهد که یک برنامه در تلاش است حافظهای بیشتر از حافظهی موجود در سیستم استفاده کند.
همانند استثناهای بررسینشده، خطاها در زمان کامپایل بررسی نمیشوند. تفاوت این است که آنها نمایندهی مسائل سطح سیستم هستند و عموماً توسط ماشین مجازی Java یا محیط پرتاب میشوند، نه توسط برنامه. برنامهها عموماً نباید تلاش کنند آنها را بگیرند یا رسیدگی کنند.
همهی خطاها در Java از کلاس Error ارث میبرند.
در این تمرین، مدیریت خطا را برای یک ماشینحساب سادهی اعداد صحیح پیادهسازی میکنید. برای اینکه کار سادهتر شود، متدهایی برای محاسبهی جمع، ضرب و تقسیم در اختیار شما قرار داده شده است.
هدف این است که یک ماشینحساب کارآمد داشته باشید که با دریافت آرگومانهای 16، 51 و +، یک String با الگوی زیر برگرداند: 16 + 51 = 67.
CalculatorConundrum calculator = new CalculatorConundrum();
calculator.calculate(16, 51, "+");
// => returns "16 + 51 = 67"
calculator.calculate(32, 6, "*");
// => returns "32 * 6 = 192"
calculator.calculate(512, 4, "/");
// => returns "512 / 4 = 128"
calculate برای پشتیبانی از چند عملیات پایهمتد اصلی که در این تمرین باید پیادهسازی کنید، متد CalculatorConundrum.calculate() است.
این متد سه آرگومان میگیرد.
دو آرگومان اول یعنی operand1 و operand2 اعداد صحیحی هستند که عملیات روی آنها انجام میشود.
آرگومان سوم یعنی operation از نوع String است و در این تمرین باید عملیات زیر را پیادهسازی کنید:
+ است* است/ استمتد CalculatorConundrum.calculate() را بهروزرسانی کنید تا عملیات غیرمجاز را مدیریت کند:
operation برابر null باشد، باید یک IllegalArgumentException با پیام Operation cannot be null پرتاب شود.operation برابر "" باشد، باید یک IllegalArgumentException با پیام Operation cannot be empty پرتاب شود.operation هر عملیاتی بهجز +، * یا / باشد، باید یک IllegalOperationException با پیام Operation '{operation}' does not exist پرتاب شود.calculator.calculate(10, 1, null);
// => throws IllegalArgumentException with message "Operation cannot be null"
calculator.calculate(10, 1, "");
// => throws IllegalArgumentException with message "Operation cannot be empty"
calculator.calculate(10, 1, "-");
// => throws IllegalOperationException with message "Operation '-' does not exist"
در Java، تلاش برای تقسیم بر صفر یک ArithmeticException پرتاب میکند.
متد CalculatorConundrum.calculate() را بهروزرسانی کنید تا این استثنا را بگیرد و سپس یک IllegalOperationException با پیام Division by zero is not allowed و ArithmeticException گرفتهشده بهعنوان علت آن پرتاب کند.
calculator.calculate(512, 0, "/");
// => throws IllegalOperationException with message "Division by zero is not allowed"