تعلم كيفية التفاعل مع المستخدمين باستخدام الكونسول والنوافذ الرسومية
في الوحدات السابقة، تعلمنا أساسيات المتغيرات، أنواع البيانات، وكيفية إجراء العمليات الحسابية والمنطقية. كما تعرفنا على مفهوم الدوال كوسيلة لتنظيم الكود. في هذه الوحدة، سنخطو خطوة إضافية نحو بناء برامج أكثر تفاعلية من خلال تعلم كيفية **الطباعة والإدخال عبر الكونسول** باستخدام فئة `Scanner`، وكيفية استخدام **النوافذ الرسومية البسيطة** (`JOptionPane`) للتفاعل مع المستخدم.
سواء كنت تبني تطبيقات نصية بسيطة أو تتجه نحو الواجهات الرسومية، فإن فهم آليات الإدخال والإخراج أمر حيوي. ستوفر لك هذه الوحدة الأدوات اللازمة لجعل برامجك تتلقى البيانات من المستخدم وتستجيب له بطرق مختلفة.
بنهاية هذه الوحدة، ستكون قادرًا على:
في الوحدات السابقة، كنا نكتب الكود ثم ننفذه مباشرة، بحيث تكون القيم التي نعتمد عليها في البرنامج محددة مسبقًا داخل الكود نفسه. أي أننا كنا نعرف النتيجة قبل تشغيل البرنامج لأننا قمنا بتخزين القيم في المتغيرات أثناء كتابة الكود.
لكن البرمجة التفاعلية تتطلب شيئًا آخر: نريد أن ينفذ البرنامج أوامر معينة بناءً على مدخلات المستخدم، أي أن المستخدم هو من يحدد القيم أثناء تشغيل البرنامج.
في هذا الجزء من الوحدة، سنتعلم كيفية جعل برنامج جافا يتفاعل مع المستخدم عبر **الكونسول (Console)**، بحيث يطلب منه إدخال بيانات من لوحة المفاتيح، ثم يقوم البرنامج بمعالجة هذه البيانات أو استخدامها في تنفيذ عمليات معينة.
الكلاس Scanner هو أحد الكلاسات الجاهزة في مكتبة Java ضمن الحزمة java.util. وظيفته الأساسية هي استقبال البيانات من المستخدم (Input) وتحويلها إلى النوع المناسب من البيانات (int, double, String… إلخ) لمعالجتها.
يُستخدم هذا الكلاس بشكل أساسي لقراءة المدخلات من مصادر نصية مثل الكونسول (لوحة المفاتيح)، الملفات، أو حتى سلاسل نصية (Strings).
يمكن للـ Scanner استقبال معظم أنواع البيانات الأساسية في Java، ومنها:
byte, short, int, long): مثل 123.float, double): مثل 10.55.char): على الرغم من أن `Scanner` لا يمتلك دالة `nextChar()` مباشرة، يمكن قراءة حرف واحد باستخدام `next().charAt(0)`.String): سواء كانت كلمة واحدة أو جملة كاملة.boolean): مثل true أو false.لكل نوع بيانات تريد استقباله في البرنامج، توجد دالة خاصة به في `Scanner`، وسنتعرف عليها بالتفصيل لاحقًا.
لكي نكتب برنامجًا في Java يستقبل بيانات من المستخدم عبر الكونسول، علينا اتباع ثلاث خطوات رئيسية:
هذه الخطوة تتيح لنا استخدام الكلاس Scanner في البرنامج. يجب وضع هذا السطر في بداية ملف الجافا الخاص بك، عادةً بعد تعريف الحزمة (`package`) الخاصة بالبرنامج (إن وجدت) وقبل تعريف الكلاس الرئيسي (`public class`).
import java.util.Scanner;
نحن بحاجة لإنشاء كائن (Object) من الكلاس Scanner ليكون مسؤولاً عن استقبال المدخلات. نمرر System.in كمعامل لكونستركتور Scanner، والذي يعني أن مصدر الإدخال سيكون لوحة المفاتيح (الكونسول).
Scanner input = new Scanner(System.in);
// هنا أنشأنا كائن باسم 'input' (يمكنك اختيار أي اسم) من الكلاس Scanner،
// ليكون مسؤولاً عن استقبال المدخلات من لوحة المفاتيح عبر مجرى الإدخال القياسي (System.in).
بعد إنشاء الكائن، يمكننا استدعاء إحدى دوال الإدخال المتاحة في الكلاس Scanner لاستقبال النوع المحدد من البيانات من المستخدم. على سبيل المثال:
System.out.print("من فضلك أدخل عمرك: "); // نطبع رسالة إرشادية للمستخدم
int age = input.nextInt(); // هذا الكود يجعل البرنامج ينتظر من المستخدم إدخال عدد صحيح، ثم يخزن هذه القيمة في المتغير age.
Scanner بعد الانتهاء من استخدامه لتحرير الموارد التي تم حجزها. يتم ذلك باستخدام الدالة input.close();.input.nextLine(); فارغة بعد كل قراءة لعدد وقبل قراءة سطر كامل لتستهلك المحرف العالق.
هنا سنستخدم دوال مثل nextLine() و nextInt() من الكلاس Scanner.
import java.util.Scanner; // استيراد الكلاس Scanner
public class UserDataCollector {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); // إنشاء كائن Scanner
System.out.print("أدخل اسمك: "); // طلب الاسم
String name = input.nextLine(); // استقبال سطر كامل (الاسم)
System.out.print("أدخل عمرك: "); // طلب العمر
int age = input.nextInt(); // استقبال عدد صحيح (العمر)
// تنبيه: nextInt() لا تستهلك السطر الجديد، مما قد يؤثر على nextLine() اللاحقة
// لذا، نضيف input.nextLine() إضافية لامتصاص السطر الجديد العالق
input.nextLine();
System.out.print("أدخل مهنتك: "); // طلب المهنة
String profession = input.nextLine(); // استقبال سطر كامل (المهنة)
System.out.println("\n--- بيانات المستخدم ---");
System.out.println("الاسم: " + name);
System.out.println("العمر: " + age + " سنة");
System.out.println("المهنة: " + profession);
input.close(); // إغلاق كائن Scanner
}
}
هذا يتطلب معرفة بالمصفوفات (سيتم تغطيتها بتفصيل في وحدات لاحقة)، ولكن هذا مثال يوضح إمكانية إدخال عدة قيم.
import java.util.Scanner; // استيراد الكلاس Scanner
public class StudentGrades {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); // إنشاء كائن Scanner
System.out.print("كم عدد المواد الدراسية؟ ");
int numSubjects = input.nextInt();
input.nextLine(); // امتصاص السطر الجديد
// تعريف مصفوفة لتخزين الدرجات
double[] grades = new double[numSubjects];
System.out.println("الرجاء إدخال علامات المواد:");
for (int i = 0; i < numSubjects; i++) {
System.out.print("علامة المادة " + (i+1) + ": ");
grades[i] = input.nextDouble();
input.nextLine(); // امتصاص السطر الجديد
}
// حساب المعدل
double sum = 0;
for (double grade : grades) {
sum += grade;
}
double average = sum / numSubjects;
System.out.println("\n--- النتائج ---");
System.out.println("عدد المواد: " + numSubjects);
System.out.println("المعدل: " + average);
input.close(); // إغلاق كائن Scanner
}
}
هذا المثال يوضح كيفية أخذ مدخلات عددية وإجراء عمليات حسابية عليها:
import java.util.Scanner;
public class TemperatureConverter {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("أدخل درجة الحرارة بالفهرنهايت: ");
double fahrenheit = input.nextDouble();
double celsius = (fahrenheit - 32) * 5.0/9.0; // استخدم 5.0/9.0 لضمان القسمة العشرية
System.out.println(fahrenheit + " فهرنهايت = " + celsius + " مئوية");
input.close();
}
}
يحتوي الكلاس Scanner على عدة كونستركتورات (Constructors) تسمح بإنشاء كائنات `Scanner` لقراءة المدخلات من مصادر مختلفة. الكونستركتور هو دالة خاصة تُستخدم لإنشاء كائن جديد من الكلاس.
| الكونستركتور | الوصف |
|---|---|
Scanner(File source) |
يقرأ البيانات من ملف. يتطلب استيراد java.io.File وقد يرمي استثناء FileNotFoundException. |
Scanner(InputStream source) |
يقرأ البيانات من مصدر إدخال مباشر (مثل System.in، وهو الأكثر شيوعاً لقراءة مدخلات الكونسول). |
Scanner(Readable source) |
يقرأ البيانات من مصدر قابل للقراءة (مثل BufferedReader). |
Scanner(ReadableByteChannel source) |
يقرأ البيانات من قناة بايت قابلة للقراءة، مفيد لعمليات الإدخال/الإخراج المعقدة. |
Scanner(String source) |
يقرأ البيانات من سلسلة نصية محددة. مفيد لتحليل النصوص الثابتة. |
Scanner(Path source) |
يقرأ البيانات من ملف محدد بواسطة مسار (`Path`). يتطلب استيراد java.nio.file.Path. |
Scanner(File source, String charsetName) |
يقرأ البيانات من ملف مع تحديد ترميز الأحرف. |
import java.io.File;
import java.io.FileNotFoundException; // يجب استيراد هذا الاستثناء
import java.util.Scanner;
public class FileReaderExample {
public static void main(String[] args) {
try {
// قم بإنشاء ملف باسم data.txt في نفس مجلد المشروع وضع فيه بعض النصوص.
// مثال:
// السطر الأول من البيانات
// السطر الثاني من البيانات
Scanner fileScanner = new Scanner(new File("data.txt"));
System.out.println("قراءة محتويات الملف:");
while (fileScanner.hasNextLine()) { // التحقق مما إذا كان هناك سطر آخر للقراءة
System.out.println(fileScanner.nextLine()); // قراءة وطباعة السطر
}
fileScanner.close(); // إغلاق كائن Scanner
} catch (FileNotFoundException e) {
System.err.println("خطأ: الملف data.txt غير موجود."); // طباعة رسالة خطأ
}
}
}
بعد إنشاء كائن `Scanner`، يمكنك استخدام هذه الدوال لقراءة أنواع مختلفة من البيانات:
| الدالة | الوصف | مثال |
|---|---|---|
nextInt() |
تقرأ العدد الصحيح التالي (بنوع `int`). | int age = input.nextInt(); |
nextDouble() |
تقرأ العدد العشري التالي (بنوع `double`). | double price = input.nextDouble(); |
nextFloat() |
تقرأ العدد العشري التالي (بنوع `float`). | float temp = input.nextFloat(); |
nextLong() |
تقرأ العدد الصحيح الطويل التالي (بنوع `long`). | long population = input.nextLong(); |
nextShort() |
تقرأ العدد الصحيح القصير التالي (بنوع `short`). | short code = input.nextShort(); |
nextByte() |
تقرأ البايت التالي (بنوع `byte`). | byte data = input.nextByte(); |
nextBoolean() |
تقرأ القيمة المنطقية التالية (`true` أو `false`). | boolean flag = input.nextBoolean(); |
next() |
تقرأ الكلمة التالية (سلسلة نصية `String`) حتى أول مسافة بيضاء أو فاصل. | String word = input.next(); |
nextLine() |
تقرأ السطر بأكمله (سلسلة نصية `String`) حتى محرف السطر الجديد (`\n`). | String line = input.nextLine(); |
هذا فرق جوهري ويسبب التباسًا للكثيرين:
next(): تقرأ **كلمة واحدة** فقط. تتوقف عن القراءة عند أول مسافة بيضاء (` `، `\t`، `\n`).nextLine(): تقرأ **السطر بأكمله**. تستمر في القراءة حتى تجد محرف السطر الجديد (`\n`).تذكر دائماً استخدام input.nextLine(); فارغة بعد قراءة الأرقام أو الكلمات الفردية إذا كنت تخطط لقراءة سطر كامل بعدها، لتجنب قراءة المحرف العالق.
هذه الدوال تسمح لك بالتحقق مما إذا كان هناك بيانات من نوع معين متاحة للقراءة في مجرى الإدخال *قبل* محاولة قراءتها. تُرجع هذه الدوال `true` إذا كان النوع المتوقع متاحًا، و `false` بخلاف ذلك. هذا يساعد على منع أخطاء إدخال النوع غير المتطابق (`InputMismatchException`).
| الدالة | الوصف | مثال |
|---|---|---|
hasNext() |
تتحقق مما إذا كان هناك المزيد من الـ "رموز" (tokens) المتاحة للقراءة (بناءً على الفاصل الحالي). | if (input.hasNext()) { // ... } |
hasNextInt() |
تتحقق مما إذا كان الرمز التالي يمكن تفسيره كعدد صحيح (`int`). | if (input.hasNextInt()) { // ... } |
hasNextDouble() |
تتحقق مما إذا كان الرمز التالي يمكن تفسيره كعدد عشري (`double`). | if (input.hasNextDouble()) { // ... } |
hasNextLine() |
تتحقق مما إذا كان هناك سطر آخر متاح للقراءة. | if (input.hasNextLine()) { // ... } |
hasNextBoolean() |
تتحقق مما إذا كان الرمز التالي يمكن تفسيره كقيمة منطقية (`boolean`). | if (input.hasNextBoolean()) { // ... } |
import java.util.Scanner;
public class InputValidationExample {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("أدخل عددًا صحيحًا: ");
if (input.hasNextInt()) { // التحقق قبل القراءة
int number = input.nextInt();
System.out.println("لقد أدخلت عددًا صحيحًا: " + number);
} else {
System.out.println("خطأ: لم تدخل عددًا صحيحًا!");
}
input.close();
}
}
هذه الدوال تسمح لك بتعديل سلوك الـ `Scanner` أو الحصول على معلومات حول حالته:
| الدالة | الوصف | مثال |
|---|---|---|
ioException() |
تُرجع آخر استثناء `IOException` حدث أثناء عمليات الإدخال. | IOException e = input.ioException(); |
radix() |
تُرجع أساس نظام الأعداد (Base) الذي يستخدمه `Scanner` لتفسير الأرقام (الافتراضي هو 10 للنظام العشري). | int currentRadix = input.radix(); // غالباً سيكون 10 |
useRadix(int radix) |
تحدد أساس نظام الأعداد الذي سيستخدمه `Scanner` لتفسير الأرقام. (مثال: 16 للنظام الست عشري). | input.useRadix(16); // لتمكين قراءة الأرقام الست عشرية |
delimiter() |
تُرجع نمط الفاصل (`Pattern`) الذي يستخدمه `Scanner` حاليًا للفصل بين الرموز. | Pattern p = input.delimiter(); |
useDelimiter(String pattern) |
تحدد نمط فاصل جديد سيستخدمه `Scanner` للفصل بين الرموز بدلاً من المسافات البيضاء الافتراضية. | input.useDelimiter(","); // استخدام الفاصلة كفاصل |
locale() |
تُرجع الإعدادات المحلية (`Locale`) التي يستخدمها `Scanner` (تؤثر على كيفية قراءة الأرقام العشرية والتاريخ والوقت). | Locale currentLocale = input.locale(); |
useLocale(Locale locale) |
تحدد إعدادات محلية جديدة لـ `Scanner`. | input.useLocale(Locale.US); // استخدام الإعدادات المحلية الأمريكية |
skip(String pattern) |
تتخطى أي مدخلات مطابقة للنمط المحدد. | input.skip("\\s+"); // تخطي المسافات البيضاء المتتالية |
close() |
يغلق كائن `Scanner` ويحرر أي موارد نظام مرتبطة به. **مهم جدًا استدعائها عند الانتهاء.** | input.close(); |
import java.util.Scanner;
public class CustomDelimiterExample {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
input.useDelimiter(","); // تعيين الفاصلة كفاصل بين المدخلات بدلاً من المسافة
System.out.println("أدخل 3 أرقام صحيحة مفصولة بفواصل (مثال: 10,20,30): ");
try {
int a = input.nextInt();
int b = input.nextInt();
int c = input.nextInt();
System.out.println("الأرقام المدخلة: " + a + ", " + b + ", " + c);
System.out.println("المجموع: " + (a + b + c));
} catch (java.util.InputMismatchException e) {
System.err.println("خطأ في الإدخال: تأكد من إدخال أرقام صحيحة مفصولة بفواصل.");
} finally {
input.close(); // يجب إغلاق الـ Scanner دائماً
}
}
}
بعد أن تعلمنا كيفية التفاعل مع المستخدم عبر الكونسول باستخدام `Scanner`، لننتقل الآن إلى طريقة أخرى أكثر جاذبية وتفاعلية: **النوافذ الرسومية المنبثقة (Pop-up windows)** باستخدام فئة `JOptionPane`. هذه الطريقة تمنح برامجك واجهة مستخدم رسومية بسيطة (GUI) بدلاً من الشاشة النصية السوداء.
`JOptionPane` (Java Option Pane) هي جزء من مكتبة Swing في جافا، وتوفر طرقًا جاهزة وسهلة الاستخدام لإنشاء نوافذ حوار قياسية (مثل نوافذ الرسائل، نوافذ الإدخال، نوافذ التأكيد) التي تظهر للمستخدم.
لكي تتمكن من استخدام `JOptionPane` في برنامجك، يجب عليك استيرادها في بداية الملف. يتم ذلك بإضافة السطر التالي:
import javax.swing.JOptionPane;
هذا السطر يخبر مترجم الجافا بأنك ستستخدم الفئات الموجودة داخل حزمة `javax.swing.JOptionPane`.
تُستخدم دالة `showMessageDialog()` لعرض رسالة بسيطة للمستخدم في نافذة منبثقة. يمكنك التحكم في محتوى الرسالة، عنوان النافذة، ونوع الأيقونة التي تظهر.
JOptionPane.showMessageDialog(Component parentComponent, Object message, String title, int messageType);
null لكي تظهر النافذة في منتصف الشاشة.String) أو أي كائن آخر.JOptionPane.INFORMATION_MESSAGE: أيقونة معلومات (دائرة زرقاء مع حرف i).JOptionPane.WARNING_MESSAGE: أيقونة تحذير (مثلث أصفر مع علامة تعجب).JOptionPane.ERROR_MESSAGE: أيقونة خطأ (دائرة حمراء مع X).JOptionPane.QUESTION_MESSAGE: أيقونة سؤال (دائرة زرقاء مع علامة استفهام).JOptionPane.PLAIN_MESSAGE: لا توجد أيقونة.import javax.swing.JOptionPane;
public class ShowMessageExamples {
public static void main(String[] args) {
// 1. رسالة معلومات بسيطة
JOptionPane.showMessageDialog(null, "تمت العملية بنجاح!", "إشعار", JOptionPane.INFORMATION_MESSAGE);
// 2. رسالة تحذير
JOptionPane.showMessageDialog(null, "تحذير: لا تتوفر مساحة كافية على القرص.", "تحذير النظام", JOptionPane.WARNING_MESSAGE);
// 3. رسالة خطأ
JOptionPane.showMessageDialog(null, "خطأ: اسم المستخدم أو كلمة المرور غير صحيحة.", "خطأ في تسجيل الدخول", JOptionPane.ERROR_MESSAGE);
// 4. رسالة بدون أيقونة (Plain message)
JOptionPane.showMessageDialog(null, "هذه رسالة نصية عادية بدون أيقونة.", "رسالة عادية", JOptionPane.PLAIN_MESSAGE);
}
}
تُستخدم دالة `showInputDialog()` لطلب نص أو قيمة من المستخدم عبر نافذة حوار. يتم عرض مربع نص يمكن للمستخدم الكتابة فيه، وعند الضغط على "OK"، تُرجع الدالة النص الذي أدخله المستخدم كقيمة من نوع String.
String userInput = JOptionPane.showInputDialog(Object message);
// أو
String userInput = JOptionPane.showInputDialog(Component parentComponent, Object message, String title, int messageType);
String يحتوي على النص الذي أدخله المستخدم. إذا ضغط المستخدم على "Cancel" أو أغلق النافذة، فإنها تُرجع null.دائمًا ما تُرجع `showInputDialog()` قيمة نصية (`String`). إذا كنت تتوقع من المستخدم إدخال رقم (عدد صحيح أو عشري)، فيجب عليك تحويل هذا النص إلى النوع الرقمي المناسب قبل إجراء أي عمليات حسابية عليه. سنتناول ذلك بالتفصيل في القسم التالي.
import javax.swing.JOptionPane;
public class ShowInputExamples {
public static void main(String[] args) {
// 1. طلب اسم المستخدم
String userName = JOptionPane.showInputDialog("من فضلك، أدخل اسمك:");
if (userName != null && !userName.isEmpty()) { // التحقق من أن المستخدم لم يضغط Cancel وأن النص ليس فارغًا
JOptionPane.showMessageDialog(null, "أهلاً بك يا: " + userName, "ترحيب", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "لم يتم إدخال الاسم.", "خطأ", JOptionPane.WARNING_MESSAGE);
}
// 2. طلب رقم هاتف
String phoneNumber = JOptionPane.showInputDialog("أدخل رقم هاتفك:");
if (phoneNumber != null && !phoneNumber.isEmpty()) {
JOptionPane.showMessageDialog(null, "رقم هاتفك هو: " + phoneNumber, "معلومات", JOptionPane.PLAIN_MESSAGE);
}
}
}
كما ذكرنا، `showInputDialog()` تُرجع دائمًا `String` (نص). لكن برامجنا غالبًا ما تحتاج إلى التعامل مع الأرقام لإجراء عمليات حسابية. لذلك، يجب علينا تحويل `String` الذي تم إدخاله إلى نوع رقمي (مثل `int` أو `double`).
تُسمى عملية تحويل نوع بيانات إلى نوع آخر بـ **"تحويل النوع" (Type Casting)** أو **"تحويل البيانات" (Data Conversion)**. في جافا، نستخدم فئات مساعدة (Wrapper Classes) لهذا الغرض.
لتحويل نص يمثل عددًا صحيحًا إلى نوع int، نستخدم الدالة الثابتة parseInt() من فئة Integer.
import javax.swing.JOptionPane;
public class StringToIntConversion {
public static void main(String[] args) {
String ageInput = JOptionPane.showInputDialog("أدخل عمرك كرقم:");
try {
int age = Integer.parseInt(ageInput); // هنا يتم التحويل
JOptionPane.showMessageDialog(null, "عمرك هو: " + age + " سنة.", "العمر", JOptionPane.INFORMATION_MESSAGE);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "إدخال غير صالح. من فضلك أدخل رقماً صحيحاً.", "خطأ في الإدخال", JOptionPane.ERROR_MESSAGE);
}
}
}
لتحويل نص يمثل عددًا عشريًا إلى نوع double، نستخدم الدالة الثابتة parseDouble() من فئة Double.
import javax.swing.JOptionPane;
public class StringToDoubleConversion {
public static void main(String[] args) {
String priceInput = JOptionPane.showInputDialog("أدخل سعر المنتج كرقم عشري:");
try {
double price = Double.parseDouble(priceInput); // هنا يتم التحويل
JOptionPane.showMessageDialog(null, "سعر المنتج: " + price + " ريال.", "السعر", JOptionPane.INFORMATION_MESSAGE);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "إدخال غير صالح. من فضلك أدخل رقماً عشرياً.", "خطأ في الإدخال", JOptionPane.ERROR_MESSAGE);
}
}
}
كما رأينا في الأمثلة أعلاه، من المهم جدًا استخدام `try-catch` عند محاولة تحويل النصوص إلى أرقام. إذا أدخل المستخدم نصًا لا يمكن تحويله إلى رقم (مثل "hello")، فستحدث مشكلة تسمى NumberFormatException. استخدام `try-catch` يسمح لبرنامجك بالتعامل مع هذا الخطأ بشكل رشيق بدلاً من الانهيار.
لنقم بإنشاء برنامج حاسبة بسيطة تأخذ رقمين من المستخدم وتحسب مجموعهما وتُظهره في نافذة رسالة.
import javax.swing.JOptionPane;
public class SimpleCalculatorGUI {
public static void main(String[] args) {
// 1. طلب الرقم الأول كنص
String num1String = JOptionPane.showInputDialog(null, "أدخل الرقم الأول:", "حاسبة الجمع", JOptionPane.QUESTION_MESSAGE);
// 2. طلب الرقم الثاني كنص
String num2String = JOptionPane.showInputDialog(null, "أدخل الرقم الثاني:", "حاسبة الجمع", JOptionPane.QUESTION_MESSAGE);
// التحقق من أن المستخدم لم يضغط "Cancel" أو ترك الحقول فارغة
if (num1String != null && !num1String.isEmpty() && num2String != null && !num2String.isEmpty()) {
try {
// 3. تحويل النصوص إلى أرقام عشرية (double)
double num1 = Double.parseDouble(num1String);
double num2 = Double.parseDouble(num2String);
// 4. إجراء عملية الجمع
double sum = num1 + num2;
// 5. عرض النتيجة للمستخدم
JOptionPane.showMessageDialog(null, "المجموع هو: " + sum, "النتيجة", JOptionPane.INFORMATION_MESSAGE);
} catch (NumberFormatException e) {
// معالجة خطأ إذا أدخل المستخدم نصًا بدلاً من رقم
JOptionPane.showMessageDialog(null, "خطأ في الإدخال: من فضلك أدخل أرقامًا صحيحة أو عشرية.", "خطأ", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(null, "تم إلغاء العملية أو ترك أحد الحقول فارغًا.", "إلغاء", JOptionPane.WARNING_MESSAGE);
}
}
}
برنامج يأخذ درجة الحرارة بالمئوية من المستخدم ويحولها إلى فهرنهايت.
import javax.swing.JOptionPane;
public class TempConverterGUI {
public static void main(String[] args) {
String celsiusString = JOptionPane.showInputDialog(null, "أدخل درجة الحرارة بالمئوي (°C):", "تحويل الحرارة", JOptionPane.QUESTION_MESSAGE);
if (celsiusString != null && !celsiusString.isEmpty()) {
try {
double celsius = Double.parseDouble(celsiusString);
// معادلة التحويل من مئوي إلى فهرنهايت: F = (C * 9/5) + 32
double fahrenheit = (celsius * 9.0/5.0) + 32; // استخدم 9.0/5.0 لضمان القسمة العشرية
JOptionPane.showMessageDialog(null, celsius + "°C يساوي " + fahrenheit + "°F", "النتيجة", JOptionPane.INFORMATION_MESSAGE);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "إدخال غير صالح. من فضلك أدخل رقماً لدرجة الحرارة.", "خطأ", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(null, "تم إلغاء عملية التحويل.", "إلغاء", JOptionPane.WARNING_MESSAGE);
}
}
}
عند استخدام `Scanner` أو `JOptionPane`، قد تواجه بعض الأخطاء الشائعة. إليك أبرزها وكيفية التعامل معها:
إذا ضغط المستخدم على زر "Cancel" أو أغلق نافذة `showInputDialog()`، فإن الدالة تُرجع قيمة `null` (أي لا شيء). إذا حاولت استخدام هذه القيمة `null` مباشرة (مثلاً لتحويلها إلى رقم)، فسيحدث خطأ `NullPointerException`.
String userInput = JOptionPane.showInputDialog("أدخل شيئًا:");
// int number = Integer.parseInt(userInput); // هذا سيسبب NullPointerException إذا كان userInput هو null
// الحل: تحقق دائمًا مما إذا كانت القيمة المرجعة ليست null قبل استخدامها
if (userInput != null) {
// يمكنك الآن معالجة userInput بأمان
System.out.println("المدخلات: " + userInput);
} else {
System.out.println("المستخدم ألغى الإدخال.");
}
يحدث هذا الخطأ عندما تحاول تحويل نص إلى رقم باستخدام `Integer.parseInt()` أو `Double.parseDouble()`، ولكن النص لا يمثل رقمًا صالحًا (مثل إدخال "hello" بدلاً من "123").
String numberString = JOptionPane.showInputDialog("أدخل رقمًا:");
// int number = Integer.parseInt(numberString); // هذا سيسبب NumberFormatException إذا أدخل المستخدم نصًا
// الحل: استخدم try-catch block لمعالجة هذا الاستثناء (Exception)
try {
int number = Integer.parseInt(numberString);
System.out.println("الرقم المدخل: " + number);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "خطأ: المدخلات ليست رقمًا صالحًا.", "خطأ في التحويل", JOptionPane.ERROR_MESSAGE);
}
يحدث هذا الخطأ عندما تحاول قراءة نوع بيانات معين باستخدام `Scanner` (مثلاً `nextInt()`)، ولكن المستخدم يدخل نوعًا آخر (مثلاً نص). على عكس `JOptionPane` حيث يجب التحويل يدوياً، `Scanner` يحاول تفسير المدخلات مباشرة.
import java.util.InputMismatchException; // استيراد الاستثناء
import java.util.Scanner;
public class InputMismatchExample {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("أدخل عددًا صحيحًا: ");
try {
int number = input.nextInt(); // محاولة قراءة عدد صحيح
System.out.println("لقد أدخلت: " + number);
} catch (InputMismatchException e) {
System.err.println("خطأ في الإدخال: من فضلك أدخل عددًا صحيحًا.");
input.nextLine(); // مهم لامتصاص المدخلات الخاطئة وتجنب حلقات لا نهائية في بعض الحالات
} finally {
input.close();
}
}
}
إذا نسيت إضافة سطر الاستيراد اللازم (`import java.util.Scanner;` أو `import javax.swing.JOptionPane;`) في بداية ملف الجافا الخاص بك، فستحصل على خطأ في الترجمة يشير إلى أن الكلاس المطلوب لا يمكن العثور عليه.
// import java.util.Scanner; // إذا لم تكتب هذا السطر
// import javax.swing.JOptionPane; // أو هذا السطر
public class MyProgram {
public static void main(String[] args) {
// استخدام أي من الكلاسين هنا سيعطي خطأ في الترجمة
// Scanner input = new Scanner(System.in);
// JOptionPane.showMessageDialog(null, "مرحبًا");
}
}
الحل: أضف سطر الاستيراد المناسب في بداية الملف: import java.util.Scanner; و/أو import javax.swing.JOptionPane;.
اكتب برنامج جافا يستخدم `JOptionPane` لطلب اسم المستخدم، ثم يعرض رسالة ترحيب مخصصة في نافذة رسالة (مثلاً: "أهلاً بك يا [اسم المستخدم] في برنامجنا الأول!"). تأكد من معالجة حالة إذا لم يدخل المستخدم أي اسم (أي إذا ضغط Cancel أو أدخل نصًا فارغًا).
import javax.swing.JOptionPane;
public class CustomWelcomeDialog {
public static void main(String[] args) {
// اكتب الكود هنا لطلب الاسم وعرض رسالة الترحيب
// تلميح: استخدم if (userName != null && !userName.isEmpty())
}
}
صمم برنامج جافا يأخذ وزن المستخدم (بالكيلوجرام) وطوله (بالمتر) كمدخلات عبر **الكونسول** باستخدام `Scanner`. احسب مؤشر كتلة الجسم (BMI) باستخدام المعادلة: $BMI = \frac{\text{الوزن (كجم)}}{\text{الطول (م)}^2}$. ثم اعرض نتيجة الـ BMI على الكونسول. تأكد من معالجة الأخطاء المحتملة إذا أدخل المستخدم قيمًا غير رقمية.
import java.util.Scanner;
import java.util.InputMismatchException;
public class BMICalculatorConsole {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// اكتب الكود هنا لطلب الوزن والطول، حساب BMI وعرضه
// تلميح: استخدم try-catch و hasNextDouble()
}
}
اكتب برنامج جافا يطلب من المستخدم:
import javax.swing.JOptionPane;
import java.util.Scanner;
import java.util.InputMismatchException;
public class CurrencyConverterHybrid {
public static void main(String[] args) {
Scanner consoleInput = new Scanner(System.in);
// الجزء الأول: إدخال المبلغ بالدولار باستخدام JOptionPane
String dollarAmountStr = JOptionPane.showInputDialog(null, "أدخل المبلغ بالدولار:", "تحويل العملة", JOptionPane.QUESTION_MESSAGE);
double dollarAmount = 0.0;
boolean dollarInputValid = false;
if (dollarAmountStr != null && !dollarAmountStr.isEmpty()) {
try {
dollarAmount = Double.parseDouble(dollarAmountStr);
dollarInputValid = true;
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "خطأ: المبلغ المدخل بالدولار ليس رقمًا صالحًا.", "خطأ في الإدخال", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(null, "تم إلغاء إدخال المبلغ بالدولار.", "إلغاء", JOptionPane.WARNING_MESSAGE);
}
// الجزء الثاني: إدخال سعر الصرف باستخدام Scanner (الكونسول)
double exchangeRate = 0.0;
boolean rateInputValid = false;
if (dollarInputValid) { // فقط إذا كان إدخال الدولار صحيحاً
System.out.print("أدخل سعر صرف الدولار مقابل عملتك المحلية (مثال: 3.75 للريال السعودي): ");
try {
exchangeRate = consoleInput.nextDouble();
rateInputValid = true;
} catch (InputMismatchException e) {
System.err.println("خطأ في الإدخال: سعر الصرف المدخل ليس رقمًا صالحًا.");
consoleInput.nextLine(); // امتصاص المدخلات الخاطئة
}
}
consoleInput.close(); // إغلاق Scanner عند الانتهاء
// الجزء الثالث: حساب وعرض النتيجة
if (dollarInputValid && rateInputValid) {
double convertedAmount = dollarAmount * exchangeRate;
JOptionPane.showMessageDialog(null,
String.format("%.2f دولار يساوي %.2f عملة محلية.", dollarAmount, convertedAmount),
"نتيجة التحويل",
JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "لا يمكن إتمام التحويل بسبب مدخلات غير صحيحة.", "تحويل غير مكتمل", JOptionPane.WARNING_MESSAGE);
}
}
}
في هذه الوحدة الأساسية، اكتسبت مهارات حاسمة في **التفاعل مع المستخدم** في برامج جافا. لقد استكشفنا طريقتين رئيسيتين للتعامل مع المدخلات والمخرجات:
بامتلاكك لهذه المهارات، يمكنك الآن بناء برامج جافا أكثر ديناميكية وتفاعلية، قادرة على استقبال البيانات من المستخدم والاستجابة لها. في الوحدة القادمة، سنتعمق في **الجمل الشرطية (`if-else`)**، وهي أدوات أساسية لجعل برامجك تتخذ القرارات وتنفذ مسارات مختلفة بناءً على شروط محددة.