JavaRush /وبلاگ جاوا /Random-FA /50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا. قسمت 1
Roman Beekeeper
مرحله

50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا. قسمت 1

در گروه منتشر شد
سلام به همه خانم ها و آقایان مهندسان نرم افزار! بیایید در مورد سوالات مصاحبه صحبت کنیم. در مورد آنچه که باید برای آن آماده شوید و آنچه باید بدانید. این یک دلیل عالی برای تکرار یا مطالعه این نکات از ابتدا است. 50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 1من مجموعه نسبتاً گسترده ای از سؤالات متداول در مورد OOP، نحو جاوا، استثنائات در جاوا، مجموعه ها و چند رشته ای دارم که برای راحتی آنها را به چند قسمت تقسیم می کنم. مهم:ما فقط در مورد نسخه های جاوا تا 8 صحبت خواهیم کرد. همه نوآوری های 9، 10، 11، 12، 13 در اینجا در نظر گرفته نمی شوند. هر گونه ایده / نظر در مورد چگونگی بهبود پاسخ ها استقبال می شود . خواندن مبارک، بیایید برویم!

مصاحبه جاوا: سوالات OOP

1. جاوا چه ویژگی هایی دارد؟

پاسخ:
  1. مفاهیم OOP:

    1. شی گرایی؛
    2. وراثت؛
    3. کپسوله سازی؛
    4. پلی مورفیسم؛
    5. انتزاع - مفهوم - برداشت.
  2. Cross-platform: یک برنامه جاوا را می توان بر روی هر پلتفرمی بدون هیچ گونه تغییری اجرا کرد. تنها چیزی که نیاز دارید JVM (ماشین مجازی جاوا) نصب شده است.

  3. عملکرد بالا: JIT (کامپایلر Just In Time) کارایی بالا را امکان پذیر می کند. JIT بایت کد را به کد ماشین تبدیل می کند و سپس JVM اجرا را شروع می کند.

  4. Multithreading: رشته ای از اجرا که به نام Thread. JVM یک رشته به نام ایجاد می کند main thread. یک برنامه نویس می تواند چندین رشته را با ارث بردن از کلاس Thread یا با پیاده سازی یک رابط ایجاد کند Runnable.

2. ارث چیست؟

وراثت به این معنی است که یک کلاس می تواند کلاس دیگری را به ارث ببرد (" گسترش می دهد "). به این ترتیب می توانید از کد کلاسی که از آن به ارث برده اید مجددا استفاده کنید. کلاس موجود به عنوان شناخته می شود superclassو کلاس در حال ایجاد به عنوان شناخته می شود subclass. همچنین می گویند parentو child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
کجاست و - . Animal_parentDogchild

3. کپسولاسیون چیست؟

این سوال اغلب در طول مصاحبه های توسعه دهندگان جاوا مطرح می شود. کپسوله سازی با استفاده از اصلاح کننده های دسترسی، با استفاده از گیرنده ها و تنظیم کننده ها، اجرا را پنهان می کند. این کار به منظور بستن دسترسی برای استفاده خارجی در مکان هایی که توسعه دهندگان آن را ضروری می دانند انجام می شود. یک نمونه قابل دسترس از زندگی یک ماشین است. ما دسترسی مستقیم به عملکرد موتور نداریم. برای ما کار این است که کلید را در جرقه قرار دهیم و موتور را روشن کنیم. و اینکه چه فرآیندهایی در زیر کاپوت انجام خواهد شد به ما مربوط نیست. علاوه بر این، دخالت ما در این فعالیت می تواند منجر به یک وضعیت غیرقابل پیش بینی شود که به دلیل آن می توانیم ماشین را بشکنیم و به خود آسیب برسانیم. دقیقاً همین اتفاق در برنامه نویسی می افتد. در ویکی پدیا به خوبی توضیح داده شده است . همچنین مقاله ای در مورد کپسوله سازی در JavaRush وجود دارد .

4. پلی مورفیسم چیست؟

Polymorphism توانایی یک برنامه برای استفاده یکسان از اشیاء با رابط یکسان بدون اطلاعات در مورد نوع خاص آن شی است. همانطور که می گویند، یک رابط - بسیاری از پیاده سازی ها. با پلی مورفیسم، می توانید انواع مختلف اشیاء را بر اساس رفتار مشترک آنها ترکیب و استفاده کنید. به عنوان مثال، ما یک کلاس Animal داریم که دارای دو نسل است - سگ و گربه. کلاس Animal عمومی یک رفتار مشترک برای همه دارد - ایجاد صدا. در مواردی که باید همه فرزندان کلاس Animal را کنار هم قرار دهیم و روش "ساخت صدا" را اجرا کنیم، از امکانات چندشکلی استفاده می کنیم. این چیزی است که به نظر خواهد رسید:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
پس چندشکلی به ما کمک می کند. علاوه بر این، این همچنین در مورد روش های چند شکلی (بارگذاری بیش از حد) صدق می کند. تمرین استفاده از پلی مورفیسم

سوالات مصاحبه - نحو جاوا

5. سازنده در جاوا چیست؟

ویژگی های زیر معتبر است:
  1. هنگامی که یک شی جدید ایجاد می شود، برنامه از سازنده مناسب برای این کار استفاده می کند.
  2. سازنده مانند یک روش است. ویژگی آن این است که هیچ عنصر بازگشتی (از جمله void) وجود ندارد و نام آن با نام کلاس یکی است.
  3. اگر هیچ سازنده ای به صراحت نوشته نشود، یک سازنده خالی به طور خودکار ایجاد می شود.
  4. سازنده را می توان نادیده گرفت.
  5. اگر سازنده ای با پارامترها ایجاد شده است، اما بدون پارامتر نیز مورد نیاز است، باید جداگانه نوشته شود، زیرا به طور خودکار ایجاد نمی شود.

6. کدام دو کلاس از Object ارث نمی برند؟

فریب تحریکات را نخورید، چنین کلاس هایی وجود ندارد: همه کلاس ها به طور مستقیم یا از طریق اجداد از کلاس Object به ارث برده می شوند!

7. متغیر محلی چیست؟

یکی دیگر از سوالات پرطرفدار در طول مصاحبه توسعه دهنده جاوا. متغیر محلی متغیری است که در داخل یک متد تعریف شده و تا لحظه اجرای متد وجود دارد. پس از پایان اجرا، متغیر محلی دیگر وجود نخواهد داشت. در اینجا برنامه ای وجود دارد که از متغیر محلی helloMessage در متد main() استفاده می کند:
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. Instance Variable چیست؟

Instance Variable متغیری است که در داخل یک کلاس تعریف شده است و تا لحظه ای که شی وجود دارد وجود دارد. یک مثال کلاس Bee است که دارای دو متغیر nectarCapacity و maxNectarCapacity است:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. اصلاح کننده های دسترسی چیست؟

اصلاح کننده های دسترسی ابزاری هستند که به شما امکان می دهد دسترسی به کلاس ها، متدها و متغیرها را سفارشی کنید. اصلاح کننده های زیر وجود دارد که به ترتیب افزایش دسترسی مرتب شده اند:
  1. private- برای متدها، فیلدها و سازنده ها استفاده می شود. سطح دسترسی فقط کلاسی است که در آن اعلام شده است.
  2. package-private(default)- قابل استفاده برای کلاس ها دسترسی فقط در یک بسته خاص که در آن یک کلاس، متد، متغیر، سازنده تعریف شده است.
  3. protected- همان دسترسی package-private+ برای آن دسته از کلاس هایی که از یک کلاس با اصلاح کننده ارث می برند protected.
  4. public- همچنین برای کلاس ها استفاده می شود. دسترسی کامل به کل برنامه
  5. 50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 2

10. روش های فراگیر چیست؟

نادیده گرفتن روش زمانی اتفاق می افتد که کودک بخواهد رفتار کلاس والد را تغییر دهد. اگر می خواهید آنچه در متد والد وجود دارد اجرا شود، می توانید از ساختاری مانند super.methodName() در فرزند استفاده کنید که کار متد والد را انجام می دهد و تنها پس از آن منطق اضافه کنید. الزاماتی که باید برآورده شوند:
  • امضای روش باید یکسان باشد.
  • مقدار بازگشتی باید یکسان باشد.

