CodeGym /בלוג Java /Random-HE /ערכים קבועים ב-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()שיטה. אבל, כלבים וחתולים בהחלט "מדברים" בדרכים שונות. אז, נכריז על שיטות 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 יש מוסכמות שמות שונה עבור קבועים. זה לא הסימון הרגיל של camelCase . אם זה היה משתנה רגיל, היינו קוראים לו constantExample . אבל, שמות הקבועים כתובים באותיות רישיות, עם קווים תחתונים בין המילים (אם יש יותר ממילה אחת), למשל "CONSTANT_EXAMPLE". למה אנחנו צריכים קבועים? הם מאוד שימושיים אם, למשל, יש ערך קבוע שאתה משתמש בו באופן קבוע בתוכנית. לדוגמה, החלטת לעשות היסטוריה ולכתוב את המשחק "The Witcher 4" בעצמך. ברור שהמשחק ישתמש באופן קבוע בשם הגיבור: "ג'רלט מריוויה". את המחרוזת הזו (ושמות של גיבורים אחרים) מומלץ להצהיר כקבוע: הערך שלה יישמר במקום אחד, ובהחלט לא תעשו שגיאת הקלדה בהזנתו מיליון פעמים.
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, אבל ג'רלט מריוויה עדיין לא יכול להחליט את מי הוא אוהב יותר: Yennefer of Wengerberg או Triss Merigold אבל, אם מעולם לא שיחקת The Witcher לפני כן, נתחיל מה- התחלה. שמו של הגיבור הוא Geralt of Rivia. Geralt of Rivia הוא מכשף, צייד מפלצות. הכרזנו על שמות הגיבורים כקבועים. עכשיו בהחלט לא נעשה שגיאות הקלדה, ואין צורך לכתוב אותן ביד בכל פעם. יתרון נוסף: אם אי פעם נצטרך לשנות את ערך המשתנה על פני כל התוכנית, אתה יכול לעשות זאת במקום אחד, במקום לשנות אותו באופן ידני על פני כל בסיס הקוד. :)

טיפוסים בלתי ניתנים לשינוי

ככל שעבדתם עם ג'אווה, כנראה שכבר התרגלתם לרעיון שלמתכנתים יש שליטה כמעט מלאה על מצב כל האובייקטים. אם אתה רוצה ליצור 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 מצביע עליו כעת. אבל, לא נראה שיש לנו השפעה על אובייקט המחרוזת "אני אוהב ג'אווה". אוקיי, בוא ננסה משהו אחר! המחלקה 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(). הוא מחזיר מחרוזת משנה המבוססת על מדדי תווים שהועברו כארגומנטים. בואו נחתוך את 10 התווים הראשונים של המחרוזת שלנו:
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". אתה יכול פשוט ליצור אובייקט חדש ולכתוב: "<object new> = התוצאה של מניפולציה של ה- "I love Java" object ". אילו סוגים אחרים אינם ניתנים לשינוי? חלקם שבהחלט תצטרכו לזכור מיד הם כל מחלקות העטיפה של הטיפוסים הפרימיטיביים. Integer, Byte, Character, Short, Boolean, Long, Double, Float: כל המחלקות הללו יוצרות immutableאובייקטים (נדבר עליהם בשיעורים הקרובים). זה כולל שיעורים המשמשים ליצירת מספרים גדולים, כגון BigIntegerו- BigDecimal. לאחרונה כיסינו חריגים ונגעת ב- Stack Trace . ובכן , נחשו מה, גם אובייקטי java.lang.StackTraceElement הם בלתי ניתנים לשינוי. זה הגיוני: אם מישהו היה יכול לשנות את הנתונים של המחסנית שלנו, זה היה הופך את כל העניין לחסר טעם. תאר לעצמך מישהו שעובר את מעקב המחסנית ומשנה OutOfMemoryError ל- FileNotFoundException . אז אתה משתמש בערימה הזו כדי למצוא את הסיבה לשגיאה. אבל התוכנית אפילו לא משתמשת בקבצים. :) אז, הם הפכו את האובייקטים האלה לבלתי ניתנים לשינוי, ליתר בטחון. אוקיי, אז זה פחות או יותר הגיוני עבור StackTraceElement . אבל , למה שמישהו יצטרך להפוך מחרוזות לבלתי ניתנות לשינוי? מדוע שינוי הערכים שלהם יהווה בעיה? זה כנראה אפילו יהיה יותר נוח. :/ יש לכך כמה סיבות. ראשית, זה חוסך זיכרון. ניתן למקם מחרוזות בלתי ניתנות לשינוי במאגר המיתרים , מה שמאפשר לעשות שימוש חוזר במחרוזות במקום ליצור חדשות. שנית, למען הביטחון. לדוגמה, שמות משתמש וסיסמאות הם מחרוזות כמעט בכל תוכנית. אפשרות לשנות אותם עלולה לגרום לבעיות הרשאה. ישנן סיבות אחרות, אבל המחקר שלנו על ג'אווה עדיין לא כיסה אותן, אז נחזור אליהן מאוחר יותר.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION