JavaRush /وبلاگ جاوا /Random-FA /تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا...

تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا. قسمت 12

در گروه منتشر شد
سلام! دانش قدرت است. هرچه قبل از اولین مصاحبه دانش بیشتری داشته باشید، اعتماد به نفس بیشتری نسبت به آن خواهید داشت. تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 1با دانش کافی، گیج کردن شما دشوار خواهد بود و در عین حال قادر خواهید بود مصاحبه کننده خود را به طرز خوشایندی غافلگیر کنید. بنابراین، امروز، بدون هیچ مقدمه ای، با بررسی بیش از 250 سوال برای یک توسعه دهنده جاوا، به تقویت پایه نظری شما ادامه می دهیم . تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 2

103. احکام بررسی استثنائات در ارث چیست؟

اگر من سوال را درست متوجه شده باشم در مورد قوانین کار با استثناء در هنگام ارث می پرسند و آنها به شرح زیر است:
  • یک روش لغو یا پیاده‌سازی شده در یک نسل/پیاده‌سازی نمی‌تواند استثناهای بررسی‌شده را که در سلسله مراتب بالاتر از استثناهای متد سوپرکلاس/اینترفیس هستند، ایجاد کند.
یعنی اگر یک رابط Animal خاص با متدی داشته باشیم که IOException را پرتاب می کند :
public  interface Animal {
   void voice() throws IOException;
}
در پیاده‌سازی این رابط، نمی‌توانیم یک استثنا پرتاب‌شده عمومی‌تر (به عنوان مثال، Exception ، Throwable ) پرتاب کنیم، اما می‌توانیم آن را با یک استثنای نسل، مانند FileNotFoundException جایگزین کنیم :
public class Cat implements Animal {
   @Override
   public void voice() throws FileNotFoundException {
// некоторая реализация
   }
}
  • سازنده کلاس فرعی باید در بلوک های پرتاب خود تمام کلاس های استثنا پرتاب شده توسط سازنده سوپرکلاس را که هنگام ایجاد شی از آن فراخوانی می شود، شامل شود.
فرض کنید سازنده کلاس Animal استثناهای زیادی ایجاد می کند:
public class Animal {
  public Animal() throws ArithmeticException, NullPointerException, IOException {
  }
سپس وارث کلاس نیز باید آنها را در سازنده نشان دهد:
public class Cat extends Animal {
   public Cat() throws ArithmeticException, NullPointerException, IOException {
       super();
   }
یا، مانند روش ها، می توانید نه استثنائات یکسان، بلکه موارد کلی تر را مشخص کنید. در مورد ما، مشخص کردن یک استثنا کلی تر - استثنا کافی است ، زیرا این جد مشترک هر سه استثنا در نظر گرفته شده است:
public class Cat extends Animal {
   public Cat() throws Exception {
       super();
   }

104. آیا می توانید برای زمانی که بلوک نهایی اجرا نمی شود کد بنویسید؟

اول، بیایید به یاد بیاوریم که در نهایت چیست . پیش از این، ما مکانیسمی را برای گرفتن استثناها بررسی کردیم: بلوک try ، منطقه گرفتن را مشخص می کند، در حالی که بلوک(های) catch کدی است که وقتی یک استثنا خاص پرتاب می شود، کار می کند. سرانجام سومین بلوک کد پس از نهایتاً است که با catch قابل تعویض است اما متقابلاً منحصر به فرد نیست. ماهیت این بلوک این است که کد موجود در آن همیشه کار می کند، صرف نظر از نتیجه تلاش یا گرفتن ( صرف نظر از اینکه آیا استثنا پرتاب شده است یا خیر). موارد شکست آن بسیار نادر و غیر طبیعی است. ساده ترین حالت شکست زمانی است که متد System.exit(0) در کد بالا فراخوانی می شود که برنامه را خاتمه می دهد (خاموش می کند):
try {
   throw new IOException();
} catch (IOException e) {
   System.exit(0);
} finally {
   System.out.println("Данное сообщение не будет выведенно в консоль");
}
شرایط دیگری نیز وجود دارد که در نهایت کار نمی کند:
  • خاتمه غیرعادی برنامه ناشی از مشکلات حیاتی سیستم، یا سقوط برخی از خطاها که برنامه را از کار می اندازد (نمونه ای از خطا می تواند همان StackOwerflowError باشد که هنگام سرریز شدن حافظه پشته رخ می دهد).
  • وقتی نخ شیطان از ry عبور می کند ... در نهایت بلاک می شود و به موازات آن برنامه به پایان می رسد. به هر حال، نخ شیطان یک رشته برای اقدامات پس زمینه است، یعنی اولویت و اجباری نیست و برنامه منتظر نمی ماند تا کارش تمام شود.
  • رایج ترین حلقه بی نهایت، در تلاش یا گرفتن ، که یک بار در آن جریان برای همیشه آنجا باقی می ماند:

    try {
       while (true) {
       }
    } finally {
       System.out.println("Данное сообщение не будет выведенно в консоль");
    }

این سوال در مصاحبه‌های تازه‌کارها بسیار محبوب است، بنابراین چند مورد از این موقعیت‌های استثنایی ارزش یادآوری را دارند. تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 3

105. مثالی از مدیریت چندین استثنا در یک بلوک catch بنویسید

1) شاید سوال اشتباه پرسیده شده باشد. تا آنجا که من متوجه شدم، این سوال به معنای چند گیرکردن برای یک بلوک آزمایشی است :
try {
  throw new FileNotFoundException();
} catch (FileNotFoundException e) {
   System.out.print("Упс, у вас упало исключение - " + e);
} catch (IOException e) {
   System.out.print("Упс, у вас упало исключение - " + e);
} catch (Exception e) {
   System.out.print("Упс, у вас упало исключение - " + e);
}
اگر یک استثنا در یک بلوک try رخ دهد ، بلوک‌های catch به طور متناوب سعی می‌کنند آن را از بالا به پایین بگیرند. اگر یک بلوک catch خاص موفق شد، این حق را دارد که استثنا را مدیریت کند، در حالی که بقیه بلوک‌های زیر دیگر وجود ندارند. قادر به تلاش برای گرفتن آن و پردازش آن به روش خود هستند. بنابراین، استثناهای باریک‌تر در زنجیره بلوک catch بالاتر و استثناهای گسترده‌تر در پایین‌تر قرار می‌گیرند. به عنوان مثال، اگر در اولین بلوک catch ما یک استثنا از کلاس Exception گرفته شود ، آنگاه استثناهای علامت‌گذاری شده نمی‌توانند وارد بلاک‌های باقی‌مانده شوند (بلاک‌های باقی‌مانده با فرزندان Exception مطلقاً بی‌فایده خواهند بود). 2) سوال به درستی پرسیده شد در این صورت پردازش ما به صورت زیر خواهد بود:
try {
  throw new NullPointerException();
} catch (Exception e) {
   if (e instanceof FileNotFoundException) {
       // некоторая обработка с сужением типа (FileNotFoundException)e
   } else if (e instanceof ArithmeticException) {
       // некоторая обработка с сужением типа (ArithmeticException)e
   } else if(e instanceof NullPointerException) {
       // некоторая обработка с сужением типа (NullPointerException)e
   }
پس از گرفتن یک استثنا از طریق catch ، سعی می کنیم نوع خاص آن را از طریق روش instanceof که برای بررسی تعلق یک شی به یک نوع خاص استفاده می شود، دریابیم تا بعداً بتوانیم آن را بدون عواقب منفی به این نوع محدود کنیم. هر دو روش در نظر گرفته شده را می توان در یک موقعیت استفاده کرد، اما من گفتم که این سوال نادرست است زیرا من گزینه دوم را خوب نمی نامم و هرگز آن را در عمل ندیده ام، در حالی که روش اول با چند گیر بسیار گسترده شده است. توجه. گسترش تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 4

106. کدام عملگر به شما اجازه می دهد که یک استثنا را مجبور به پرتاب کنید؟ یک مثال بنویسید

من قبلاً چندین بار از آن در بالا استفاده کرده ام، اما با این وجود این کلمه کلیدی را تکرار می کنم - پرتاب . کاربرد مثال (اجبار کردن یک استثنا):
throw new NullPointerException();

107. آیا روش اصلی می تواند یک استثناء پرتابی ایجاد کند؟ اگر هست به کجا منتقل می شود؟

قبل از هر چیز می خواهم به این نکته توجه کنم که main چیزی بیش از یک متد معمولی نیست و بله، توسط ماشین مجازی برای شروع اجرای برنامه فراخوانی می شود، اما علاوه بر این، می توان آن را از هر کد دیگری فراخوانی کرد. به این معنی که آن نیز تابع قوانین معمول برای تعیین استثناهای بررسی شده پس از پرتاب است :
public static void main(String[] args) throws IOException {
بر این اساس، استثناهایی نیز ممکن است در آن رخ دهد. اگر main در برخی از روش ها فراخوانی نشده باشد، اما به عنوان نقطه راه اندازی برنامه راه اندازی شده باشد، آنگاه استثنای ایجاد شده توسط آن توسط رهگیر .UncaughtExceptionHandler مدیریت می شود . این هندلر یک در هر نخ است (یعنی یک کنترلر در هر نخ). در صورت لزوم، می توانید کنترل کننده خود را ایجاد کرده و با استفاده از متد setDefaultUncaughtExceptionHandler که روی شی Thread فراخوانی شده است، تنظیم کنید .

چند رشته ای

تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 5

108. چه ابزارهایی را برای کار با چند نخ می شناسید؟

ابزارهای پایه/پایه برای استفاده از Multithreading در جاوا:
  • Synchronized مکانیزمی است برای بستن (مسدود کردن) یک متد/بلاک زمانی که یک نخ وارد آن می شود، از موضوعات دیگر.
  • فرار مکانیزمی برای اطمینان از دسترسی ثابت به یک متغیر توسط رشته های مختلف است، یعنی با وجود این اصلاح کننده روی یک متغیر، تمام عملیات تخصیص و خواندن باید اتمی باشد. به عبارت دیگر، نخ ها این متغیر را در حافظه محلی خود کپی نمی کنند و آن را تغییر نمی دهند، بلکه مقدار اصلی آن را تغییر می دهند.
اطلاعات بیشتر در مورد فرار را اینجا بخوانید .
  • Runnable یک رابط است که می تواند (به ویژه، روش اجرا آن) در یک کلاس خاص پیاده سازی شود:
public class CustomRunnable implements Runnable {
   @Override
   public void run() {
       // некоторая логика
   }
}
و با ایجاد یک شی از این کلاس، می توانید با تنظیم این شی در سازنده شی Thread جدید و فراخوانی متد ()start آن، یک رشته جدید راه اندازی کنید :
Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
متد start متد run() پیاده سازی شده را در یک رشته مجزا اجرا می کند.
  • Thread کلاسی است که از آن ارث می برد (در حالی که روش اجرا را نادیده می گیرد ):
public class CustomThread extends Thread {
   @Override
   public void run() {
       // некоторая логика
   }
}
و با ایجاد یک شی از این کلاس و راه اندازی آن با استفاده از متد ()start ، یک موضوع جدید راه اندازی می کنیم:
new CustomThread().start();
  • Concurrency بسته ای است با ابزارهایی برای کار در یک محیط چند رشته ای.
متشکل از:
  • مجموعه های همزمان - مجموعه ای از مجموعه های تخصصی برای کار در یک محیط چند رشته ای.
  • صف - صف های تخصصی برای یک محیط چند رشته ای (مسدود و غیر مسدود).
  • همگام سازها ابزارهای تخصصی برای کار در یک محیط چند رشته ای هستند.
  • مجری ها مکانیسم هایی برای ایجاد استخرهای نخ هستند.
  • قفل ها - مکانیسم های همگام سازی نخ (انعطاف پذیرتر از موارد استاندارد - همگام سازی شده، منتظر بمانید، اطلاع رسانی کنید، به همه اطلاع دهید).
  • اتمی ها کلاس هایی هستند که برای اجرای چند رشته ای بهینه شده اند؛ هر عملیات اتمی است.
اطلاعات بیشتر در مورد بسته همزمان را اینجا بخوانید .

109. در مورد همگام سازی بین رشته ها صحبت کنید. متدهای wait(), notify() - notifyAll() join() برای چه مواردی استفاده می شوند؟

تا آنجا که من سوال را درک کردم، همگام سازی بین رشته ها در مورد اصلاح کننده کلید است - همگام سازی شده . این اصلاح کننده را می توان مستقیماً در کنار بلوک قرار داد:
synchronized (Main.class) {
   // некоторая логика
}
یا مستقیماً در امضای متد:
public synchronized void move() {
   // некоторая логика}
همانطور که قبلاً گفتم، همگام سازی مکانیزمی است که به شما امکان می دهد یک بلوک/روش را از رشته های دیگر ببندید، زمانی که یک رشته قبلاً وارد آن شده است. یک بلوک/روش را به عنوان یک اتاق در نظر بگیرید. جریانی که به سمت آن آمده، وارد آن می شود و آن را قفل می کند، نهرهای دیگر که به اتاق آمده و بسته است، نزدیک آن منتظر می مانند تا آزاد شود. پس از انجام کار، اولین نخ از اتاق خارج می شود و کلید را رها می کند. و بی جهت نبود که من دائماً در مورد کلید صحبت می کردم ، زیرا واقعاً وجود دارد. این یک شی خاص است که حالت اشغال/آزاد دارد. این شی به هر شی جاوا متصل است، بنابراین هنگام استفاده از یک بلوک همگام، باید در پرانتز شیئی را که می‌خواهیم mutex آن را ببندیم، نشان دهیم:
Cat cat = new Cat();
synchronized (cat) {
   // некоторая логика
}
شما همچنین می توانید از یک کلاس mutex استفاده کنید، همانطور که در مثال اول انجام دادم ( Main.class ). وقتی از synchronized روی یک متد استفاده می کنیم ، شیئی را که می خواهیم روی آن ببندیم مشخص نمی کنیم، درست است؟ در این حالت، برای یک متد غیر استاتیک، روی mutex شیء this ، یعنی شی فعلی این کلاس بسته می‌شود. استاتیک در mutex کلاس فعلی بسته می شود ( this.getClass(); ). در اینجا می توانید اطلاعات بیشتری در مورد mutex بخوانید . خوب، در مورد همگام سازی اینجا بخوانید . Wait() متدی است که mutex را آزاد می کند و رشته فعلی را در حالت آماده به کار قرار می دهد، گویی به مانیتور فعلی متصل است (چیزی شبیه یک لنگر). به همین دلیل، این روش را فقط می توان از یک بلوک یا متد هماهنگ فراخوانی کرد (در غیر این صورت، چه چیزی باید آزاد شود و چه چیزی باید انتظار داشته باشد). همچنین توجه داشته باشید که این متد از کلاس Object است . به طور دقیق تر، نه یک، بلکه حتی سه:
  • Wait() - رشته فعلی را در حالت انتظار قرار می دهد تا زمانی که رشته دیگری متد notify() یا notifyAll() را برای این شیء فراخوانی کند (در ادامه در مورد این روش ها صحبت خواهیم کرد).

  • Wait (مدت زمان طولانی) - رشته فعلی را در حالت انتظار قرار می دهد تا زمانی که رشته دیگری متد notify() یا notifyAll() را روی این شیء فراخوانی کند یا مدت زمان تعیین شده منقضی شود .

  • صبر کنید (تایم وقفه طولانی، نانوهای داخلی) - مشابه مورد قبلی، فقط نانو به شما امکان می دهد نانوثانیه ها را مشخص کنید (تنظیم زمان دقیق تر).

  • Notify() روشی است که به شما امکان می دهد یک رشته تصادفی از بلوک همگام سازی فعلی را بیدار کنید. باز هم، فقط می‌توان آن را در یک بلوک یا روش همگام‌سازی شده فراخوانی کرد (به هر حال، در جاهای دیگر کسی برای باز کردن انجماد نخواهد داشت).

  • NotifyAll() متدی است که تمام رشته های انتظار را در مانیتور فعلی بیدار می کند (همچنین فقط در یک بلوک یا روش همگام استفاده می شود ).

110. چگونه می توان جریان را متوقف کرد؟

اولین چیزی که باید بگوییم این است که وقتی متد ()run به طور کامل اجرا می شود ، رشته به طور خودکار از بین می رود. اما گاهی لازم است قبل از اتمام این روش او را زودتر از موعد مقرر بکشید. خب پس باید چیکار کنیم؟ شاید شی Thread باید متد stop() داشته باشد ؟ مهم نیست که چگونه است! این روش قدیمی در نظر گرفته می شود و می تواند منجر به خرابی سیستم شود. تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 6خب پس چی؟ دو راه برای انجام این کار وجود دارد: روش اول استفاده از پرچم بولین داخلی است. بیایید به یک مثال نگاه کنیم. ما پیاده سازی خودمان را از یک موضوع داریم که باید عبارت خاصی را روی صفحه نمایش دهد تا زمانی که کاملاً متوقف شود:
public class CustomThread extends Thread {
private boolean isActive;

   public CustomThread() {
       this.isActive = true;
   }

   @Override
   public void run() {
       {
           while (isActive) {
               System.out.println("Поток выполняет некую логику...");
           }
           System.out.println("Поток остановлен!");
       }
   }

   public void stopRunningThread() {
       isActive = false;
   }
}
هنگام استفاده از متد stopRunning() پرچم داخلی false می شود و متد run متوقف می شود. بیایید آن را به صورت اصلی اجرا کنیم :
System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный  CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
در نتیجه، چیزی شبیه به این را در کنسول خواهیم دید:
شروع اجرای برنامه Thread در حال اجرای برخی از منطق است... Thread در حال اجرای برخی از منطق ها... Thread در حال اجرای برخی از منطق ها... Thread در حال اجرای برخی از منطق ها... Thread در حال اجرای برخی از منطق ها... thread در حال اجرای برخی از منطق است... پایان اجرای برنامه موضوع متوقف شده است!
این بدان معناست که رشته ما کار کرد، تعداد مشخصی پیام را به کنسول خروجی داد و با موفقیت متوقف شد. توجه داشته باشم که تعداد پیام‌های خروجی از اجرا به اجرا متفاوت است؛ گاهی اوقات رشته اضافی حتی چیزی را خروجی نمی‌کند. همانطور که متوجه شدم، این بستگی به زمان خواب نخ اصلی دارد، هر چه طولانی تر باشد، احتمال کمتری دارد که نخ اضافی چیزی خروجی نداشته باشد. با زمان خواب 1 میلی‌ثانیه، پیام‌ها تقریباً هرگز خروجی نمی‌شوند، اما اگر آن را روی 20 میلی‌ثانیه تنظیم کنید، تقریباً همیشه کار می‌کند. شاید وقتی زمان کوتاه است، موضوع به سادگی زمان شروع و شروع کار خود را نداشته باشد و بلافاصله متوقف شود. راه دوم استفاده از متد interrupted() روی شی Thread است که مقدار پرچم وقفه داخلی را برمی‌گرداند (این پرچم به طور پیش‌فرض نادرست است ) و متد interrupt() دیگر آن ، که این پرچم را روی true قرار می‌دهد (وقتی این flag درست است موضوع باید کار خود را متوقف کند). بیایید به یک مثال نگاه کنیم:
public class CustomThread extends Thread {

   @Override
   public void run() {
       {
           while (!Thread.interrupted()) {
               System.out.println("Поток выполняет некую логику...");
           }
           System.out.println("Поток остановлен!");
       }
   }
}
اجرا به صورت اصلی :
System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
نتیجه اجرا مانند مورد اول خواهد بود، اما من این روش را بیشتر دوست دارم: کد کمتری می نویسیم و از عملکردهای آماده و استاندارد بیشتری استفاده می کنیم. تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 7اینجاست که ما امروز متوقف خواهیم شد.تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 12 - 8
سایر مواد این سری:
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION