المسارات
/
Java
Java
/
التمارين
/
لغز الآلة الحاسبة
لغز الآلة الحاسبة

لغز الآلة الحاسبة

تمرين تعلّمي

مقدمة

الاستثناءات

تستخدم لغة البرمجة Java الاستثناءات للتعامل مع الأخطاء والأحداث الاستثنائية الأخرى.

ما هو الاستثناء

الاستثناء حدث يقع أثناء تنفيذ البرنامج فيعطّل التدفق الطبيعي للتعليمات. تُثار الاستثناءات صراحةً في Java، وتُسمى عملية إثارتها رمي الاستثناء. وتُسمى عملية التعامل مع الاستثناء التقاط الاستثناء.

في Java، جميع الاستثناءات أصناف فرعية من الصنف Exception، وهو نفسه صنف فرعي من Throwable.

تميّز Java بين نوعين من الاستثناءات:

  1. الاستثناءات المُتحقَّق منها
  2. الاستثناءات غير المُتحقَّق منها
الاستثناءات المُتحقَّق منها

الاستثناءات المُتحقَّق منها هي حالات استثنائية ينبغي للتطبيق أن يتوقعها ويتعافى منها. ومن أمثلتها 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 = 67، عند تزويدها بالوسائط 16 و51 و+.

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"

1. طبّق الطريقة calculate لدعم بعض العمليات الأساسية

الطريقة الرئيسية للتنفيذ في هذه المهمة ستكون الطريقة CalculatorConundrum.calculate(). تأخذ هذه الطريقة ثلاثة وسائط. الوسيطان الأولان operand1 وoperand2 هما عددان صحيحان ستُجرى عليهما العملية. الوسيط الثالث operation من النوع String، ومن الضروري في هذا التمرين تنفيذ العمليات التالية:

  • الجمع باستخدام السلسلة النصية +
  • الضرب باستخدام السلسلة النصية *
  • القسمة باستخدام السلسلة النصية /

2. عالج العمليات غير الصالحة

حدّث الطريقة 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"

3. عالج الاستثناء الذي يُرمى عند القسمة على صفر

في 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"
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Java Exercism

مستعد لبدء لغز الآلة الحاسبة؟

سجّل في Exercism لتتعلّم وتتقن Java عبر 26 مفهومًا158 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.