كلاس String والدوال الشائعة
تُعدّ السلاسل النصية من أكثر الأنواع استخدامًا في أي برنامج Java. في Java، String ليست نوعًا بدائيًا — بل هي كائن كامل مدعوم بكلاس java.lang.String، ويأتي محملًا بدوال تغطي كل مهام معالجة النصوص التي ستحتاجها.
ثبات String — القاعدة الأهم
بمجرد إنشاء كائن String، لا يمكن تغيير محتواه. كل دالة تبدو وكأنها "تعدّل" السلسلة تُعيد في الحقيقة كائن String جديدًا تمامًا؛ أما الأصل فيبقى دون تغيير.
String greeting = "Hello";
greeting.toUpperCase(); // لا فائدة — النتيجة تُهمَل
System.out.println(greeting); // ما زال يطبع: Hello
String upper = greeting.toUpperCase(); // صحيح: احتفظ بالكائن الجديد
System.out.println(upper); // يطبع: HELLO
لماذا الثبات؟ تتيح السلاسل الثابتة مشاركتها بين أجزاء متعددة من البرنامج دون آثار جانبية غير متوقعة. تخزّن Java أيضًا الثوابت النصية في String Pool، فتشير الثوابت المتماثلة إلى نفس الكائن مما يوفّر الذاكرة. هذا ممكن فقط لأنه لا أحد يستطيع تعديل الكائن المشترك.
إنشاء السلاسل
أبسط طريقة هي الثابت النصي، لكن يمكنك أيضًا استخدام المُنشئ:
String a = "Java"; // ثابت — يُخزَّن في String Pool
String b = new String("Java"); // مُنشئ صريح — دائمًا كائن جديد في الكومة
System.out.println(a.equals(b)); // true — نفس الأحرف
System.out.println(a == b); // false — مرجعان مختلفان
لا تقارن السلاسل أبدًا بـ ==. يتحقق == مما إذا كان المتغيران يشيران إلى نفس الكائن في الذاكرة، وليس ما إذا كانا يحتويان على نفس النص. استخدم دائمًا .equals() (حساس لحالة الأحرف) أو .equalsIgnoreCase().
length() و charAt()
تُعيد length() عدد الأحرف. تُعيد charAt(index) الحرف عند موضع يبدأ من الصفر.
String word = "Java";
System.out.println(word.length()); // 4
System.out.println(word.charAt(0)); // J
System.out.println(word.charAt(3)); // a
// المرور على كل حرف
for (int i = 0; i < word.length(); i++) {
System.out.print(word.charAt(i) + " "); // J a v a
}
substring()
تُعيد substring(beginIndex) كل شيء من ذلك الفهرس حتى النهاية. تُعيد substring(beginIndex, endIndex) الأحرف من beginIndex حتى — لكن لا تشمل — endIndex.
String text = "Learning Java";
System.out.println(text.substring(9)); // Java
System.out.println(text.substring(0, 8)); // Learning
indexOf() و lastIndexOf()
تُعيد indexOf(str) فهرس أول ظهور لسلسلة فرعية، أو -1 إن لم تُوجد. تجد lastIndexOf(str) آخر ظهور.
String sentence = "to be or not to be";
System.out.println(sentence.indexOf("be")); // 3
System.out.println(sentence.lastIndexOf("be")); // 16
System.out.println(sentence.indexOf("Java")); // -1 (غير موجود)
التحقق من الوجود: استخدم
contains() حين تحتاج فقط إجابة نعم/لا — فهو أكثر وضوحًا من التحقق مما إذا أعادت
indexOf() قيمة
-1.
if (sentence.contains("not")) {
System.out.println("Found it!");
}
split()
تقسّم split(regex) السلسلة إلى مصفوفة عند كل تطابق مع النمط. النمط هو تعبير نظامي، لكن للفواصل البسيطة كالفاصلة أو المسافة يمكن تمريرها مباشرة.
String csv = "Alice,Bob,Carol,Dave";
String[] names = csv.split(",");
for (String name : names) {
System.out.println(name);
}
// Alice
// Bob
// Carol
// Dave
التقسيم على نقطة: النقطة . في التعبيرات النظامية تطابق أي حرف. للتقسيم على نقطة حرفية، استخدم escape: split("\\.").
replace() و replaceAll()
تستبدل replace(old, new) كل ظهور لحرف أو سلسلة فرعية حرفية. تفعل replaceAll(regex, replacement) الأمر ذاته بنمط تعبير نظامي.
String path = "C:\\Users\\alice\\Documents";
String unixPath = path.replace("\\", "/");
System.out.println(unixPath); // C:/Users/alice/Documents
String noDigits = "p4ssw0rd".replaceAll("\\d", "*");
System.out.println(noDigits); // p*ssw*rd
equals() و equalsIgnoreCase()
استخدم equals() للمقارنة مع مراعاة حالة الأحرف، وequalsIgnoreCase() حين لا يهم الفرق بين الكبير والصغير.
String input = "Admin";
System.out.println(input.equals("admin")); // false
System.out.println(input.equalsIgnoreCase("admin")); // true
// ممارسة جيدة: ضع الثابت المعروف على اليسار لتجنب NullPointerException
System.out.println("admin".equalsIgnoreCase(input)); // true — آمن حتى لو input قيمتها null
دوال مفيدة أخرى
trim() / strip() — تزيل المسافات البيضاء البادئة والتالية. يُفضَّل strip() في Java 11+ لأنه يدعم Unicode.
toUpperCase() / toLowerCase() — تغيير حالة الأحرف.
startsWith(prefix) / endsWith(suffix) — فحوصات منطقية.
isEmpty() — تُعيد true إذا كان الطول صفرًا. isBlank() (Java 11+) — تُعيد true إذا كانت فارغة أو تحتوي مسافات فقط.
String.valueOf(x) — تحوّل أي نوع بدائي إلى String.
تطبيق شامل
هذا مثال صغير يُحلّل سطر إعداد بسيط:
String line = " host = localhost:8080 ";
String trimmed = line.strip(); // "host = localhost:8080"
String[] parts = trimmed.split("=", 2); // ["host ", " localhost:8080"]
String key = parts[0].strip(); // "host"
String value = parts[1].strip(); // "localhost:8080"
System.out.println("Key: " + key);
System.out.println("Value: " + value);
// تقسيم القيمة إضافيًا على ":"
String host = value.substring(0, value.indexOf(":"));
String port = value.substring(value.indexOf(":") + 1);
System.out.println("Host: " + host + ", Port: " + port);
// Host: localhost, Port: 8080
دمج السلاسل داخل الحلقات بطيء لأن كل + ينشئ كائنًا جديدًا. عندما تحتاج بناء سلسلة خطوة بخطوة داخل حلقة، استخدم StringBuilder بدلًا من ذلك — وهذا موضوع الدرس التالي مباشرة.
الخلاصة
السلاسل في Java كائنات ثابتة. احتفظ دائمًا بنتيجة أي دالة من دوال String — الأصل لا يتغير أبدًا. الدوال الأساسية التي ستستخدمها باستمرار: length() وcharAt() وsubstring() وindexOf() وsplit() وreplace() وequals(). قارن السلاسل دائمًا بـ .equals() وليس بـ ==.