JavaRush /בלוג Java /Random-HE /סופי, קבועים ובלתי ניתנים לשינוי ב-Java

סופי, קבועים ובלתי ניתנים לשינוי ב-Java

פורסם בקבוצה
שלום! המילה "מתקן" כבר מוכרת לך. לכל הפחות, נתקלת בשינויי גישה (ציבוריים, פרטיים) ובשינוי הסטטי. היום נדבר על המשנה הסופי המיוחד . ניתן לומר שהוא "מלט" את אותם אזורים בתוכנית שלנו שבהם אנו זקוקים להתנהגות מתמדת, חד משמעית, בלתי משתנה. ניתן להשתמש בו בשלושה תחומים בתוכנית שלנו: מחלקות, שיטות ומשתנים. בלתי ניתן לשינוי בג'אווה: סופי, קבועים ואי משתנה - 2 בואו נעבור עליהם אחד אחד. אם הצהרת מחלקה מכילה את השינוי הסופי , פירוש הדבר שאינך יכול לרשת ממחלקה זו. בהרצאות קודמות ראינו דוגמה פשוטה של ​​ירושה: הייתה לנו כיתת הורים Animalושתי כיתות ילדים - CatוDog
public class Animal {
}

public class Cat extends Animal {
   //..поля и методы класса Cat
}

public class Dog extends Animal {

   //..поля и методы класса Dog
}
עם זאת, אם נציין Animalמשנה למחלקה, גם finalמחלקות לא יוכלו לרשת ממנה . CatDog
public final class Animal {

}

public class Cat extends Animal {

   //ошибка! Cannot inherit from final Animal
}
המהדר מייצר מיד שגיאה. ישנן מחלקות רבות שכבר מיושמות ב-Java final. המפורסם שבהם אתה משתמש כל הזמן הוא String. בנוסף, אם מחלקה מוכרזת כ- final, כל השיטות שלה הופכות גם ל- final. מה זה אומר? אם צוין שינוי עבור שיטה final, לא ניתן לעקוף שיטה זו. לדוגמה, יש לנו מחלקה Animalשמגדירה שיטה voice(). עם זאת, כלבים וחתולים בבירור "מדברים" אחרת. לכן, בכל אחת מהמחלקות - Catו Dog- ניצור שיטה voice(), אך ניישם אותה אחרת.
public class Animal {

   public void voice() {
       System.out.println("Voice!");
   }
}

public class Cat extends Animal {

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

public class Dog extends Animal {

   @Override
   public void voice() {
       System.out.println("Woof!");
   }
}
בשיעורים Catודפנו Dogאת השיטה של ​​כיתת האב. כעת החיה תשמיע קול בהתאם לאיזה אובייקט מחלקה היא:
public class Main {

   public static void main(String[] args) {

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

       cat.voice();
       dog.voice();
   }
}
מסקנה: מיאו! וואו! עם זאת, אם Animalנכריז על מתודה במחלקה voice()כ- final, לא ניתן יהיה להגדיר אותה מחדש במחלקות אחרות:
public class Animal {

   public final void voice() {
       System.out.println("Voice!");
   }
}


public class Cat extends Animal {

   @Override
   public void voice() {//ошибка! final-метод не может быть переопределен!
       System.out.println("Meow!");
   }
}
ואז האובייקטים שלנו ייאלצו להשתמש בשיטה voice()כפי שהיא מוגדרת במחלקת האב:
public static void main(String[] args) {

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

   cat.voice();
   dog.voice();
}
מסקנה: קול! קוֹל! עכשיו לגבי finalהמשתנים. אחרת הם נקראים קבועים . ראשית (והכי חשוב), לא ניתן לשנות את הערך הראשון שהוקצה לקבוע. זה מוקצה פעם אחת ולתמיד.
public class Main {

   private static final int CONSTANT_EXAMPLE = 333;

   public static void main(String[] args) {

       CONSTANT_EXAMPLE = 999;//ошибка! Нельзя присвоить новое meaning final-переменной!
   }
}
אין צורך לאתחל את הקבוע באופן מיידי. זה יכול להיעשות מאוחר יותר. אבל הערך שהוקצה ראשון יישאר לנצח.
public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;//так делать можно
}
שנית, שימו לב לשם המשתנה שלנו. לקבועי Java יש מוסכמות שמות שונה. זה לא ה-camelCase שאנחנו רגילים אליו. במקרה של משתנה רגיל, היינו קוראים לו constantExample, אבל שמות הקבועים כתובים באותיות גדולות, ובין המילים (אם יש כמה מהן) יש קו תחתון - "CONSTANT_EXAMPLE". למה נחוצים קבועים? לדוגמה, הם יהיו שימושיים אם אתה משתמש כל הזמן בערך קבוע כלשהו בתוכנית. נניח שהחלטת להיכנס להיסטוריה ולכתוב את המשחק "The Witcher 4" לבד. המשחק ישתמש ללא הרף בשם הדמות הראשית - "ג'רלט מריביה". עדיף להפריד את השורה הזו ואת השמות של גיבורים אחרים לקבוע: הערך שאתה צריך יאוחסן במקום אחד, ובהחלט לא תטעו כשאתה מקליד אותו בפעם המיליון.
public class TheWitcher4 {

   private static final String GERALT_NAME = "Геральт из Ривии";
   private static final String YENNEFER_NAME = "Йеннифэр из Венгерберга";
   private static final String TRISS_NAME = "Трисс Меригольд";

   public static void main(String[] args) {

       System.out.println("Ведьмак 4");
       System.out.println("Это уже четвертая часть Ведьмака, а " + GERALT_NAME + " ниHow не определится кто ему" +
               " нравится больше: " + YENNEFER_NAME + " or " + TRISS_NAME);

       System.out.println("Но если вы никогда не играли в Ведьмака - начнем сначала.");
       System.out.println("Главного героя зовут " + GERALT_NAME);
       System.out.println(GERALT_NAME + " - ведьмак, охотник на чудовищ");
   }
}
סיכום:
Ведьмак 4
Это уже четвертая часть Ведьмака, а Геральт из Ривии ниHow не определится, кто ему нравится больше: Йеннифэр из Венгерберга or Трисс Меригольд.
Но если вы никогда не играли в Ведьмака — начнем сначала.
Главного героя зовут Геральт из Ривии
Геральт из Ривии — ведьмак, охотник на чудовищ
הפרדנו את שמות הדמויות לקבועים, ועכשיו בהחלט לא נכתוב אותם שגוי, ולא יהיה צורך לכתוב אותם ביד כל פעם. יתרון נוסף: אם בסופו של דבר אנחנו צריכים לשנות את הערך של משתנה לאורך כל התוכנית, מספיק לעשות את זה במקום אחד, במקום לבצע אותו מחדש באופן ידני לאורך כל הקוד :)

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

במהלך העבודה שלך בג'אווה, אתה כנראה כבר רגיל לעובדה שהמתכנת שולט כמעט לחלוטין במצב של כל האובייקטים. מבוקש נוצר אובייקט Cat. אם רציתי, שיניתי את השם. אם הוא רצה, הוא שינה את גילו, או משהו אחר. אבל בג'אווה ישנם מספר סוגי נתונים בעלי מצב מיוחד. הם בלתי ניתנים לשינוי , או בלתי ניתנים לשינוי . המשמעות היא שאם מחלקה היא בלתי ניתנת לשינוי, לא ניתן לשנות את מצב האובייקטים שלה. דוגמאות? אולי תופתעו, אבל הדוגמה המפורסמת ביותר למחלקה הבלתי משתנה היא String! נראה שאיננו יכולים לשנות את הערך של מחרוזת? בוא ננסה:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1 = "I love Python";//но поведение str1 ниHow не влияет на str2
   System.out.println(str2);//str2 продолжает указывать на строку "I love Java", хотя str1 уже указывает на другой an 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;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1.replace("Java", "Python");//попробуем изменить состояние str1, заменив слово "Java" на “Python”
   System.out.println(str2);
}
מסקנה: אני אוהב ג'אווה אני אוהב ג'אווה זה לא הסתדר שוב! אולי שיטת העקומה לא עובדת? בוא ננסה עוד אחד. לדוגמה, substring(). חותך מחרוזת על סמך מספרי התווים המועברים. בואו נקצץ את שלנו ל-10 התווים הראשונים:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1.substring(10);//обрезаем исходную строку
   System.out.println(str2);
}
מסקנה: אני אוהב ג'אווה אני אוהב ג'אווה בלתי ניתן לשינוי בג'אווה: סופי, קבועים ואי משתנה - 3 שום דבר לא השתנה. וזה לא היה צריך. כפי שאמרנו, חפצים Stringאינם ניתנים לשינוי. מהן אם כן כל שיטות המחלקה הללו String? הם יכולים לקצץ את השורה, לשנות את התווים בו וכו'. למה הם נחוצים אם לא קורה כלום? הם יכולים! אבל הם מחזירים אובייקט מחרוזת חדש בכל פעם. אין טעם לכתוב:
str1.replace("Java", "Python");
- לא תשנה את האובייקט המקורי. אבל אם תכתוב את התוצאה של השיטה לתוך משתנה ייחוס חדש, תראה מיד את ההבדל!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

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

   System.out.println(str1AfterReplacement);
}
רק כך Stringפועלות כל השיטות הללו. אתה לא יכול לעשות שום דבר עם האובייקט "אני אוהב ג'אווה" . פשוט צור אובייקט חדש וכתוב: "אובייקט חדש = תוצאה של כמה מניפולציות עם האובייקט "אני אוהב ג'אווה ". אילו סוגים נוספים הם בלתי ניתנים לשינוי? ממה שאתה בהחלט צריך לזכור עכשיו - כל שיעורי העטיפה על טיפוסים פרימיטיביים הם בלתי ניתנים לשינוי. Integer, Byte, Character, Short, Boolean, Long, Double, Float- כל המחלקות הללו יוצרות אובייקטים בלתי ניתנים לשינוי . זה כולל גם מחלקות המשמשות ליצירת מספרים גדולים - BigIntegerו BigDecimal. לאחרונה עברנו חריגים ונגענו ב StackTrace. אז: אובייקטים של המחלקה java.lang.StackTraceElement הם גם בלתי ניתנים לשינוי. זה הגיוני: אם מישהו יכול לשנות את הנתונים בערימה שלנו, זה יכול לשלול את כל העבודה איתו. תאר לעצמך שמישהו נכנס ל- StackTrace ומשנה את OutOfMemoryError ל- FileNotFoundException . ואתה צריך לעבוד עם המחסנית הזו ולחפש את הסיבה לשגיאה. והתוכנה לא משתמשת בקבצים בכלל :) לכן, ליתר בטחון, האובייקטים הללו הפכו לבלתי ניתנים לשינוי. ובכן, עם StackTraceElement זה פחות או יותר ברור. למה שמישהו ירצה להפוך מחרוזות לבלתי ניתנות לשינוי? מה תהיה הבעיה אם אפשר היה לשנות את הערכים שלהם. זה כנראה יהיה אפילו יותר נוח :/ יש לזה כמה סיבות. ראשית, חיסכון בזיכרון. ניתן למקם מחרוזות בלתי ניתנות לשינוי String Poolוניתן להשתמש באותן מחרוזות בכל פעם במקום ליצור חדשות. שנית, בטיחות. לדוגמה, רוב כניסות וסיסמאות בכל תוכנית הן מחרוזות. האפשרות לשנות אותם עלולה להוביל לבעיות בהרשאה. ישנן סיבות אחרות, אבל עדיין לא הגענו אליהן בלימוד ג'אווה - נחזור מאוחר יותר.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION