JavaRush /จาวาบล็อก /Random-TH /การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java ตอนท...
Константин
ระดับ

การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java ตอนที่ 7

เผยแพร่ในกลุ่ม
เฮ้ทุกคน! การเขียนโปรแกรมเต็มไปด้วยข้อผิดพลาด และไม่มีหัวข้อใดที่คุณจะไม่สะดุดและกระแทก โดยเฉพาะอย่างยิ่งสำหรับผู้เริ่มต้น วิธีเดียวที่จะลดสิ่งนี้ได้คือการศึกษา โดยเฉพาะอย่างยิ่งสิ่งนี้ใช้กับการวิเคราะห์โดยละเอียดของหัวข้อพื้นฐานที่สุด วันนี้ฉันยังคงวิเคราะห์คำถามมากกว่า 250 ข้อจากการสัมภาษณ์นักพัฒนา Java ซึ่งครอบคลุมหัวข้อพื้นฐานได้ดี ฉันอยากจะทราบว่ารายการนี้มีคำถามที่ไม่เป็นมาตรฐานซึ่งทำให้คุณดูหัวข้อทั่วไปจากมุมที่ต่างออกไปได้การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java  ตอนที่ 7 - 1

62. String Pool คืออะไร และเหตุใดจึงจำเป็น?

ในหน่วยความจำใน Java (ฮีปซึ่งเราจะพูดถึงในภายหลัง) มีพื้นที่ - สตริงพูลหรือพูลสตริง ได้รับการออกแบบมาเพื่อเก็บค่าสตริง กล่าวอีกนัยหนึ่ง เมื่อคุณสร้างสตริงบางอย่าง เช่น ผ่านเครื่องหมายคำพูดคู่:
String str = "Hello world";
มีการตรวจสอบเพื่อดูว่าพูลสตริงมีค่าที่กำหนดหรือไม่ หากเป็นเช่นนั้น ตัวแปร strจะได้รับการกำหนดให้อ้างอิงกับค่านั้นในพูล หากไม่เป็นเช่นนั้น ค่า ใหม่จะถูกสร้างขึ้นในพูล และการอ้างอิงจะถูกกำหนดให้กับ ตัวแปร str ลองดูตัวอย่าง:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
จริง จะปรากฏบนหน้าจอ เราจำได้ว่า==เปรียบเทียบการอ้างอิง ซึ่งหมายความว่าการอ้างอิงทั้งสองนี้อ้างอิงถึงค่าเดียวกันจากพูลสตริง สิ่งนี้ทำเพื่อไม่ให้สร้างอ็อบเจ็กต์ ประเภท String ที่เหมือนกันจำนวนมาก ในหน่วยความจำ เนื่องจากอย่างที่เราจำได้Stringเป็นคลาสที่ไม่เปลี่ยนรูป และถ้าเรามีการอ้างอิงจำนวนมากไปยังค่าเดียวกัน ก็ไม่มีอะไรผิดปกติกับสิ่งนั้น เป็นไปไม่ได้อีกต่อไปสำหรับสถานการณ์ที่การเปลี่ยนแปลงค่าในที่เดียวทำให้เกิดการเปลี่ยนแปลงสำหรับลิงก์อื่นๆ หลายลิงก์พร้อมกัน แต่อย่างไรก็ตาม ถ้าเราสร้างสตริงโดยใช้new :
String str = new String("Hello world");
ออบเจ็กต์แยกต่างหากจะถูกสร้างขึ้นในหน่วยความจำที่จะเก็บค่าสตริงนี้ (และไม่สำคัญว่าเรามีค่าดังกล่าวอยู่ในพูลสตริงแล้วหรือไม่) เป็นการยืนยัน:
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
เราจะได้ค่าเท็จ สองค่า ซึ่งหมายความว่าเรามีค่าที่แตกต่างกันสามค่าที่กำลังถูกอ้างอิง จริงๆ แล้ว นี่คือสาเหตุที่แนะนำให้สร้างสตริงโดยใช้เครื่องหมายคำพูดคู่ อย่างไรก็ตาม คุณสามารถเพิ่ม (หรือรับการอ้างอิง) ค่าลงในพูลสตริงได้เมื่อสร้างออบเจ็กต์โดยใช้ไฟล์ . ในการทำสิ่งนี้ เราใช้เมธอดคลาสสตริง - intern( ) เมธอดนี้บังคับให้สร้างค่าในสตริงพูล หรือรับลิงก์ไปยังค่านั้นหากเก็บไว้ที่นั่นแล้ว นี่คือตัวอย่าง:
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
เป็นผลให้เราจะได้รับค่า จริง สาม ค่าในคอนโซลซึ่งหมายความว่าตัวแปรทั้งสามอ้างอิงถึงสตริงเดียวกันการวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java  ตอนที่ 7 - 2

63. รูปแบบ GOF ใดที่ใช้ในสตริงพูล?

รูปแบบGOFมองเห็นได้ชัดเจนในสตริงพูล - รุ่นฟลายเวทหรือที่เรียกว่าไม้ตาย หากคุณเห็นเทมเพลตอื่นที่นี่ แบ่งปันในความคิดเห็น เรามาพูดถึงเทมเพลตน้ำหนักเบากันดีกว่า Lightweight คือรูปแบบการออกแบบโครงสร้างซึ่งวัตถุที่แสดงตัวเองเป็นตัวอย่างเฉพาะในตำแหน่งต่างๆ ในโปรแกรมนั้น แท้จริงแล้วไม่เป็นเช่นนั้น Lightweight ช่วยประหยัดหน่วยความจำด้วยการแชร์สถานะที่ใช้ร่วมกันของอ็อบเจ็กต์ระหว่างกัน แทนที่จะจัดเก็บข้อมูลเดียวกันในแต่ละอ็อบเจ็กต์ เพื่อให้เข้าใจถึงสาระสำคัญ มาดูตัวอย่างที่ง่ายที่สุดกัน สมมติว่าเรามีอินเทอร์เฟซสำหรับพนักงาน:
public interface Employee {
   void work();
}
และมีการใช้งานบางอย่าง เช่น ทนายความ:
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("Юрист взят в штат.");
   }

   @Override
   public void work() {
       System.out.println("Решение юридических вопросов...");
   }
}
และนักบัญชี:
public class Accountant implements Employee{

   public Accountant() {
       System.out.println("Бухгалтер взят в штат.");
   }

   @Override
   public void work() {
       System.out.println("Ведение бухгалтерского отчёта....");
   }
}
วิธีการนั้นมีเงื่อนไขมาก: เราแค่ต้องดูว่าได้ดำเนินการไปแล้ว สถานการณ์เดียวกันนี้ใช้กับตัวสร้าง ต้องขอบคุณคอนโซลเอาต์พุต เราจะเห็นเมื่อมีการสร้างออบเจ็กต์ใหม่ นอกจากนี้เรายังมีแผนกพนักงานซึ่งมีหน้าที่ในการออกพนักงานที่ได้รับการร้องขอ และหากเขาไม่อยู่ที่นั่น ให้จ้างเขาและออกตามคำขอ:
public class StaffDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee receiveEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Бухгалтер":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Юрист":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("Данный сотрудник в штате не предусмотрен!");
           }
       }
       return result;
   }
}
นั่นคือตรรกะนั้นง่าย: หากมีหน่วยใดให้ส่งคืน หากไม่มี ให้สร้างมันขึ้นมา วางไว้ในที่เก็บข้อมูล (เช่นแคช) แล้วส่งคืน ตอนนี้เรามาดูกันว่ามันทำงานอย่างไร:
public static void main(String[] args) throws Exception {
   StaffDepartment staffDepartment = new StaffDepartment();
   Employee empl1  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl2  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl3  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl4  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl5  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl6  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl7  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl8  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl9  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl10  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
}
และในคอนโซลจะมีเอาต์พุตดังนี้:
ทนายความได้รับการว่าจ้าง แก้ไขปัญหากฎหมาย...รับสมัครนักบัญชี ดูแลรักษารายงานทางบัญชี.... แก้ไขปัญหาทางกฎหมาย... ดูแลรักษารายงานทางบัญชี.... แก้ไขปัญหาทางกฎหมาย... ดูแลรักษารายงานทางบัญชี.... แก้ไขปัญหาทางกฎหมาย... ดูแลรักษารายงานทางบัญชี.... แก้ไขปัญหาทางกฎหมาย .. ดูแลรายงานทางบัญชี...
อย่างที่คุณเห็น มีเพียงสองวัตถุเท่านั้นที่ถูกสร้างขึ้น ซึ่งถูกนำมาใช้ซ้ำหลายครั้ง ตัวอย่างนี้ง่ายมาก แต่แสดงให้เห็นอย่างชัดเจนว่าการใช้เทมเพลตนี้สามารถช่วยประหยัดทรัพยากรของเราได้อย่างไร อย่างที่คุณสังเกตเห็น ตรรกะของรูปแบบนี้คล้ายกับตรรกะของกลุ่มประกันมาก คุณสามารถอ่านเพิ่มเติมเกี่ยวกับประเภทของ รูปแบบ GOF ได้ในบทความนี้การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java  ตอนที่ 7 - 3

64. จะแยกสตริงออกเป็นส่วน ๆ ได้อย่างไร? โปรดระบุตัวอย่างของรหัสที่เกี่ยวข้อง

แน่นอนว่าคำถาม นี้เกี่ยวกับsplit method คลาสStringมีเมธอดนี้สองรูปแบบ:
String split(String regex);
และ
String split(String regex);
regexเป็นตัวคั่นบรรทัด - นิพจน์ทั่วไปบางตัวที่แบ่งสตริงออกเป็นอาร์เรย์ของสตริง ตัวอย่างเช่น:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
สิ่งต่อไปนี้จะถูกส่งออกไปยังคอนโซล:
สวัสดีชาวโลก ฉันคือ Amigo!
นั่นคือ ค่าสตริงของเราถูกแบ่งออกเป็นอาร์เรย์ของสตริง และตัวคั่นคือช่องว่าง (สำหรับการแยก เราสามารถใช้นิพจน์ทั่วไปที่ไม่ใช่ช่องว่าง"\\s"และเพียงนิพจน์สตริง" " ) วิธีที่ สอง ที่โอเวอร์โหลดมีอาร์กิวเมนต์เพิ่มเติม - ขีดจำกัด ขีด จำกัด - ค่าสูงสุดที่อนุญาตของอาร์เรย์ผลลัพธ์ นั่นคือ เมื่อสตริงถูกแบ่งออกเป็นจำนวนสตริงย่อยสูงสุดที่อนุญาตแล้ว จะไม่มีการแยกอีกต่อไป และองค์ประกอบสุดท้ายจะมี "ส่วนที่เหลือ" ของสตริงที่อาจแบ่งต่ำกว่านั้น ตัวอย่าง:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
เอาต์พุตคอนโซล:
สวัสดีชาวโลก ฉันคือ Amigo!
ดังที่เราเห็น ถ้าไม่ใช่เพราะข้อจำกัด = 2องค์ประกอบสุดท้ายของอาร์เรย์สามารถแบ่งออกเป็นสามสตริงย่อยได้การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java  ตอนที่ 7 - 4

65. เหตุใดอาร์เรย์อักขระจึงดีกว่าสตริงสำหรับจัดเก็บรหัสผ่าน

มีเหตุผลหลายประการที่ต้องการใช้อาร์เรย์มากกว่าสตริงเมื่อจัดเก็บรหัสผ่าน: 1. สตริงพูลและความไม่เปลี่ยนรูปของสตริง เมื่อใช้อาร์เรย์ ( char[] ) เราสามารถลบข้อมูลได้อย่างชัดเจนหลังจากที่เราทำเสร็จแล้ว นอกจากนี้เรายังสามารถเขียนอาร์เรย์ใหม่ได้มากเท่าที่เราต้องการ และรหัสผ่านที่ถูกต้องจะไม่อยู่ที่ใดเลยในระบบ แม้กระทั่งก่อนการรวบรวมขยะ (ก็เพียงพอแล้วที่จะเปลี่ยนสองสามเซลล์ให้เป็นค่าที่ไม่ถูกต้อง) ในขณะเดียวกันStringก็เป็นคลาสที่ไม่เปลี่ยนรูป นั่นคือหากเราต้องการเปลี่ยนค่าของมัน เราก็จะได้ค่าใหม่ ในขณะที่ค่าเก่าจะยังคงอยู่ใน String Pool หากเราต้องการลบ ค่า Stringของรหัสผ่าน นี่อาจเป็นงานที่ยากมาก เนื่องจากเราต้องการตัวรวบรวมขยะเพื่อลบค่าออกจาก String poolและมีความเป็นไปได้สูงที่ ค่า String นี้ จะยังคงอยู่ตรงนั้นเป็นเวลา เวลานาน. นั่นคือในสถานการณ์นี้String ด้อยกว่า อาร์เรย์ถ่านในแง่ของความปลอดภัยในการจัดเก็บข้อมูล 2. หากค่า สตริงถูกส่งไปยังคอนโซล (หรือบันทึก) โดยไม่ได้ตั้งใจ ค่านั้นจะปรากฏขึ้นเอง:
String password = "password";
System.out.println("Пароль - " + password);
เอาต์พุตคอนโซล:
รหัสผ่าน
ในเวลาเดียวกัน หากคุณส่งออกอาร์เรย์ไปยังคอนโซลโดยไม่ได้ตั้งใจ:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Пароль - " + arr);
จะมี gobbledygook ที่เข้าใจยากในคอนโซล:
รหัสผ่าน - [C@7f31245a
จริงๆแล้วไม่ใช่ gobbledygook แต่: [Cคือชื่อคลาสคืออาร์เรย์ ถ่าน@เป็นตัวคั่นตามด้วย 7f31245aเป็นรหัสแฮชเลขฐานสิบหก 3. เอกสารอย่างเป็นทางการของ Java Cryptography Architecture Guide ระบุอย่างชัดเจนถึงการจัดเก็บรหัสผ่านในchar[]แทนString : “ดูเหมือนว่าจะสมเหตุสมผลที่จะรวบรวมและจัดเก็บรหัสผ่านในอ็อบเจ็กต์ประเภทjava.lang.String อย่างไรก็ตาม มีข้อแม้อยู่ที่นี่: อ็อบเจ็กต์ Stringนั้นไม่เปลี่ยนรูป กล่าวคือ ไม่มีวิธีการที่กำหนดไว้เพื่ออนุญาตให้แก้ไขเนื้อหาของ อ็อบเจ็กต์ String (เขียนทับ) หรือทำให้เป็นโมฆะ หลังการใช้งาน คุณลักษณะนี้ทำให้ ออบเจ็กต์ Stringไม่เหมาะสมสำหรับการจัดเก็บข้อมูลละเอียดอ่อน เช่น รหัสผ่านผู้ใช้ คุณควรรวบรวมและจัดเก็บข้อมูลความปลอดภัยที่ละเอียดอ่อนไว้ในอาเรย์อักขระแทนเสมอ”การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java  ตอนที่ 7 - 5

เอนัม

66. ให้คำอธิบายสั้น ๆ เกี่ยวกับ Enum ในภาษา Java

Enumคือการแจงนับ ซึ่งเป็นชุดของค่าคงที่สตริงที่รวมกันเป็นประเภททั่วไป ประกาศผ่านคำหลัก- enum นี่คือตัวอย่างที่มีenum - บทบาทที่ถูกต้องในโรงเรียนบางแห่ง:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
คำที่เขียนด้วยตัวพิมพ์ใหญ่เป็นค่าคงที่การแจกแจงแบบเดียวกับที่ประกาศด้วยวิธีที่เรียบง่าย โดยไม่ต้องใช้ตัว ดำเนินการ ใหม่ การใช้การแจงนับทำให้ชีวิตง่ายขึ้นอย่างมาก เนื่องจากช่วยหลีกเลี่ยงข้อผิดพลาดและความสับสนในการตั้งชื่อ (เนื่องจากอาจมีรายการค่าได้เพียงบางรายการเท่านั้น) โดยส่วนตัวแล้ว ฉันคิดว่ามันสะดวกมากเมื่อใช้ในการออกแบบลอจิกของSwitch

67. Enum สามารถใช้อินเทอร์เฟซได้หรือไม่

ใช่. ท้ายที่สุดแล้ว การแจงนับต้องเป็นตัวแทนมากกว่าคอลเลกชันแบบพาสซีฟ (เช่น บทบาท) ใน Java พวกเขาสามารถแสดงวัตถุที่ซับซ้อนมากขึ้นด้วยฟังก์ชันการทำงานบางอย่าง ดังนั้นคุณอาจต้องเพิ่มฟังก์ชันการทำงานเพิ่มเติมให้กับวัตถุเหล่านั้น สิ่งนี้ยังช่วยให้คุณใช้ความสามารถของความหลากหลายได้โดยการแทนที่ ค่าแจง นับในตำแหน่งที่จำเป็นต้องใช้ประเภทของอินเทอร์เฟซที่นำไปใช้

68. Enum สามารถขยายคลาสได้หรือไม่?

ไม่ เป็นไปไม่ได้ เนื่องจาก enum เป็นคลาสย่อยเริ่มต้นของคลาสทั่วไปEnum <T>โดยที่Tแสดงถึงประเภท enum ทั่วไป มันไม่มีอะไรเลยนอกจากคลาสพื้นฐานทั่วไปสำหรับภาษา Java ทุกประเภท การแปลงenumเป็นคลาสทำได้โดยคอมไพลเลอร์ Java ณ เวลาคอมไพล์ ส่วนขยายนี้ไม่ได้ระบุไว้อย่างชัดเจนในโค้ด แต่จะมองเห็นได้เสมอ

69. เป็นไปได้ไหมที่จะสร้าง Enum โดยไม่มีอินสแตนซ์ของวัตถุ?

สำหรับฉันคำถามนั้นแปลกนิดหน่อยหรือฉันไม่เข้าใจทั้งหมด ฉันมีการตีความสองแบบ: 1. สามารถมีenumที่ไม่มีค่าได้หรือไม่ - ใช่แน่นอนว่ามันจะเป็นคลาสที่ว่างเปล่า - ไม่มีความหมาย:
public enum Role {
}
และโทร:
var s = Role.values();
System.out.println(s);
เราจะได้รับในคอนโซล:
[Lflyweight.Role;@9f70c54
(อาร์เรย์ว่างของ ค่า บทบาท ) 2. เป็นไปได้หรือไม่ที่จะสร้างแจงนับโดยไม่มี ตัวดำเนินการ ใหม่ - ใช่ แน่นอน ดังที่ได้กล่าวไปแล้วข้างต้น คุณไม่จำเป็นต้องใช้ ตัวดำเนินการ ใหม่ สำหรับค่าแจงนับ (การแจงนับ) เนื่องจากค่าเหล่านี้เป็นค่าคงที่

70. เราสามารถแทนที่เมธอด toString() สำหรับ Enum ได้หรือไม่

ได้ แน่นอน คุณสามารถแทนที่เมธอดtoString()เพื่อกำหนดวิธีเฉพาะในการแสดงenum ของคุณ เมื่อเรียกใช้เมธอดtoString (เมื่อแปลenumให้เป็นสตริงปกติ เช่น สำหรับเอาต์พุตไปยังคอนโซลหรือบันทึก)
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Выбрана роль - " + super.toString();
   }
}
นั่นคือทั้งหมดสำหรับวันนี้จนกว่าจะถึงตอนต่อไป!การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java  ตอนที่ 7 - 6
วัสดุอื่นๆ ในชุด:
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION