JavaRush /وبلاگ جاوا /Random-FA /نهایی، ثابت و تغییرناپذیر در جاوا

نهایی، ثابت و تغییرناپذیر در جاوا

در گروه منتشر شد
سلام! کلمه "تغییر کننده" قبلاً برای شما آشنا است. حداقل، شما با اصلاح کننده های دسترسی (عمومی، خصوصی) و اصلاح کننده ایستا مواجه شده اید. امروز در مورد اصلاح کننده نهایی ویژه صحبت خواهیم کرد . می‌توان گفت آن بخش‌هایی از برنامه‌مان را که در آن‌ها به رفتار ثابت، بدون ابهام و تغییرناپذیر نیاز داریم، «تثبیت» می‌کند. می توان از آن در سه حوزه برنامه ما استفاده کرد: کلاس ها، متدها و متغیرها. Immutable در جاوا: نهایی، ثابت و Immutable - 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
}
کامپایلر بلافاصله یک خطا ایجاد می کند. بسیاری از کلاس‌ها قبلاً در جاوا پیاده‌سازی شده‌اند 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;//так делать можно
}
ثانیاً به نام متغیر خود توجه کنید. ثابت های جاوا یک قرارداد نامگذاری متفاوت دارند. این همان camelCase نیست که ما به آن عادت کرده ایم. در مورد یک متغیر معمولی، ما آن را ثابت مثال می نامیم، اما نام ثابت ها با حروف بزرگ نوشته می شود، و بین کلمات (اگر چندین مورد از آنها وجود دارد) یک خط زیر وجود دارد - "CONSTANT_EXAMPLE". چرا ثابت مورد نیاز است؟ به عنوان مثال، اگر دائماً از مقدار ثابتی در یک برنامه استفاده کنید، آنها مفید خواهند بود. فرض کنید تصمیم گرفتید به تاریخ بروید و بازی The Witcher 4 را به تنهایی بنویسید. بدیهی است که بازی دائماً از نام شخصیت اصلی - "Geralt of Rivia" استفاده می کند. بهتر است این خط و نام سایر قهرمانان را به یک ثابت جدا کنید: مقدار مورد نیاز در یک مکان ذخیره می شود و قطعاً هنگام تایپ آن برای میلیونمین بار اشتباه نخواهید کرد.
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. اگه خواستم اسمشو عوض کردم اگر می خواست سنش را عوض می کرد یا چیز دیگری. اما در جاوا چندین نوع داده وجود دارد که حالت خاصی دارند. آنها تغییر ناپذیر یا غیر قابل تغییر هستند . این بدان معنی است که اگر یک کلاس تغییرناپذیر باشد، وضعیت اشیاء آن قابل تغییر نیست. مثال ها؟ شاید تعجب کنید، اما معروف ترین مثال کلاس Immutable این است 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به آن اشاره می کند. اما ما به هیچ وجه نمی‌توانیم روی شی «I love Java» تأثیر بگذاریم. خوب، بیایید آن را متفاوت امتحان کنیم! کلاس Stringپر از متد است و به نظر می رسد برخی از آنها وضعیت ردیف را تغییر می دهند! به عنوان مثال، یک روش وجود دارد replace(). بیایید در خط خود کلمه "جاوا" را به کلمه "پایتون" تغییر دهیم!
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کار می کنند. شما نمی توانید با شی "I love Java" کاری انجام دهید. فقط یک شی جدید ایجاد کنید و بنویسید: "شیء جدید = نتیجه برخی دستکاری ها با شی "I love Java ". چه انواع دیگری غیر قابل تغییر هستند؟ از آنچه اکنون قطعاً باید به خاطر بسپارید - همه کلاس های لفاف بر روی انواع اولیه تغییر ناپذیر هستند. Integer, Byte, Character, Short, Boolean, Long, Double, Float- همه این کلاس ها اشیای Immutable ایجاد می کنند . این همچنین شامل کلاس هایی است که برای ایجاد اعداد بزرگ استفاده می شوند - 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