0Pricing
Java Academy · درس

عدم قابلية String للتغيير ومجمّع السلاسل النصية

افهم سبب عدم قابلية Strings للتغيير، وكيفية عمل مجمّع السلاسل النصية، ومتى تستخدم == مقابل equals

عدم قابلية String للتغيير ومجمّع السلاسل النصية درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

عدم قابلية تغيير String وString Pool

تتسم السلاسل النصية بعدم قابلية التغيير في Java. ويساعد فهم عدم قابلية التغيير وString Pool والفرق بين مساواة المراجع ومساواة القيم على تجنب الأخطاء الخفية.

لماذا لا يمكن تغيير السلاسل النصية

لا يمكن تعديل كائنات String بعد إنشائها. فأي عملية "تغيّر" كائن String تنشئ في الواقع كائن String جديدًا. وهذا يتيح المشاركة والأمان في بيئات تعدد الخيوط وتخزين hashCode مؤقتًا.

String s = "hello";
s.toUpperCase(); // creates a new String, does NOT modify s
System.out.println(s); // still "hello"

String upper = s.toUpperCase(); // must capture the result
System.out.println(upper); // HELLO

String Pool (Interning)

تُخزَّن الثوابت النصية في String Pool، وهو قسم خاص من الذاكرة heap. وتشترك الثوابت التي لها المحتوى نفسه في الكائن ذاته.

String a = "hello"; // goes into pool
String b = "hello"; // same object from pool
String c = new String("hello"); // forces new object outside pool

System.out.println(a == b);       // true  (same pool reference)
System.out.println(a == c);       // false (different objects)
System.out.println(a.equals(c));  // true  (same content)

== مقابل equals()

استخدموا دائمًا .equals() لمقارنة محتوى String. يتحقق العامل == مما إذا كان متغيران يشيران إلى الكائن ذاته، أي مساواة المراجع.

String input = new String("admin"); // from user input, not pooled
String role  = "admin";

// Dangerous: fails because different objects
if (input == role) System.out.println("Same"); // not printed!

// Correct: compares content
if (input.equals(role)) System.out.println("Equal!"); // prints

// Null-safe: put constant first to avoid NPE
if ("admin".equals(input)) System.out.println("Admin"); // prints

intern(): وضع String يدويًا في String Pool

تفرض String.intern() وضع String في String Pool، وتعيد المرجع الموجود فيه. ونادرًا ما تكون هناك حاجة إليها في Java الحديثة.

String s1 = new String("interned");
String s2 = s1.intern();
String s3 = "interned"; // already in pool

System.out.println(s2 == s3); // true (both point to pool)
System.out.println(s1 == s3); // false (s1 is off-pool)

// Modern use: prefer String.intern() only when you need
// identity-based caching with thousands of repeated strings

سلوك دمج السلاسل النصية

ينشئ الدمج باستخدام + كائنات String جديدة. ويؤدي ذلك داخل حلقة إلى تعقيد O(n²). يحسّن المترجم التعبيرات المفردة، لكنه لا يحسّن الدمج القائم على الحلقات.

// Compiler optimizes this to a StringBuilder automatically:
String result = "Hello, " + "World" + "!";

// In loops: compiler does NOT optimize — use StringBuilder explicitly!
List<String> words = List.of("a", "b", "c", "d");
String bad = ""; // O(n^2) — new object each iteration
for (String w : words) bad += w;

StringBuilder sb = new StringBuilder();
for (String w : words) sb.append(w); // O(n) — efficient
String good = sb.toString();
System.out.println(good); // abcd

مقارنة السلاسل النصية بطريقة صحيحة

استخدموا دائمًا equals()؛ وللمقارنة مع تجاهل حالة الأحرف استخدموا equalsIgnoreCase(). استخدموا compareTo() للترتيب المعجمي.

String a = "Java";
String b = "java";

System.out.println(a.equals(b));             // false
System.out.println(a.equalsIgnoreCase(b));   // true
System.out.println(a.compareToIgnoreCase(b)); // 0 (equal)
System.out.println(a.compareTo(b));           // negative (J < j in Unicode)

التخزين المؤقت لـ hashCode في String

نظرًا إلى أن السلاسل النصية غير قابلة للتغيير، تخزّن Java قيمة hashCode مؤقتًا بعد الاستدعاء الأول. وتعيد الاستدعاءات اللاحقة القيمة المخزنة مؤقتًا، مما يجعل السلاسل النصية فعّالة جدًا كمفاتيح في HashMap.

String key = "product:12345";
// hashCode computed once and cached:
int h1 = key.hashCode();
int h2 = key.hashCode(); // returns cached value
System.out.println(h1 == h2); // true

// Strings are ideal HashMap keys because:
// 1. hashCode is consistent (immutable content)
// 2. hashCode is cached (fast repeated lookups)
// 3. equals is well-defined (content-based)
Map<String, Integer> map = new HashMap<>();
map.put("key", 42);
System.out.println(map.get("key")); // 42

String نهائي

فئة String تحمل الكلمة المفتاحية final، لذلك لا يمكن اشتقاق فئات فرعية منها. وهذا يضمن عدم قدرة أي فئة فرعية على الإخلال بعدم قابلية التغيير أو باتساق hashCode.

// String is final — this will not compile:
// class MutableString extends String {} // compile error

// You cannot override String behavior.
// If you need custom string-like behavior, use a wrapper:
record ProductSku(String value) {
    ProductSku { if (!value.matches("[A-Z]{3}-[0-9]{4}")) throw new IllegalArgumentException(); }
    @Override public String toString() { return value; }
}

String.format مقابل الدمج

بالنسبة إلى السلاسل النصية متعددة الأجزاء، يُفضَّل استخدام String.format() أو كتل النصوص بدلًا من الدمج، لتحسين الوضوح وتسهيل الترجمة.

String name = "Alice";
int age = 30;
double score = 97.5;

// Concatenation — harder to read with many parts
String bad = "User: " + name + ", Age: " + age + ", Score: " + score;

// String.format — clear placeholders
String good = String.format("User: %s, Age: %d, Score: %.1f", name, age, score);

System.out.println(good);
// User: Alice, Age: 30, Score: 97.5

عمليات String الآمنة مع null

تطرح العديد من عمليات String استثناء NPE إذا كانت السلسلة النصية تساوي null. استخدموا أنماطًا دفاعية أو String.valueOf() للتعامل مع null بأمان.

String value = null;

// Null-safe length check
int len = (value != null) ? value.length() : 0;
System.out.println(len); // 0

// Null-safe comparison (put constant first)
boolean isAdmin = "admin".equals(value); // false, no NPE

// String.valueOf converts null to "null" string
System.out.println(String.valueOf(value)); // "null"
System.out.println(Objects.toString(value, "default")); // "default"

تحقق سريع

ماذا يطبع الكود التالي؟

String a = "hello";
String b = new String("hello");
System.out.println(a.equals(b));
System.out.println(a == b);

مراجعة: عدم قابلية تغيير String وString Pool

أهم النقاط:

  • السلاسل النصية غير قابلة للتغيير؛ فالعمليات تعيد كائنات String جديدة ولا تعدّل الكائن الأصلي أبدًا
  • تُخزَّن الثوابت النصية في String Pool؛ وتشترك الثوابت ذات المحتوى نفسه في كائن واحد
  • استخدموا دائمًا .equals() لمقارنة محتوى String؛ أما == فيقارن مراجع الكائنات
  • استخدموا equalsIgnoreCase() للمقارنات التي تتجاهل حالة الأحرف
  • يبلغ تعقيد دمج السلاسل النصية داخل الحلقات O(n²)؛ لذا استخدموا StringBuilder لتحسين الكفاءة
  • تخزّن السلاسل النصية قيمة hashCode مؤقتًا، مما يجعلها فعّالة وآمنة كمفاتيح في HashMap

الأسئلة الشائعة

هل درس «عدم قابلية String للتغيير ومجمّع السلاسل النصية» مجاني؟

نعم — نص درس «عدم قابلية String للتغيير ومجمّع السلاسل النصية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «عدم قابلية String للتغيير ومجمّع السلاسل النصية»؟

افهم سبب عدم قابلية Strings للتغيير، وكيفية عمل مجمّع السلاسل النصية، ومتى تستخدم == مقابل equals تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «عدم قابلية String للتغيير ومجمّع السلاسل النصية»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. عدم قابلية String للتغيير ومجمّع السلاسل النصية
  2. StringBuilder للضم الفعّال
  3. كتل النص وString.format
  4. التعابير النمطية باستخدام Pattern وMatcher
← العودة إلى Java Academy