สวัสดี! ความรู้คือพลัง. ยิ่งคุณมีความรู้มากขึ้นก่อนการสัมภาษณ์ครั้งแรก คุณก็จะรู้สึกมั่นใจมากขึ้นเท่านั้น
ด้วยความรู้ที่ดี คุณจะสับสนได้ยาก และในขณะเดียวกัน คุณก็สามารถทำให้ผู้สัมภาษณ์ของคุณประหลาดใจได้ ดังนั้น ในวันนี้ เราจะยังคงเสริมความแข็งแกร่งให้กับฐานทางทฤษฎีของคุณต่อไปโดยการตรวจสอบคำถามมากกว่า 250 ข้อสำหรับนักพัฒนา Java เพื่อไม่ให้เสียเวลา
แล้วไงล่ะ? มีสองวิธีในการทำเช่นนี้ วิธีแรกคือการใช้แฟล็กบูลีนภายในของคุณ ลองดูตัวอย่าง เรามีการใช้งานเธรดของเราเองที่ควรแสดงวลีบางอย่างบนหน้าจอจนกว่าจะหยุดสนิท:
นั่นคือสิ่งที่เราจะหยุดในวันนี้
103. มีหลักเกณฑ์ในการตรวจสอบข้อยกเว้นในการรับมรดกอย่างไร?
หากฉันเข้าใจคำถามถูกต้อง พวกเขาจะถามถึงกฎการทำงานโดยมีข้อยกเว้นในระหว่างการรับมรดก ดังนี้- วิธีการแทนที่หรือนำไปใช้ในการสืบทอด/การใช้งานไม่สามารถโยนข้อยกเว้นที่ถูกตรวจสอบซึ่งอยู่ในลำดับชั้นที่สูงกว่าข้อยกเว้นในวิธีซูเปอร์คลาส/อินเทอร์เฟซ
public interface Animal {
void voice() throws IOException;
}
ในการใช้งานอินเทอร์เฟซนี้ เราไม่สามารถส่งข้อยกเว้นที่ส่งทั่วไปมากกว่านี้ได้ (เช่นException , Throwable ) แต่เราสามารถแทนที่ด้วยข้อยกเว้นที่สืบทอดมา เช่นFileNotFoundException :
public class Cat implements Animal {
@Override
public void voice() throws FileNotFoundException {
// некоторая реализация
}
}
- ตัวสร้างคลาสย่อยต้องรวม คลาสข้อ ยกเว้นทั้งหมดที่ถูกโยนโดยตัวสร้างคลาสย่อยที่ถูกเรียกเมื่อมีการสร้างวัตถุในการบล็อกการพ่น
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 เดียวกัน กับที่เกิดขึ้นเมื่อหน่วยความจำสแต็กเต็ม)
- เมื่อ เธรด deamonผ่านry...ในที่สุดก็ บล็อก และขนานกับสิ่งนี้ โปรแกรมจะสิ้นสุด ท้ายที่สุดแล้ว เธรด deamon นั้นเป็นเธรดสำหรับการดำเนินการในเบื้องหลัง นั่นคือ มันไม่มีความสำคัญและจำเป็น และแอปพลิเคชันจะไม่รอให้งานเสร็จสิ้น
- การวนซ้ำอนันต์ที่พบบ่อยที่สุด ในtryหรือcatchครั้งหนึ่งซึ่งกระแสจะยังคงอยู่ตรงนั้นตลอดไป:
try { while (true) { } } finally { System.out.println("Данное сообщение не будет выведенно в консоль"); }

105. เขียนตัวอย่างการจัดการข้อยกเว้นหลายรายการใน catch block เดียว
1) บางทีคำถามอาจถูกถามไม่ถูกต้อง เท่าที่ฉันเข้าใจ คำถามนี้อ้างถึงการจับ หลายครั้ง สำหรับ try block หนึ่งครั้ง :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 block ดังนั้นcatch block จะสลับกันพยายามจับมันจากบนลงล่าง หากcatch บล็อกใด สำเร็จ บล็อกจะได้รับสิทธิ์ในการจัดการข้อยกเว้น ในขณะที่บล็อกที่เหลือด้านล่างจะไม่เป็นอีกต่อไป สามารถลองจับและแปรรูปตามวิถีของตนเองได้ ดังนั้นข้อยกเว้นที่แคบกว่าจะถูกวางไว้ให้สูงกว่าในcatch block chain และข้อยกเว้นที่กว้างกว่าจะถูกวางไว้ที่ต่ำกว่า ตัวอย่างเช่น หากในcatch block แรกของเรา มีข้อยกเว้นของ คลาส 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ซึ่งใช้ในการตรวจสอบว่าวัตถุอยู่ในประเภทใดประเภทหนึ่งหรือไม่ เพื่อที่ในภายหลังเราจะสามารถจำกัดให้แคบลงเหลือประเภทนี้โดยไม่มีผลกระทบด้านลบในภายหลัง วิธีที่พิจารณาทั้งสองวิธีสามารถใช้ในสถานการณ์เดียวกันได้ แต่ฉันบอกว่าคำถามนั้นไม่ถูกต้องเพราะฉันจะไม่เรียกตัวเลือกที่สองว่าดีและไม่เคยเห็นมันมาก่อนในทางปฏิบัติของฉัน ในขณะเดียวกันวิธีแรกที่มี multicatch ก็ได้รับความนิยมอย่างแพร่หลาย ความสนใจ. การแพร่กระจาย. 
106. โอเปอเรเตอร์ใดที่อนุญาตให้คุณบังคับให้โยนข้อยกเว้น? เขียนตัวอย่าง
ฉันได้ใช้มันหลายครั้งข้างต้น แต่อย่างไรก็ตามฉันจะทำซ้ำคำหลักนี้ - โยน . ตัวอย่างการใช้งาน (บังคับให้มีข้อยกเว้น):throw new NullPointerException();
107. วิธีการหลักสามารถส่งข้อยกเว้นการโยนได้หรือไม่? ถ้ามีจะโอนที่ไหนครับ?
ก่อนอื่น ฉันต้องการทราบว่าmainไม่มีอะไรมากไปกว่าวิธีการปกติ และใช่ เครื่องเสมือนถูกเรียกเพื่อเริ่มการทำงานของโปรแกรม แต่นอกเหนือจากนี้ ยังสามารถเรียกจากโค้ดอื่นได้ นั่นคือยังอยู่ภายใต้กฎปกติสำหรับการระบุข้อยกเว้นที่ถูกตรวจสอบหลังการโยน :public static void main(String[] args) throws IOException {
ดังนั้นอาจมีข้อยกเว้นเกิดขึ้นด้วย ถ้าmain ไม่ได้ถูกเรียกในบางวิธี แต่ถูกสตาร์ทเป็นจุด เริ่มโปรแกรม ดังนั้นข้อยกเว้นที่เกิดขึ้นจะถูกจัดการโดย interceptor .UncaughtExceptionHandler ตัวจัดการนี้เป็นหนึ่งตัวต่อเธรด (นั่นคือ หนึ่งตัวจัดการในแต่ละเธรด) หากจำเป็น คุณสามารถสร้างตัว จัดการ ของคุณเองและตั้งค่าโดยใช้ เมธอด setDefaultUncaughtExceptionHandlerที่เรียกบน อ็อบเจ็กต์ Thread
มัลติเธรด

108. คุณรู้เครื่องมืออะไรในการทำงานกับมัลติเธรด?
เครื่องมือพื้นฐาน/พื้นฐานสำหรับการใช้มัลติเธรดใน Java:- การซิงโครไนซ์เป็นกลไกในการปิด (บล็อก) วิธีการ/บล็อกเมื่อเธรดเข้ามาจากเธรดอื่น
- ความผันผวนเป็นกลไกในการรับรองการเข้าถึงตัวแปรโดยเธรดต่างๆ อย่างสม่ำเสมอ กล่าวคือ เมื่อมีตัวแก้ไขนี้อยู่บนตัวแปร การดำเนินการมอบหมายและการอ่านทั้งหมดจะต้องเป็นแบบอะตอมมิก กล่าวอีกนัยหนึ่ง เธรดจะไม่คัดลอกตัวแปรนี้ไปยังหน่วยความจำภายในเครื่องและทำการเปลี่ยนแปลง แต่จะเปลี่ยนค่าดั้งเดิม
- Runnableเป็นอินเทอร์เฟซที่สามารถนำไปใช้ได้ (โดยเฉพาะวิธีการรัน) ในบางคลาส:
public class CustomRunnable implements Runnable {
@Override
public void run() {
// некоторая логика
}
}
และเมื่อสร้างอ็อบเจ็กต์ของคลาสนี้แล้ว คุณสามารถเริ่มเธรดใหม่ได้โดยการตั้งค่าอ็อบเจ็กต์นี้ในตัวสร้างของ อ็อบเจ็กต์ Thread ใหม่ และเรียกใช้ เมธอด start() :
Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
เมธอด start รันเมธอดrun() ที่นำไปใช้งาน ในเธรดที่แยกจากกัน
- Threadเป็นคลาสที่สืบทอดมาจาก (ในขณะที่แทนที่ วิธี run ):
public class CustomThread extends Thread {
@Override
public void run() {
// некоторая логика
}
}
และโดยการสร้างอ็อบเจ็กต์ของคลาสนี้และเปิดใช้งานโดยใช้ เมธอด start()เราจะเปิดตัวเธรดใหม่:
new CustomThread().start();
- Concurrencyเป็นแพ็คเกจที่มีเครื่องมือสำหรับการทำงานในสภาพแวดล้อมแบบมัลติเธรด
- Concurrent Collections - ชุดคอลเลกชันเฉพาะสำหรับการทำงานในสภาพแวดล้อมแบบมัลติเธรด
- คิว - คิวพิเศษสำหรับสภาพแวดล้อมแบบมัลติเธรด (การบล็อกและการไม่บล็อก)
- Synchronisersเป็นโปรแกรมอรรถประโยชน์เฉพาะสำหรับการทำงานในสภาพแวดล้อมแบบมัลติเธรด
- ผู้ดำเนินการเป็นกลไกสำหรับการสร้างเธรดพูล
- ล็อค - กลไกการซิงโครไนซ์เธรด (ยืดหยุ่นกว่ากลไกมาตรฐาน - ซิงโครไนซ์, รอ, แจ้งเตือน, แจ้งเตือนทั้งหมด)
- Atomicsเป็นคลาสที่ปรับให้เหมาะสมสำหรับการประมวลผลแบบมัลติเธรด แต่ละการดำเนินการเป็นแบบอะตอมมิก
109. พูดคุยเกี่ยวกับการซิงโครไนซ์ระหว่างเธรด wait(), notify() - notifyAll() join() วิธีการใช้คืออะไร?
เท่าที่ฉันเข้าใจคำถาม การซิงโครไนซ์ระหว่างเธรดนั้นเกี่ยวกับตัวแก้ไขคีย์- synchronized ตัวแก้ไขนี้สามารถวางไว้ติดกับบล็อกได้โดยตรง:synchronized (Main.class) {
// некоторая логика
}
หรือโดยตรงในลายเซ็นวิธีการ:
public synchronized void move() {
// некоторая логика}
อย่างที่ผมได้กล่าวไว้ก่อนหน้านี้การซิงโครไนซ์เป็นกลไกที่ช่วยให้คุณสามารถปิดบล็อก/เมธอดจากเธรดอื่นเมื่อมีเธรดหนึ่งเข้ามาแล้ว คิดว่าบล็อก/วิธีการเป็นเหมือนห้อง กระแสบางกระแสมาถึงแล้วก็จะเข้าไปล็อค กระแสอื่นๆ มาถึงห้องแล้วเห็นว่าปิดก็รออยู่ใกล้ๆ จนเป็นอิสระ เมื่อทำธุรกิจเสร็จ เธรดแรกก็ออกจากห้องไปและปล่อยกุญแจ และไม่ใช่เพื่ออะไรเลยที่ฉันพูดถึงกุญแจอยู่ตลอดเวลาเพราะมันมีอยู่จริง นี่คือออบเจ็กต์พิเศษที่มีสถานะไม่ว่าง/ว่าง อ็อบเจ็กต์นี้แนบอยู่กับอ็อบเจ็กต์ Java ทุกตัว ดังนั้นเมื่อใช้ บล็อก ซิงโครไนซ์ เราจำเป็นต้องระบุอ็อบเจ็กต์ที่มี mutex ที่เราต้องการปิดประตูในวงเล็บ:
Cat cat = new Cat();
synchronized (cat) {
// некоторая логика
}
คุณยังสามารถใช้คลาส mutex ได้เหมือนกับที่ฉันทำในตัวอย่างแรก ( Main.class ) เวลาเราใช้synchronizedกับ method เราไม่ได้ระบุ object ที่ต้องการปิดใช่ไหม? ในกรณีนี้ สำหรับวิธีการที่ไม่คงที่ มันจะปิดบน mutex ของ อ็อบเจ็กต์ นี้ซึ่งก็คืออ็อบเจ็กต์ปัจจุบันของคลาสนี้ สแตติกจะปิดบน mutex ของคลาสปัจจุบัน ( this.getClass(); ) คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ mutex ได้ที่นี่ อ่านเกี่ยว กับ การซิงโครไนซ์ที่นี่ Wait()เป็นวิธีการที่ปล่อย mutex และทำให้เธรดปัจจุบันเข้าสู่โหมดสแตนด์บาย เหมือนกับว่าต่ออยู่กับมอนิเตอร์ปัจจุบัน (คล้ายกับจุดยึด) ด้วยเหตุนี้ วิธีนี้สามารถเรียกได้จาก บล็อกหรือวิธี การซิงโครไนซ์ เท่านั้น (ไม่เช่นนั้น วิธีนี้จะว่างอะไรและควรคาดหวังอะไร) โปรดทราบว่า นี่เป็นวิธีการของ คลาส Object แม่นยำยิ่งขึ้นไม่ใช่หนึ่งเดียว แต่มีถึงสาม:
-
Wait() - ทำให้เธรดปัจจุบันเข้าสู่โหมดรอจนกว่าเธรดอื่นจะเรียกใช้เมธอดnotify()หรือnotifyAll()สำหรับอ็อบเจ็กต์นี้ (เราจะพูดถึงวิธีการเหล่านี้ในภายหลัง)
-
รอ (หมดเวลานาน) - ทำให้เธรดปัจจุบันเข้าสู่โหมดรอจนกว่าเธรดอื่นจะเรียกใช้เมธอดnotify()หรือnotifyAll() บนวัตถุนี้ หรือการ หมดเวลา ที่ระบุ หมดอายุ
-
รอ (การหมดเวลานาน int nanos) - คล้ายกับครั้งก่อน มีเพียงnanos เท่านั้น ที่ให้คุณระบุนาโนวินาที (การตั้งค่าเวลาที่แม่นยำยิ่งขึ้น)
-
Notify()เป็นวิธีการที่ช่วยให้คุณสามารถปลุกเธรดแบบสุ่มของบล็อกการซิงโครไนซ์ปัจจุบันได้ อีกครั้งสามารถเรียกได้ใน บล็อกหรือวิธี การซิงโครไนซ์ เท่านั้น (ท้ายที่สุดในที่อื่นจะไม่มีใครยกเลิกการตรึง)
-
NotifyAll()เป็นวิธีการที่ปลุกเธรดที่รอทั้งหมดบนมอนิเตอร์ปัจจุบัน (ใช้เฉพาะใน บล็อกหรือวิธี การซิงโครไนซ์ เท่านั้น )
110. จะหยุดการไหลได้อย่างไร?
สิ่งแรกที่ต้องพูดคือเมื่อ ดำเนินการเมธอด run() อย่างสมบูรณ์ เธรดจะถูกทำลายโดยอัตโนมัติ แต่บางครั้งคุณจำเป็นต้องฆ่าเขาก่อนกำหนดก่อนที่วิธีนี้จะเสร็จสิ้น แล้วเราควรทำอย่างไร? บางที วัตถุThread ควรมี เมธอดstop()หรือไม่ ไม่ว่ายังไงก็ตาม! วิธีนี้ถือว่าล้าสมัยและอาจทำให้ระบบล่มได้
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()ค่าสถานะภายในจะกลายเป็นเท็จ และ วิธี การ runจะหยุดทำงาน มารันกันที่main :
System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
ด้วยเหตุนี้เราจะเห็นสิ่งนี้ในคอนโซล:
เริ่มต้นการทำงานของโปรแกรม เธรดกำลังดำเนินการตรรกะบางอย่าง... เธรดกำลังดำเนินการตรรกะบางอย่าง... เธรดกำลังดำเนินการตรรกะบางอย่าง... เธรดกำลังดำเนินการตรรกะบางอย่าง... เธรดกำลังดำเนินการตรรกะบางอย่าง... thread กำลังดำเนินการตรรกะบางอย่าง... สิ้นสุดการทำงานของโปรแกรม เธรดหยุดทำงาน !
ซึ่งหมายความว่าเธรดของเราใช้งานได้ ส่งข้อความจำนวนหนึ่งไปยังคอนโซลและหยุดได้สำเร็จ ฉันทราบว่าจำนวนข้อความที่ส่งออกจะแตกต่างกันไปในแต่ละรัน บางครั้งเธรดเพิ่มเติมไม่ได้ส่งออกอะไรเลยด้วยซ้ำ อย่างที่ฉันสังเกตเห็น สิ่งนี้ขึ้นอยู่กับเวลาพักของเธรดหลัก ยิ่งนานเท่าไร โอกาสที่เธรดเพิ่มเติมจะไม่เอาท์พุตก็จะน้อยลงเท่านั้น ด้วยเวลาพักเครื่องที่ 1 มิลลิวินาที ข้อความแทบจะไม่มีการส่งออกเลย แต่ถ้าคุณตั้งค่าเป็น 20 มิลลิวินาที ข้อความก็จะใช้งานได้เกือบทุกครั้ง บางที เมื่อเวลามีน้อย เธรดก็ไม่มีเวลาเริ่มต้นและเริ่มทำงาน และหยุดทันที วิธีที่สองคือการใช้เมธอด Interrupted() บนวัตถุ Thread ซึ่งส่งคืนค่าของแฟล็กขัดจังหวะภายใน (แฟล็กนี้ เป็นเท็จโดยค่าเริ่มต้น ) และเมธอด Interrupt()อื่น ๆซึ่งตั้งค่าแฟล็กนี้เป็นจริง (เมื่อ flag เป็นจริงเธรดควรหยุดทำงาน) ลองดูตัวอย่าง:
public class CustomThread extends Thread {
@Override
public void run() {
{
while (!Thread.interrupted()) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
}
รันในmain :
System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
ผลลัพธ์ของการดำเนินการจะเหมือนกับในกรณีแรก แต่ฉันชอบวิธีนี้มากกว่า: เราเขียนโค้ดน้อยลงและใช้ฟังก์ชันมาตรฐานสำเร็จรูปมากขึ้น 

GO TO FULL VERSION