CodeGym /จาวาบล็อก /สุ่ม /สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Dev...
John Squirrels
ระดับ
San Francisco

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

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

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

ส่วนหนึ่งของหน่วยความจำที่โปรแกรม Java ใช้งานได้เรียกว่าฮีป (ซึ่งเราจะพูดถึงในภายหลัง) และส่วน หนึ่งของฮีปเรียกว่า String Pool ใช้สำหรับจัดเก็บค่าสตริง กล่าวอีกนัยหนึ่ง เมื่อคุณสร้างสตริง เช่น การใช้เครื่องหมายคำพูดคู่เช่นนี้
String str = "Hello world";
JVM ตรวจสอบว่าพูลสตริงมีค่าที่ระบุอยู่แล้วหรือไม่ หากเป็นเช่นนั้น ตัวแปร 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);
เราจะได้ข้อความสองบรรทัดที่ระบุว่าเป็น falseซึ่งหมายความว่าเรามีสตริงแยกกันสามบรรทัด โดยพื้นฐานแล้ว นี่คือเหตุผลที่คุณควรสร้างสตริงโดยใช้เครื่องหมายคำพูดคู่ ดังที่กล่าวไปแล้ว คุณสามารถเพิ่ม (หรือรับการอ้างอิง) ค่าในพูลสตริงได้ แม้ว่าจะสร้างออบเจ็กต์โดยใช้คีย์เวิร์ดใหม่ก็ตาม ในการทำสิ่งนี้ เราใช้ เมธอดintern()ของคลาส String วิธีการนี้ช่วยให้แน่ใจว่าเราสร้างค่าใน string pool หรือเราได้รับการอ้างอิงถึงค่านั้นหากมีอยู่แล้ว นี่คือตัวอย่าง:
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);
รหัสนี้แสดงผลจริงไปยังคอนโซลสามครั้ง ซึ่งบอกเราว่าตัวแปรทั้งสามอ้างอิงถึงสตริงเดียวกันในหน่วยความจำ

63. รูปแบบการออกแบบ GoF ใดที่ใช้ใน string pool?

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

   public Lawyer() {
       System.out.println("A lawyer was hired.");
   }

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
และ ชั้นเรียน นักบัญชี :
public class Accountant implements Employee {

   public Accountant() {
       System.out.println("An accountant was hired.");
   }

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
วิธีการต่างๆ เป็นไปตามอำเภอใจ — สำหรับตัวอย่างนี้ เราเพียงแค่ต้องดูว่าวิธีการเหล่านั้นกำลังถูกดำเนินการ เช่นเดียวกับตัวสร้าง เอาต์พุตคอนโซลจะบอกเราเมื่อมีการสร้างออบเจ็กต์ใหม่ นอกจากนี้เรายังมีแผนกทรัพยากรบุคคลซึ่งมีหน้าที่ส่งคืนพนักงานที่ได้รับการร้องขอ หากพนักงานคนนั้นไม่ได้อยู่ในพนักงานอยู่แล้ว พวกเขาจะได้รับการว่าจ้างและแผนกทรัพยากรบุคคลจะส่งคืนพนักงานใหม่:
public class HumanResourcesDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee getEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Accountant":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Lawyer":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("This employee is not on the staff!");
           }
       }
       return result;
   }
}
ดังนั้นตรรกะจึงง่าย: หากมีวัตถุที่ต้องการอยู่ ให้ส่งคืน ถ้าไม่เช่นนั้น ให้สร้างมันขึ้นมา เก็บไว้ในที่เก็บข้อมูล (เช่น แคช) แล้วส่งคืน ตอนนี้เรามาดูกันว่ามันทำงานอย่างไร:
public static void main(String[] args) throws Exception {
   HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
   Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
}
นี่คือสิ่งที่เราจะเห็นในคอนโซล:
มีการจ้างทนายความ แก้ปัญหาทางกฎหมาย...จ้างนักบัญชี เก็บบันทึกทางบัญชี... ชำระประเด็นทางกฎหมาย... เก็บบันทึกทางบัญชี... บันทึกปัญหาทางกฎหมาย... เก็บบันทึกการบัญชี... บันทึก...
อย่างที่คุณเห็น เราสร้างวัตถุเพียงสองชิ้นและนำมาใช้ซ้ำหลายครั้ง ตัวอย่างนี้ง่ายมาก แต่แสดงให้เห็นว่ารูปแบบการออกแบบนี้สามารถอนุรักษ์ทรัพยากรของเราได้อย่างไร ดังที่คุณอาจสังเกตเห็นแล้วว่า ตรรกะของรูปแบบนี้มีความคล้ายคลึงกับตรรกะของกลุ่มประกันอย่างมาก สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 7 - 2

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

แน่นอนว่าคำถามนี้เกี่ยวกับวิธีแยก คลาส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!
ดังที่เราเห็น หากไม่ใช่สำหรับLimit = 2องค์ประกอบสุดท้ายของอาร์เรย์สามารถแบ่งออกเป็นสามสตริงย่อยได้

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

มีสาเหตุหลายประการในการเลือกอาร์เรย์มากกว่าสตริงเมื่อจัดเก็บรหัสผ่าน:

1. พูลสตริงและความไม่เปลี่ยนรูปของสตริง

เมื่อใช้อาร์เรย์ ( char[] ) เราสามารถลบข้อมูลได้อย่างชัดเจนเมื่อเราทำงานกับมันเสร็จแล้ว นอกจากนี้เรายังสามารถเขียนทับอาร์เรย์ได้มากเท่าที่เราต้องการ โดยกำจัดรหัสผ่านออกจากระบบก่อนที่จะรวบรวมขยะ (ก็เพียงพอแล้วที่จะเปลี่ยนสองสามเซลล์ให้เป็นค่าที่ไม่ถูกต้อง) ในทางตรงกันข้ามStringเป็นคลาสที่ไม่เปลี่ยนรูป ซึ่งหมายความว่าหากเราต้องการเปลี่ยนค่าของ ออบเจ็กต์ Stringเราจะได้อันใหม่ แต่อันเก่าจะยังคงอยู่ในพูลสตริง หากเราต้องการลบสตริงที่มีรหัสผ่าน เราต้องเผชิญกับงานที่ซับซ้อนเนื่องจากเราต้องการตัวรวบรวมขยะเพื่อลบค่านั้นออกจากพูลสตริง แต่สตริง นั้น มีแนวโน้มที่จะยังคงอยู่ตรงนั้นเป็นเวลานาน นั่นคือเมื่อพูดถึงการจัดเก็บข้อมูลอย่างปลอดภัยStringนั้นด้อยกว่าอาร์เรย์ ถ่าน

2. หากเราส่งออก ค่า Stringไปยังคอนโซล (หรือบันทึก) เราจะได้รับ:

String password = "password";
System.out.println("Password - " + password);
เอาต์พุตคอนโซล:
รหัสผ่าน - รหัสผ่าน
และหากคุณพิมพ์อาร์เรย์ไปยังคอนโซล:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
คอนโซลจะแสดงคำพูดที่ไม่ชัดเจนที่เข้าใจไม่ได้:
รหัสผ่าน - [C@7f31245a
ในความเป็นจริงมันไม่ได้พูดพล่อยๆ ต่อไปนี้เป็นวิธีทำความเข้าใจสิ่งที่คุณเห็น: [Cคือชื่อคลาส - char array, @เป็นตัวคั่น จากนั้น 7f31245aคือรหัสแฮชเลขฐานสิบหก

3. คู่มืออ้างอิง Java Cryptography Architecture (JCA) อย่างเป็นทางการระบุอย่างชัดเจนถึงการจัดเก็บรหัสผ่านในchar[]แทนที่จะเป็นString :

"ดูเหมือนว่าจะมีเหตุผลในการรวบรวมและจัดเก็บรหัสผ่านในออบเจ็กต์ประเภทjava.lang.Stringอย่างไรก็ตาม นี่คือข้อแม้: ออบเจ็กต์ประเภทStringนั้นไม่เปลี่ยนรูป กล่าวคือ ไม่มีวิธีการที่กำหนดไว้ที่อนุญาตให้คุณเปลี่ยนแปลง (เขียนทับ) หรือทำให้เนื้อหาของStringเป็นศูนย์หลังการใช้งาน คุณสมบัตินี้ทำให้ ออบเจ็กต์ Stringไม่เหมาะสำหรับการจัดเก็บข้อมูลที่ละเอียดอ่อนด้านความปลอดภัย เช่น รหัสผ่านผู้ใช้ คุณควรรวบรวมและจัดเก็บข้อมูลที่ละเอียดอ่อนด้านความปลอดภัยในอาร์เรย์ถ่านแทนเสมอ" สำรวจคำถามและคำตอบจากการสัมภาษณ์งานสำหรับตำแหน่ง Java Developer  ตอนที่ 7 - 3

เอนัม

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

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

67. Enum สามารถใช้อินเทอร์เฟซได้ (ใช้คีย์เวิร์ด Implements) ได้หรือไม่

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

68. Enum สามารถขยายคลาสได้หรือไม่ (ใช้คีย์เวิร์ดขยาย)

ไม่ ไม่สามารถ เนื่องจาก enum เป็นคลาสย่อยของคลาสEnum<T> เริ่มต้น โดยที่Tคือประเภท enum นี่ไม่มีอะไรมากไปกว่าคลาสพื้นฐานทั่วไปสำหรับทุกประเภท 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 "Selected role - " + super.toString();
   }
}
นั่นคือทั้งหมดสำหรับฉันในวันนี้ จนกว่าจะถึงตอนต่อไป!
อ่านเพิ่มเติม:
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION