CodeGym /مدونة جافا /Random-AR /القيم الثابتة في Java: النهائية والثوابت وغير القابلة للت...
John Squirrels
مستوى
San Francisco

القيم الثابتة في Java: النهائية والثوابت وغير القابلة للتغيير

نشرت في المجموعة
أهلاً! أنت بالفعل على دراية بكلمة "المعدل". على الأقل، لقد واجهت معدّلات الوصول (العامة والخاصة) والمعدّل الثابت. سنناقش اليوم معدّلًا خاصًا يسمى Final . يمكنك القول إن المُعدِّل النهائي "يعزز" أجزاء برنامجنا التي تتطلب سلوكيات ثابتة لا لبس فيها وغير متغيرة. هناك ثلاثة أماكن في برامجك يمكنك استخدامها: الفئات والأساليب والمتغيرات. القيم الثابتة في Java: النهائية والثوابت وغير القابلة للتغيير - 2 دعونا نمر عبرها بالترتيب. إذا تم استخدام المعدل النهائي في إعلان الفصل، فهذا يعني أنه لا يمكن وراثة الفصل. في الدروس السابقة، استخدمنا مثالًا بسيطًا للميراث: كان لدينا Animalفئة الأصل وفئتان فرعيتان: CatوDog
public class Animal {
}



public class Cat extends Animal {
   // Fields and methods of the Cat class
}


public class Dog extends Animal {

   // Fields and methods of the Dog class
}
ومع ذلك، إذا استخدمنا المعدل النهائي في Animalالفصل، فلا يمكن أن ترثه الفئات Cat.Dog
public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
يقوم المترجم بإنشاء خطأ على الفور. في Java، تم بالفعل تنفيذ العديد من الفئات النهائية . من بين تلك التي تستخدمها بشكل متكرر، Stringهو الأكثر شهرة. علاوة على ذلك، إذا تم الإعلان عن فئة ما على أنها نهائية ، فإن جميع أساليب الفئة تصبح نهائية أيضًا . ماذا يعني ذالك؟ إذا تم الإعلان عن طريقة باستخدام المعدل النهائي ، فلا يمكنك تجاوز هذه الطريقة. على سبيل المثال، لدينا هنا Animalفئة تعلن عن speak()طريقة. لكن الكلاب والقطط "تتحدث" بالتأكيد بطرق مختلفة. لذا، سنعلن عن أساليب التحدث () في كل من الفئتين Catو Dog، ولكننا سننفذها بشكل مختلف.
public class Animal {

   public void speak() {
       System.out.println("Hello!");
   }
}

public class Cat extends Animal {

   @Override
   public void speak() {
       System.out.println("Meow!");
   }
}

public class Dog extends Animal {

   @Override
   public void speak() {
       System.out.println("Woof!");
   }
}
لقد جعلنا Catالفئتين Dogتتجاوزان الطريقة المعلنة في الفئة الأصل. الآن، سيتحدث الحيوان بشكل مختلف، اعتمادًا على نوع الكائن:
public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       Dog dog = new Dog();

       cat.speak();
       dog.speak();
   }
}
الإخراج: مواء! اللحمة! ومع ذلك، إذا أعلنا أن طريقة Animalالفصل speak()نهائية، فلن نتمكن من تجاوزها في الفئات الأخرى:
public class Animal {

   public final void speak() {
       System.out.println("Hello!");
   }
}


public class Cat extends Animal {

   @Override
   public void speak() {// Error! A final method can't be overridden!
       System.out.println("Meow!");
   }
}
وسيتم إجبار كائناتنا على استخدام speak()الطريقة كما هو محدد في الفئة الأصل:
public static void main(String[] args) {

   Cat cat = new Cat();
   Dog dog = new Dog();

   cat.speak();
   dog.speak();
}
الإخراج: مرحبا! مرحبًا! الآن فيما يتعلق بالمتغيرات النهائية . وتعرف أيضًا باسم الثوابت . أولاً (والأهم من ذلك)، لا يمكن تغيير القيمة الأولية المخصصة لقيمة ثابتة. يتم تعيينه مرة واحدة وإلى الأبد.
public class Main {

   private static final int CONSTANT_EXAMPLE = 333;

   public static void main(String[] args) {

       CONSTANT_EXAMPLE = 999;// Error! You can't assign a new value to a final variable!
   }
}
لا يلزم تهيئة الثابت على الفور. يمكن القيام بذلك لاحقًا. لكن القيمة المخصصة لها في البداية ستبقى كما هي إلى الأبد.
public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;// This is allowed
}
ثانيًا، لاحظ اسم المتغير الخاص بنا. لدى Java اصطلاح تسمية مختلف للثوابت. إنها ليست تدوين حالة الجمل المعتادة . لو كان متغيرًا عاديًا، لكنا أطلقنا عليه اسم ConstantExample . لكن أسماء الثوابت تكتب بأحرف كبيرة، مع وجود شرطة سفلية بين الكلمات (إذا كان هناك أكثر من كلمة واحدة)، على سبيل المثال "CONSTANT_EXAMPLE". لماذا نحتاج إلى الثوابت؟ وهي مفيدة جدًا، على سبيل المثال، إذا كانت هناك قيمة ثابتة تستخدمها بانتظام في أحد البرامج. على سبيل المثال، قررت أن تصنع التاريخ وتكتب لعبة "The Witcher 4" بنفسك. من الواضح أن اللعبة ستستخدم اسم بطل الرواية بانتظام: "Geralt of Rivia". من الأفضل الإعلان عن هذه السلسلة (وأسماء الأبطال الآخرين) على أنها ثابتة: سيتم تخزين قيمتها في مكان واحد، وبالتأكيد لن ترتكب خطأً مطبعيًا عند إدخالها مليون مرة.
public class TheWitcher4 {

   private static final String GERALT_NAME = "Geralt of Rivia";
   private static final String YENNEFER_NAME = "Yennefer of Wengerberg";
   private static final String TRISS_NAME = "Triss Merigold";

   public static void main(String[] args) {

       System.out.println("The Witcher 4");
       System.out.println("It's already the fourth Witcher game, but " + GERALT_NAME + " still can't decide who" +
               " he likes more: " + YENNEFER_NAME + " or " + TRISS_NAME);

       System.out.println("But, if you've never played The Witcher before, we'll start from the beginning.");
       System.out.println("The protagonist's name is " + GERALT_NAME);
       System.out.println(GERALT_NAME + " is a witcher, a monster hunter");
   }
}
الإخراج: The Witcher 4 إنها بالفعل لعبة Witcher الرابعة، لكن لا يزال Geralt of Rivia غير قادر على تحديد من يحبه أكثر: Yennefer من Wengerberg أو Triss Merigold ولكن، إذا لم تلعب The Witcher من قبل، فسنبدأ من بداية. اسم بطل الرواية هو Geralt of Rivia Geralt of Rivia هو ساحر، صائد وحوش. لقد أعلنا أسماء الأبطال كثوابت. الآن، بالتأكيد لن نرتكب أي خطأ مطبعي، وليست هناك حاجة لكتابتها يدويًا في كل مرة. ميزة أخرى: إذا احتجنا في أي وقت إلى تغيير قيمة المتغير عبر البرنامج بأكمله، فيمكنك القيام بذلك في مكان واحد، بدلاً من تعديله يدويًا عبر قاعدة التعليمات البرمجية بأكملها. :)

أنواع غير قابلة للتغيير

نظرًا لأنك عملت مع Java، فمن المحتمل أنك اعتدت بالفعل على فكرة أن المبرمجين لديهم سيطرة كاملة تقريبًا على حالة جميع الكائنات. إذا كنت ترغب في إنشاء Catكائن، يمكنك ذلك. إذا كنت ترغب في إعادة تسميته، يمكنك ذلك. إذا كنت ترغب في تغيير عمره أو أي شيء آخر، يمكنك ذلك. لكن لدى Java العديد من أنواع البيانات التي لها خاصية خاصة. إنهم غير قابلين للتغيير . إذا كانت الفئة غير قابلة للتغيير، فلا يمكن تغيير حالة كائناتها. هل تريد بعض الأمثلة؟ قد يفاجئك ذلك، لكن الفئة غير القابلة للتغيير الأكثر شهرة هي String! إذن، لا يمكننا حقًا تغيير قيمة السلسلة؟ حسنًا، فلنحاول:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1 = "I love Python";// but changing str1 has no impact on str2
   System.out.println(str2);// str2 continues to point to the "I love Java" string, but str1 now points to a different object
}
الإخراج: أنا أحب جافا أحب جافا بعد أن كتبنا
str1 = "I love Python";
كائن السلسلة "I love Java"لم يتغير أو يذهب إلى أي مكان. لا يزال موجودًا بسعادة ويحتوي على نفس النص تمامًا كما كان من قبل. الرمز
str1 = "I love Python";
ببساطة أنشأنا كائنًا آخر، والذي يشير إليه الآن str1 . ولكن، يبدو أنه لا يمكننا أن يكون لدينا أي تأثير على كائن السلسلة "أنا أحب Java". حسنًا، دعنا نجرب شيئًا آخر! الفصل Stringمليء بالأساليب، ويبدو أن بعضها يغير حالة الكائن! على سبيل المثال، هناك replace()طريقة. دعونا نغير كلمة "Java" إلى "Python" في سلسلتنا!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1.replace("Java", "Python");// We try to change the state of str1 by swapping the word "Java" with "Python"
   System.out.println(str2);
}
الإخراج: أنا أحب جافا أحب جافا ولم يعمل مرة أخرى! ربما طريقة الاستبدال لا تعمل؟ دعونا نجرب شيئا آخر. على سبيل المثال، substring(). تقوم بإرجاع سلسلة فرعية بناءً على فهارس الأحرف التي تم تمريرها كوسائط. دعونا نقطع الأحرف العشرة الأولى من السلسلة:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1.substring(10);// Truncate the original String
   System.out.println(str2);
}
الإخراج: أنا أحب جافا أحب جافا القيم الثابتة في Java: النهائية والثوابت وغير القابلة للتغيير - 3 لم يتغير شيء. ولا ينبغي أن يكون. كما قلنا سابقًا، السلاسل النصية غير قابلة للتغيير. إذًا، ما الأمر مع كل الأساليب الموجودة في الفصل String؟ بعد كل شيء، يمكنهم اقتطاع السلاسل وتغيير الأحرف والمزيد. ما الفائدة إذا لم يحدث شيء؟ يمكنهم في الواقع القيام بهذه الأشياء! لكنهم يعيدون سلسلة جديدة في كل مرة. لا معنى للكتابة
str1.replace("Java", "Python");
لأنه لا يمكنك تغيير الكائن الأصلي. ولكن، إذا قمت بكتابة نتيجة الطريقة إلى متغير مرجعي جديد، فسوف ترى الفرق على الفور!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   String str1AfterReplacement =  str1.replace("Java", "Python");
   System.out.println(str2);

   System.out.println(str1AfterReplacement);
}
كل Stringالطرق تعمل بهذه الطريقة. لا يمكن فعل أي شيء للكائن "I love Java". يمكنك فقط إنشاء كائن جديد وكتابة: "<new object> = نتيجة معالجة "I love Java" object ". ما هي الأنواع الأخرى غير القابلة للتغيير؟ بعض الأنواع التي ستحتاج بالتأكيد إلى تذكرها على الفور هي جميع فئات الغلاف للأنواع البدائية Integer. Byte, Character, Short, Boolean, Long, Double, Float: كل هذه الفئات تقوم بإنشاء immutableكائنات (سنتحدث عنها في الدروس القادمة). يتضمن ذلك الفئات المستخدمة لإنشاء أعداد كبيرة، مثل BigIntegerو BigDecimal. لقد قمنا مؤخرًا بتغطية الاستثناءات وتطرقنا إلى تتبع المكدس . حسنًا ، خمن ماذا، كائنات java.lang.StackTraceElement هي أيضًا غير قابلة للتغيير. وهذا منطقي: إذا تمكن شخص ما من تغيير بيانات مكدسنا، فسيجعل الأمر برمته بلا معنى. تخيل شخصًا يمر عبر تتبع المكدس ويغير OutOfMemoryError إلى FileNotFoundException . و ثم تستخدم هذا المكدس للعثور على سبب الخطأ. لكن البرنامج لا يستخدم الملفات حتى. :) لذلك، جعلوا هذه الكائنات غير قابلة للتغيير، فقط في حالة حدوث ذلك. حسنًا، لذلك يبدو الأمر منطقيًا إلى حد ما بالنسبة لـ StackTraceElement . لكن لماذا يحتاج أي شخص إلى جعل السلاسل غير قابلة للتغيير؟ لماذا قد يكون تغيير قيمهم مشكلة؟ وربما يكون أكثر ملاءمة. :/ هناك عدة أسباب لذلك. أولا، أنه يحفظ الذاكرة. يمكن وضع السلاسل غير القابلة للتغيير في تجمع السلاسل ، مما يسمح بإعادة استخدام السلاسل بدلاً من إنشاء سلاسل جديدة. ثانيا، من أجل الأمن. على سبيل المثال، أسماء المستخدمين وكلمات المرور عبارة عن سلاسل في كل برنامج تقريبًا. قد يؤدي إتاحة تغييرها إلى حدوث مشكلات في الترخيص. هناك أسباب أخرى، لكن دراستنا لجافا لم تتناولها بعد، لذا سنعود إليها لاحقًا.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION