CodeGym/Blog Java/Ngẫu nhiên/Khám phá các câu hỏi và câu trả lời từ cuộc phỏng vấn xin...
John Squirrels
Mức độ
San Francisco

Khám phá các câu hỏi và câu trả lời từ cuộc phỏng vấn xin việc cho vị trí nhà phát triển Java. Phần 7

Xuất bản trong nhóm
Nè mọi người! Lập trình đầy cạm bẫy. Và hiếm có một chủ đề nào không khiến bạn vấp ngã, vấp chân. Điều này đặc biệt đúng đối với người mới bắt đầu. Cách duy nhất để cứu những ngón chân của bạn là học hỏi. Đặc biệt, bạn cần đi sâu vào những chủ đề cơ bản nhất. Hôm nay, chúng ta sẽ tiếp tục xem xét các câu hỏi phổ biến nhất trong các cuộc phỏng vấn dành cho nhà phát triển Java. Những câu hỏi phỏng vấn này làm rất tốt việc đề cập đến các chủ đề cơ bản. Lưu ý rằng danh sách này cũng bao gồm một số câu hỏi không chuẩn cho phép bạn tiếp cận các vấn đề phổ biến theo cách khác. Khám phá các câu hỏi và câu trả lời từ cuộc phỏng vấn xin việc cho vị trí nhà phát triển Java.  Phần 7 - 1

62. String pool là gì và tại sao lại cần đến nó?

Một phần bộ nhớ được cung cấp cho chương trình Java được gọi là vùng heap (mà chúng ta sẽ nói đến sau) và một phần của vùng heap được gọi là String pool . Nó dùng để lưu trữ các giá trị chuỗi. Nói cách khác, ví dụ: khi bạn tạo một chuỗi, hãy sử dụng dấu ngoặc kép như thế này:
String str = "Hello world";
JVM kiểm tra xem nhóm chuỗi đã có giá trị được chỉ định chưa. Nếu đúng như vậy thì biến str sẽ được gán tham chiếu đến giá trị đó trong nhóm. Nếu không, một giá trị mới sẽ được tạo trong nhóm và tham chiếu đến giá trị đó sẽ được gán cho biến str . Hãy xem xét một ví dụ:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
true sẽ được hiển thị trên màn hình. Hãy nhớ rằng == so sánh các tham chiếu và hai biến này trỏ đến cùng một giá trị trong nhóm chuỗi. Điều này giúp tránh tạo ra nhiều đối tượng String giống hệt nhau trong bộ nhớ. Chúng ta có thể làm điều này bởi vì, như bạn sẽ nhớ lại, String là một lớp bất biến, do đó không có gì sai khi có nhiều tham chiếu đến cùng một giá trị. Bây giờ không thể xảy ra trường hợp việc thay đổi giá trị ở một nơi dẫn đến thay đổi một số tham chiếu khác. Tuy nhiên, nếu chúng ta tạo một chuỗi bằng cách sử dụng new :
String str = new String("Hello world");
sau đó một đối tượng riêng biệt được tạo trong bộ nhớ và lưu trữ giá trị chuỗi đã chỉ định (không thành vấn đề nếu giá trị đó đã có trong nhóm chuỗi). Để xác nhận khẳng định này, hãy xem xét điều này:
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);
Chúng ta sẽ nhận được hai dòng chỉ sai , nghĩa là chúng ta có ba chuỗi riêng biệt. Về cơ bản, đây là lý do tại sao bạn nên tạo chuỗi chỉ bằng cách sử dụng dấu ngoặc kép. Điều đó nói lên rằng, có thể thêm (hoặc lấy tham chiếu đến) các giá trị trong nhóm chuỗi ngay cả khi tạo một đối tượng bằng từ khóa mới . Để làm điều này, chúng ta sử dụng phương thức intern() của lớp String . Phương thức này đảm bảo rằng chúng ta tạo giá trị trong nhóm chuỗi hoặc chúng ta nhận được tham chiếu đến nó nếu nó đã có ở đó. Đây là một ví dụ:
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);
Mã này xuất ra bảng điều khiển đúng ba lần, điều này cho chúng ta biết rằng cả ba biến đều tham chiếu đến cùng một chuỗi trong bộ nhớ.

63. Những mẫu thiết kế GoF nào được sử dụng trong nhóm chuỗi?

Trong nhóm chuỗi, mẫu thiết kế GoF là mẫu flyweight . Nếu bạn nhận thấy một mẫu thiết kế khác ở đây, hãy chia sẻ nó trong phần bình luận. Ở đây chúng ta sẽ nói về mẫu thiết kế flyweight. Nó là một mẫu thiết kế cấu trúc trong đó một đối tượng thể hiện chính nó như một thể hiện duy nhất ở những vị trí khác nhau trong chương trình thực tế không phải là duy nhất. Flyweight tiết kiệm bộ nhớ bằng cách lưu trữ trạng thái chia sẻ của các đối tượng thay vì lưu trữ dữ liệu giống hệt nhau trong mỗi đối tượng. Để hiểu ý chính, hãy xem xét ví dụ cơ bản này. Giả sử chúng ta có giao diện Nhân viên :
public interface Employee {
   void work();
}
Và nó có một vài cách triển khai, chẳng hạn như lớp Luật sư :
public class Lawyer implements Employee {

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

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
Và lớp Kế toán :
public class Accountant implements Employee {

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

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
Các phương thức này hoàn toàn tùy ý - trong ví dụ này, chúng ta chỉ cần thấy rằng chúng đang được thực thi. Điều này cũng đúng về hàm tạo. Đầu ra của bàn điều khiển cho chúng ta biết khi nào các đối tượng mới được tạo. Chúng tôi còn có bộ phận nhân sự có nhiệm vụ trả lại nhân viên được yêu cầu. Nếu nhân viên đó chưa có mặt trong biên chế thì họ sẽ được tuyển dụng và bộ phận nhân sự sẽ trả lại nhân viên mới:
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;
   }
}
Vì vậy, logic rất đơn giản: nếu đối tượng mong muốn tồn tại, hãy trả lại nó; nếu không, hãy tạo nó, cất vào bộ lưu trữ (thứ gì đó giống như bộ đệm) và trả lại. Bây giờ hãy xem mọi thứ hoạt động như thế nào:
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();
}
Đây là những gì chúng ta sẽ thấy trong bảng điều khiển:
Một luật sư đã được thuê. Giải quyết các vấn đề pháp lý... Thuê một kế toán viên. Lưu trữ hồ sơ kế toán... Giải quyết các vấn đề pháp lý... Lưu trữ hồ sơ kế toán... Giải quyết các vấn đề pháp lý... Lưu trữ hồ sơ kế toán... Giải quyết các vấn đề pháp lý... Lưu trữ hồ sơ kế toán... Giải quyết các vấn đề pháp lý... Làm kế toán Hồ sơ…
Như bạn có thể thấy, chúng tôi chỉ tạo hai đối tượng và sử dụng lại chúng nhiều lần. Ví dụ này rất đơn giản nhưng nó thể hiện cách mẫu thiết kế này có thể bảo tồn tài nguyên của chúng ta. Như bạn có thể nhận thấy, logic của mô hình này rất giống với logic của nhóm bảo hiểm. Khám phá các câu hỏi và câu trả lời từ cuộc phỏng vấn xin việc cho vị trí nhà phát triển Java.  Phần 7 - 2

64. Làm thế nào để chia một chuỗi thành nhiều phần? Đưa ra một ví dụ về mã có liên quan

Rõ ràng, câu hỏi này là về phương pháp phân chia . Lớp String có hai biến thể của phương thức này:
String split(String regex);
String split(String regex);
Tham số biểu thức chính quy là dấu phân cách - ví dụ: một số biểu thức chính quy được sử dụng để chia chuỗi thành một mảng các chuỗi:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
Bảng điều khiển sẽ hiển thị:
Xin chào thế giới, đó là Amigo!
Vì vậy, chuỗi của chúng tôi được chia thành một mảng các chuỗi, sử dụng khoảng trắng làm dấu phân cách (thay vì biểu thức chính quy "\\s" , chúng tôi cũng có thể sử dụng biểu thức chuỗi thông thường " " ). Biến thể thứ hai, quá tải, có tham số giới hạn bổ sung . limit là kích thước tối đa được phép của mảng kết quả. Nói cách khác, khi chuỗi đã được chia thành số lượng chuỗi con tối đa được phép, quá trình phân tách sẽ dừng lại và phần tử cuối cùng sẽ chứa bất kỳ "phần còn lại" nào từ chuỗi có thể không được phân tách. Ví dụ:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
Đầu ra của bảng điều khiển:
Xin chào thế giới, đó là Amigo!
Như chúng ta có thể thấy, nếu không có limit = 2 , phần tử cuối cùng của mảng có thể được chia thành ba chuỗi con.

65. Tại sao mảng ký tự lại tốt hơn chuỗi ký tự trong việc lưu trữ mật khẩu?

Có một số lý do để ưu tiên mảng hơn chuỗi khi lưu trữ mật khẩu:

1. Nhóm chuỗi và tính bất biến của chuỗi.

Khi sử dụng một mảng ( char[] ), chúng ta có thể xóa dữ liệu một cách rõ ràng sau khi làm việc xong với nó. Chúng tôi cũng có thể ghi đè mảng bao nhiêu tùy thích, loại bỏ mật khẩu khỏi hệ thống ngay cả trước khi thu gom rác (chỉ cần thay đổi một vài ô thành giá trị không hợp lệ). Ngược lại, String là một lớp bất biến. Điều này có nghĩa là nếu chúng ta muốn thay đổi giá trị của một đối tượng String , chúng ta sẽ nhận được một giá trị mới, nhưng giá trị cũ sẽ vẫn còn trong nhóm chuỗi. Nếu muốn xóa Chuỗi chứa mật khẩu, chúng ta sẽ phải đối mặt với một nhiệm vụ phức tạp vì chúng ta cần trình thu gom rác xóa giá trị đó khỏi nhóm chuỗi, nhưng Chuỗi đó có thể sẽ vẫn ở đó trong một thời gian dài. Nghĩa là, khi nói đến việc lưu trữ dữ liệu một cách an toàn, String kém hơn mảng char .

2. Nếu xuất giá trị Chuỗi ra bảng điều khiển (hoặc nhật ký), thì chúng ta sẽ nhận được:

String password = "password";
System.out.println("Password - " + password);
Đầu ra của bảng điều khiển:
Mật khẩu - mật khẩu
Và nếu bạn tình cờ in mảng ra bàn điều khiển:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
bảng điều khiển sẽ hiển thị những từ vô nghĩa khó hiểu:
Mật khẩu - [C@7f31245a
Trên thực tế, nó không phải là nói lắp. Dưới đây là cách hiểu những gì bạn thấy: [C là tên lớp - mảng char , @ là dấu phân cách và sau đó 7f31245a là mã băm thập lục phân.

3. Hướng dẫn tham khảo Kiến trúc mật mã Java (JCA) chính thức đề cập rõ ràng việc lưu trữ mật khẩu trong char[] thay vì String :

"Có vẻ hợp lý khi thu thập và lưu trữ mật khẩu trong một đối tượng thuộc loại java.lang.String . Tuy nhiên, cần lưu ý: Các đối tượng thuộc loại String là bất biến, tức là không có phương thức nào được xác định cho phép bạn thay đổi (ghi đè) hoặc loại bỏ nội dung của Chuỗi sau khi sử dụng. Tính năng này làm cho các đối tượng Chuỗi không phù hợp để lưu trữ thông tin nhạy cảm về bảo mật như mật khẩu người dùng. Thay vào đó, bạn phải luôn thu thập và lưu trữ thông tin nhạy cảm về bảo mật trong một mảng char." Khám phá các câu hỏi và câu trả lời từ cuộc phỏng vấn xin việc cho vị trí nhà phát triển Java.  Phần 7 - 3

liệt kê

66. Mô tả ngắn gọn về Enum trong Java

Enum là viết tắt của liệt kê, là một tập hợp các hằng chuỗi được hợp nhất bởi một kiểu chung. Chúng tôi khai báo một bằng cách sử dụng từ khóa enum . Đây là một ví dụ với enum : các vai trò được phép trong một số khuôn viên trường học:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
Các từ viết hoa là hằng số liệt kê. Chúng được khai báo một cách đơn giản, không có toán tử mới . Việc sử dụng bảng liệt kê giúp cuộc sống dễ dàng hơn nhiều vì chúng giúp tránh sai sót và nhầm lẫn về tên (vì danh sách chỉ xác định các giá trị hợp lệ). Đối với tôi, chúng rất thuận tiện trong việc xây dựng switch .

67. Enum có thể triển khai giao diện không (sử dụng từ khóa cụ thể)?

Đúng. Xét cho cùng, enum không chỉ đại diện cho các tập hợp thụ động (chẳng hạn như các vai trò trong khuôn viên trường học). Trong Java, chúng có thể biểu diễn các đối tượng phức tạp hơn, vì vậy bạn có thể cần thêm chức năng bổ sung cho chúng. Điều này cũng cho phép bạn tận dụng tính đa hình bằng cách thay thế giá trị enum ở những nơi cần loại giao diện được triển khai.

68. Enum có thể mở rộng một lớp không (dùng từ khóa mở rộng)?

Không, không thể, vì enum là lớp con của lớp Enum<T> mặc định , trong đó T là loại enum. Đây không gì khác hơn là một lớp cơ sở chung cho tất cả các kiểu enum trong ngôn ngữ Java. Việc chuyển đổi từ enum sang lớp được thực hiện bởi trình biên dịch Java tại thời điểm biên dịch. Phần mở rộng không được chỉ định rõ ràng trong mã nhưng nó luôn được ngụ ý.

69. Có thể tạo Enum mà không cần bất kỳ phiên bản nào của đối tượng không?

Câu hỏi này hơi khó hiểu và tôi không chắc mình hiểu hết nó. Tôi có hai cách giải thích: 1. Bạn có thể có một enum mà không có bất kỳ giá trị nào không? Đúng, tất nhiên, nhưng nó sẽ giống như một lớp học trống rỗng - chẳng hạn như vô nghĩa
public enum Role {
}
Và nếu chúng ta gọi:
var s = Role.values();
System.out.println(s);
Chúng tôi nhận được những điều sau đây trong bảng điều khiển:
[Lflyweight.Role;@9f70c54
(một mảng trống các giá trị Vai trò ) 2. Có thể tạo một enum mà không cần toán tử mới không ? Vâng tất nhiên. Như tôi đã nói ở trên, bạn không sử dụng toán tử mới cho các giá trị enum vì chúng là các giá trị tĩnh.

70. Chúng ta có thể ghi đè phương thức toString() của Enum không?

Có, tất nhiên bạn có thể ghi đè phương thức toString() để xác định cách hiển thị enum của mình khi phương thức toString được gọi ( ví dụ: khi chuyển đổi một enum thành một chuỗi thông thường để xuất nó ra bảng điều khiển hoặc nhật ký).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
Đó là tất cả đối với tôi ngày hôm nay. Cho đến phần tiếp theo!
Bình luận
  • Phổ biến
  • Mới
Bạn phải đăng nhập để đăng nhận xet
Trang này chưa có bất kỳ bình luận nào