11. امضای متد چیست؟

50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 3امضای متد مجموعه ای از نام متد و آرگومان هایی است که متد می پذیرد. امضای متد یک شناسه منحصر به فرد برای یک روش در هنگام بارگذاری بیش از حد متدها است.

12. اضافه بار روش چیست؟

بارگذاری متد یک ویژگی چندشکلی است که در آن با تغییر امضای متد، می‌توانید روش‌های مختلفی را برای اقدامات مشابه ایجاد کنید:
  • نام روش مشابه؛
  • استدلال های مختلف؛
  • ممکن است نوع بازگشت متفاوتی وجود داشته باشد.
به عنوان مثال، همان add()of را ArrayListمی توان به صورت زیر بارگذاری کرد و بسته به آرگومان های ورودی، جمع را به روشی متفاوت انجام می دهد:
  • add(Object o)- به سادگی یک شی را اضافه می کند.
  • add(int index, Object o)- یک شی را به یک شاخص خاص اضافه می کند.
  • add(Collection<Object> c)- لیستی از اشیاء را اضافه می کند.
  • add(int index, Collection<Object> c)- لیستی از اشیاء را اضافه می کند که از یک شاخص خاص شروع می شود.

13. اینترفیس چیست؟

وراثت چندگانه در جاوا پیاده سازی نمی شود، بنابراین برای غلبه بر این مشکل، اینترفیس هایی که ما می شناسیم اضافه شدند؛) برای مدت طولانی، اینترفیس ها فقط متدهایی داشتند بدون اینکه آنها را پیاده سازی کنند. به عنوان بخشی از این پاسخ، ما در مورد آنها صحبت خواهیم کرد. مثلا:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
برخی تفاوت های ظریف از این نتیجه می شود:
  • همه متدها در رابط عمومی و انتزاعی هستند.
  • همه متغیرها عمومی استاتیک نهایی هستند.
  • کلاس ها آنها را به ارث نمی برند (بسط می دهند)، بلکه آنها را پیاده سازی می کنند (پیاده می کنند). علاوه بر این، شما می توانید هر تعداد رابط را که دوست دارید پیاده سازی کنید.
  • کلاس هایی که یک اینترفیس را پیاده سازی می کنند باید پیاده سازی تمام روش هایی را که این رابط دارد ارائه دهند.
مثل این:
public class Cat implements Animal {
   public void makeSound() {
       // method implementation
   }

   public void eat() {
       // implementation
   }

   public void sleep() {
       // implementation
   }
}

14. متد پیش فرض در Interface چیست؟

حالا بیایید در مورد روش های پیش فرض صحبت کنیم. برای چه، برای چه کسی؟ این روش‌ها اضافه شدند تا همه چیز «هم مال شما و هم مال ما» باشد. من در مورد چه چیزی صحبت می کنم؟ بله، از یک طرف، لازم بود عملکرد جدیدی اضافه شود: lambdas، Stream API، از سوی دیگر، لازم بود آنچه جاوا به آن معروف است را ترک کنیم - سازگاری با عقب. برای این کار لازم بود راه حل های آماده ای در اینترفیس ها وارد شوند. روش های پیش فرض اینگونه به دست ما آمد. یعنی متد پیش‌فرض یک روش پیاده‌سازی شده در اینترفیس است که دارای کلمه کلیدی است default. به عنوان مثال، روش شناخته شده stream()در Collection. آن را بررسی کنید، این رابط به آن سادگی که به نظر می رسد نیست ;). یا همچنین یک روش به همان اندازه شناخته شده forEach()از Iterable. همچنین تا زمانی که روش‌های پیش‌فرض اضافه نشده بودند، وجود نداشت. به هر حال، شما همچنین می توانید در مورد این در JavaRush بخوانید .

15. پس چگونه می توان دو روش پیش فرض یکسان را به ارث برد؟

با توجه به پاسخ قبلی در مورد اینکه روش پیش فرض چیست، می توانید یک سوال دیگر بپرسید. اگر بتوانید متدها را در اینترفیس ها پیاده سازی کنید، از نظر تئوری می توانید دو اینترفیس را با یک متد پیاده سازی کنید و چگونه این کار را انجام دهید؟ دو رابط مختلف با یک روش وجود دارد:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
و یک کلاس وجود دارد که این دو رابط را پیاده سازی می کند. برای جلوگیری از عدم قطعیت و کامپایل کد، باید متد را foo()در کلاس نادیده بگیریم C، و به سادگی می‌توانیم متدی foo()از هر یک از اینترفیس‌های موجود در آن را فراخوانی کنیم - Aیا B. اما چگونه می توان یک روش رابط خاص را انتخاب کرد Аیا В؟ ساختاری مانند این برای این وجود دارد A.super.foo():
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
یا:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
بنابراین، یک متد foo()کلاس Cیا از متد پیش‌فرض foo()از اینترفیس Aیا یک متد foo()از رابط استفاده می‌کند B.

16. متدها و کلاس های انتزاعی چیست؟

جاوا یک کلمه رزرو شده دارد abstractکه برای نشان دادن کلاس ها و متدهای انتزاعی استفاده می شود. ابتدا چند تعاریف متد انتزاعی متدی است که بدون پیاده سازی با کلمه کلیدی abstractدر یک کلاس انتزاعی ایجاد می شود. یعنی این روشی است مانند رابط، فقط با اضافه کردن یک کلمه کلیدی، به عنوان مثال:
public abstract void foo();
کلاس انتزاعی کلاسی است که abstractکلمه زیر را نیز دارد:
public abstract class A {

}
یک کلاس انتزاعی چندین ویژگی دارد:
  • یک شی را نمی توان بر اساس آن ایجاد کرد.
  • می تواند روش های انتزاعی داشته باشد.
  • ممکن است روش های انتزاعی نداشته باشد.
کلاس های انتزاعی برای تعمیم نوعی انتزاع (با عرض پوزش برای توتولوژی) مورد نیاز است که در زندگی واقعی وجود ندارد، اما شامل بسیاری از رفتارها و حالت های رایج (یعنی روش ها و متغیرها) است. نمونه های زیادی از زندگی وجود دارد. همه چیز در اطراف ماست. این می تواند "حیوان"، "ماشین"، "شکل هندسی" و غیره باشد.

17. تفاوت String، String Builder و String Buffer چیست؟

مقادیر Stringدر یک مجموعه رشته ثابت ذخیره می شوند. هنگامی که یک ردیف ایجاد می شود، در این مجموعه ظاهر می شود. و امکان حذف آن وجود نخواهد داشت. مثلا:
String name = "book";
... متغیر به استخر رشته اشاره خواهد کرد استخر رشته ثابت 50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 4 اگر نام متغیر را روی مقدار دیگری تنظیم کنید، موارد زیر را دریافت خواهید کرد:
name = "pen";
استخر رشته ثابت 50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 5بنابراین این دو مقدار در آنجا باقی خواهند ماند. بافر رشته:
  • مقادیر Stringدر پشته ذخیره می شوند. اگر مقدار تغییر کند، مقدار جدید با مقدار قبلی جایگزین می شود.
  • String Bufferهماهنگ شده و در نتیجه نخ ایمن است.
  • با توجه به ایمنی رزوه، سرعت عمل بسیار مورد نظر باقی می ماند.
مثال:
StringBuffer name = "book";
50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 6به محض اینکه مقدار نام تغییر می کند، مقدار روی پشته تغییر می کند: 50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 7StringBuilder دقیقاً مشابه است StringBuffer، فقط در thread ایمن نیست. بنابراین، سرعت آن به وضوح بالاتر از در است StringBuffer.

18. تفاوت بین کلاس انتزاعی و رابط چیست؟

کلاس چکیده:
  • کلاس های انتزاعی یک سازنده پیش فرض دارند. هر بار که فرزندی از این کلاس انتزاعی ایجاد می شود، نامیده می شود.
  • شامل هر دو روش انتزاعی و غیرانتزاعی است. به طور کلی، ممکن است شامل متدهای انتزاعی نباشد، اما همچنان یک کلاس انتزاعی باشد.
  • کلاسی که از یک کلاس انتزاعی به ارث می برد باید فقط متدهای انتزاعی را پیاده سازی کند.
  • یک کلاس انتزاعی می تواند شامل یک متغیر نمونه باشد (به سوال شماره 5 مراجعه کنید).
رابط:
  • سازنده ندارد و نمی توان آن را مقدار دهی اولیه کرد.
  • فقط روش های انتزاعی باید اضافه شوند (بدون احتساب روش های پیش فرض).
  • کلاس هایی که یک رابط را پیاده سازی می کنند باید همه متدها را پیاده سازی کنند (بدون احتساب روش های پیش فرض).
  • اینترفیس ها فقط می توانند حاوی ثابت باشند.

19. چرا دسترسی به یک عنصر در یک آرایه O(1) می گیرد؟

این سوال به معنای واقعی کلمه مربوط به آخرین مصاحبه است. همانطور که بعداً فهمیدم، این سؤال برای این است که ببینیم یک فرد چگونه فکر می کند. روشن است که معنای عملی چندانی در این معرفت وجود ندارد: فقط دانستن این حقیقت کافی است. ابتدا، باید روشن کنیم که O(1) تعیین پیچیدگی زمانی یک الگوریتم است ، زمانی که عملیات در زمان ثابت انجام شود. یعنی این نامگذاری سریعترین اجراست. برای پاسخ به این سوال، باید بدانیم که در مورد آرایه ها چه می دانیم؟ برای ایجاد یک آرایه intباید موارد زیر را بنویسیم:
int[] intArray = new int[100];
از این ضبط چند نتیجه می توان گرفت:
  1. هنگام ایجاد یک آرایه، نوع آن مشخص است، اگر نوع آن مشخص باشد، مشخص است که هر خانه از آرایه چه اندازه ای خواهد داشت.
  2. معلوم است که آرایه چه اندازه خواهد بود.
از این نتیجه می شود: برای درک اینکه در کدام سلول باید بنویسید، فقط باید محاسبه کنید که در کدام ناحیه حافظه بنویسید. برای یک ماشین نمی تواند ساده تر باشد. دستگاه دارای یک شروع حافظه اختصاص داده شده، تعدادی عنصر و یک اندازه سلول است. از اینجا مشخص می شود که فضای ضبط برابر با محل شروع آرایه + اندازه سلول ضربدر اندازه آن خواهد بود.

چگونه می توانید O(1) را در دسترسی به اشیاء در یک ArrayList بدست آورید؟

این سوال بلافاصله بعد از سوال قبلی می آید. درست است که وقتی با یک آرایه کار می کنیم و موارد اولیه در آنجا وجود دارد، از قبل می دانیم که اندازه این نوع در هنگام ایجاد چقدر است. اما اگر طرحی مانند تصویر داشته باشیم: 50 پرسش و پاسخ اصلی مصاحبه اصلی جاوا.  قسمت 1 - 8و بخواهیم یک مجموعه با عناصر نوع A ایجاد کنیم و پیاده سازی های مختلفی را اضافه کنیم - B، C، D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
در این شرایط چگونه می توان فهمید که هر سلول چه اندازه ای خواهد داشت، زیرا هر شی متفاوت خواهد بود و ممکن است فیلدهای اضافی متفاوتی داشته باشد (یا کاملاً متفاوت باشد). چه باید کرد؟ در اینجا سؤال به گونه ای مطرح می شود که باعث سردرگمی و سردرگمی شود. می دانیم که در واقع مجموعه اشیاء را ذخیره نمی کند، بلکه فقط به این اشیا پیوند می دهد. و همه لینک ها یک اندازه دارند و معلوم است. بنابراین شمارش فضا در اینجا مانند سوال قبلی کار می کند.

21. اتوباکسینگ و جعبه گشایی

پیشینه تاریخی: اتوباکسینگ و اتوانباکسینگ یکی از نوآوری های اصلی JDK 5 است. اتوباکسینگ فرآیند تبدیل خودکار از نوع اولیه به کلاس لفاف مناسب است. جعبه گشایی خودکار - دقیقاً برعکس اتوباکسینگ انجام می دهد - یک کلاس wrapper را به یک کلاس اولیه تبدیل می کند. اما اگر یک مقدار wrapper وجود داشته باشد null، در حین باز کردن بسته‌بندی استثنا ایجاد می‌شود NullPointerException.

تطبیق بدوی - لفاف

اولیه کلاس لفاف
بولی بولی
بین المللی عدد صحیح
بایت بایت
کاراکتر شخصیت
شناور شناور
طولانی طولانی
کوتاه کوتاه
دو برابر دو برابر

بسته بندی خودکار رخ می دهد:

  • هنگام اختصاص دادن یک مرجع اولیه به کلاس wrapper:

    قبل از جاوا 5:

    //manual packaging or how it was BEFORE Java 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // and so on to other types
    }
    
    после Java 5:
    //automatic packaging or how it became in Java 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // and so on to other types
    }
  • هنگام ارسال یک آرگومان اولیه به روشی که انتظار یک wrapper را دارد:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

بسته بندی خودکار رخ می دهد:

  • وقتی یک متغیر اولیه را به کلاس wrapper اختصاص می دهیم:

    //before Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    //and after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • در موارد با عملیات حسابی. آنها فقط برای انواع ابتدایی اعمال می شوند؛ برای این کار باید جعبه گشایی را در ابتدایی انجام دهید.

    // Before Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // for comparison it was necessary to do this:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • هنگامی که به یک wrapper در روشی ارسال می شود که اولیه مربوطه را می پذیرد:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22. کلمه کلیدی نهایی چیست و کجا باید از آن استفاده کرد؟

کلمه کلیدی را finalمی توان برای متغیرها، متدها و کلاس ها استفاده کرد.
  1. یک متغیر نهایی را نمی توان دوباره به شی دیگری اختصاص داد.
  2. طبقه نهایی عقیم است)) نمی تواند وارث داشته باشد.
  3. روش نهایی را نمی توان روی یک اجداد نادیده گرفت.
ما قسمت بالایی را پوشش داده ایم، اکنون اجازه دهید با جزئیات بیشتر در مورد آن بحث کنیم.

متغیرهای نهایی

جاوا دو راه برای ایجاد یک متغیر و اختصاص مقداری به آن در اختیار ما قرار می دهد:
  1. می توانید یک متغیر را اعلام کنید و بعداً آن را مقداردهی اولیه کنید.
  2. می توانید یک متغیر را اعلام کرده و بلافاصله آن را اختصاص دهید.
مثال با استفاده از متغیر نهایی برای این موارد:
public class FinalExample {

   //final static variable, which is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final is a variable that is not initialized, but will only work if
   //initialize this in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // final field Config.creationTime cannot be assigned
//    finalExample.creationTime = 1L;
   }
}

آیا می توان متغیر Final را ثابت در نظر گرفت؟

از آنجایی که نمی‌توانیم مقدار جدیدی را به یک متغیر نهایی اختصاص دهیم، به نظر می‌رسد که اینها متغیرهای ثابت هستند. اما این فقط در نگاه اول است. اگر نوع داده ای که متغیر به آن اشاره می کند immutable، باشد، بله، یک ثابت است. اما اگر نوع داده mutableقابل تغییر باشد، با استفاده از متدها و متغیرها می توان مقدار شی مورد finalنظر متغیر را تغییر داد و در این حالت نمی توان آن را ثابت نامید. بنابراین، مثال نشان می دهد که برخی از متغیرهای نهایی واقعاً ثابت هستند، اما برخی دیگر ثابت نیستند و می توان آنها را تغییر داد.
public class FinalExample {

   //immutable final variables:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // mutable filter variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

متغیرهای نهایی محلی

هنگامی که finalیک متغیر در داخل یک متد ایجاد می شود، به آن local finalمتغیر می گویند:
public class FinalExample {

   public static void main(String[] args) {
       // This is how you can
       final int minAgeForDriveCar = 18;

       // or you can do it this way, in the foreach loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
ما می توانیم از کلمه کلیدی finalدر حلقه توسعه یافته استفاده کنیم forزیرا پس از تکمیل یک تکرار از حلقه، forهر بار یک متغیر جدید ایجاد می شود. اما این برای یک حلقه for معمولی صدق نمی کند، بنابراین کد زیر یک خطای زمان کامپایل ایجاد می کند.
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

کلاس پایانی

شما نمی توانید کلاسی را که به عنوان اعلام شده است گسترش دهید final. به زبان ساده، هیچ کلاسی نمی تواند از این یکی ارث ببرد. یک مثال عالی finalاز یک کلاس در JDK است String. اولین گام برای ایجاد یک کلاس تغییرناپذیر این است که آن را به عنوان علامت گذاری کنید final، به طوری که نمی توان آن را گسترش داد:
public final class FinalExample {
}

// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}

روش های نهایی

هنگامی که یک روش علامت نهایی می شود، به آن روش نهایی می گویند (منطقی، درست است؟). متد Final را نمی توان در یک کلاس نزول لغو کرد. به هر حال، متدهای کلاس Object - wait() و notify() - نهایی هستند، بنابراین ما فرصتی برای لغو آنها نداریم.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // compile error here
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

نحوه و مکان استفاده از final در جاوا

  • از کلمه کلیدی نهایی برای تعریف برخی از ثابت های سطح کلاس استفاده کنید.
  • زمانی که نمی خواهید آنها را تغییر دهید، متغیرهای نهایی را برای اشیاء ایجاد کنید. به عنوان مثال، ویژگی‌های شی خاص که می‌توانیم برای اهداف لاگ استفاده کنیم.
  • اگر نمی خواهید کلاس تمدید شود، آن را به عنوان نهایی علامت بزنید.
  • اگر نیاز به ایجاد یک کلاس غیرقابل تغییر دارید، باید آن را نهایی کنید.
  • اگر می‌خواهید اجرای یک متد در نسل‌های آن تغییر نکند، روش را به‌عنوان مشخص کنید final. این برای اطمینان از عدم تغییر پیاده سازی بسیار مهم است.

23. mutable unmutable چیست؟

قابل تغییر است

قابل تغییر اشیایی هستند که حالت ها و متغیرهای آنها پس از ایجاد قابل تغییر است. به عنوان مثال، کلاس هایی مانند StringBuilder، StringBuffer. مثال:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // this setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("first address");
       System.out.println(obj.getAddress());

       // update the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

تغییرناپذیر

Immutable اشیائی هستند که حالتها و متغیرهای آنها پس از ایجاد شیء قابل تغییر نیستند. چرا یک کلید عالی برای HashMap نیست، درست است؟) به عنوان مثال، String، Integer، Double و غیره. مثال:
// make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Therefore, do not change this field in any way, so this is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24. چگونه یک کلاس تغییرناپذیر بنویسیم؟

بعد از اینکه متوجه شدید که اشیاء قابل تغییر و تغییر ناپذیر چیست، سؤال بعدی طبیعی است - چگونه آن را بنویسیم؟ برای نوشتن یک کلاس تغییرناپذیر غیرقابل تغییر، باید مراحل ساده را دنبال کنید:
  • کلاس را نهایی کنید
  • همه فیلدها را خصوصی کنید و فقط دریافت کننده ها را برای آنها ایجاد کنید. البته نیازی به ست نیست.
  • تمام فیلدهای قابل تغییر را نهایی کنید تا مقدار فقط یک بار تنظیم شود.
  • تمام فیلدها را از طریق سازنده مقداردهی اولیه کنید، یک کپی عمیق انجام دهید (یعنی کپی کردن خود شی، متغیرهای آن، متغیرهای متغیرها و غیره)
  • اشیاء متغیر قابل تغییر را در گیرنده کلون کنید تا فقط کپی مقادیر را برگرداند و نه ارجاع به اشیاء واقعی.
مثال:
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Clone the object before returning it.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * In the constructor, deep copy the mutable objects.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println("Performing a deep copy in the constructor");
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION