JavaRush /Blog Java /Random-VI /Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấ...

Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java. Phần 16

Xuất bản trong nhóm
Chào bạn! Mất bao lâu để trở thành một nhà phát triển? Tôi đã hỏi rất nhiều người và nghe rất nhiều câu trả lời khác nhau. Đối với một số người thậm chí một tháng có thể là đủ, nhưng đối với những người khác thậm chí một năm sẽ không đủ. Nhưng tôi biết chắc chắn rằng việc trở thành nhà phát triển Java là một con đường đầy chông gai và dài, bất kể khả năng ban đầu của bạn như thế nào. Suy cho cùng, khả năng không quan trọng bằng sự bướng bỉnh và chăm chỉ. Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 1Vì vậy, hôm nay chúng tôi tiếp tục phân tích một cách có mục đích các câu hỏi phỏng vấn phổ biến nhất dành cho nhà phát triển Java. Nghiên cứu chúng sẽ dần dần đưa bạn đến gần hơn với mục tiêu ấp ủ của mình. Bắt đầu nào!

17. Cho ví dụ về việc sử dụng Tùy chọn thành công và không thành công

Giả sử chúng ta có một chuỗi giá trị nhất định mà qua đó chúng ta đi qua luồng và cuối cùng chúng ta nhận được một số Tùy chọn :
Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
   .filter(str -> str.length() >= 3)
   .findAny();
Đúng như mong đợi, chúng ta cần lấy giá trị từ Tùy chọn này . Chỉ sử dụng get() là một cách tồi:
String result = stringOptional.get();
Nhưng phương pháp này được cho là lấy giá trị từ Tùy chọn và trả lại cho chúng tôi? Tất nhiên điều này đúng, nhưng nếu nó có ý nghĩa. Chà, nếu các giá trị trong luồng khác nhau và cuối cùng chúng tôi nhận được một Tùy chọn trống , khi chúng tôi cố gắng lấy một giá trị từ nó bằng phương thức get() , thì kết quả sau sẽ được đưa ra: Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 2Điều này không tốt. Trong trường hợp này, tốt hơn là sử dụng các công trình sau:
  1. String result = null;
    if (stringOptional.isPresent()) {
     stringOptional.get();
    }

    Trong trường hợp này, chúng tôi đang kiểm tra xem phần tử có nằm trong Tùy chọn hay không . Nếu không, chuỗi kết quả sẽ có giá trị cũ.

  2. String result = stringOptional.orElse("default value");

    Trong trường hợp này, chúng tôi chỉ định một số giá trị mặc định sẽ được cấp cho chuỗi kết quả trong trường hợp Tùy chọn trống .

  3. String result = stringOptional.orElseThrow(() -> new CustomException());

    Trong trường hợp này, chính chúng ta sẽ đưa ra một ngoại lệ khi Tùy chọn trống .

Điều này có thể thuận tiện trong một ứng dụng, chẳng hạn như khi phương thức Spring JPA được sử dụng - findById() , phương thức này trả về các giá trị Tùy chọn . Trong trường hợp này, với phương pháp này, chúng tôi cố gắng lấy giá trị và nếu nó không có ở đó, chúng tôi sẽ đưa ra một số ngoại lệ Thời gian chạy , được xử lý ở cấp bộ điều khiển bằng cách sử dụng ExceptionHandler và được chuyển đổi thành phản hồi HTTP với trạng thái 404 - KHÔNG TÌM THẤY . Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 3

18. Có thể khai báo phương thức main là phương thức cuối cùng không?

Có, tất nhiên, không có gì ngăn cản chúng ta khai báo phương thức main()Final . Trình biên dịch sẽ không tạo ra lỗi. Nhưng điều đáng ghi nhớ là bất kỳ phương thức nào sau khi khai báo nó là phương thức cuối cùng sẽ trở thành phương thức cuối cùng - không bị ghi đè. Mặc dù vậy, ai sẽ xác định lại chính ??? Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 4

19. Có thể nhập cùng một gói/lớp hai lần không? Hậu quả có thể là gì?

Có, bạn có thể. Hậu quả? Chúng tôi sẽ có một số nội dung nhập không cần thiết mà Intelijj IDEA sẽ hiển thị dưới dạng màu xám, tức là. chưa sử dụng. Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 5Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 6

20. Đúc là gì? Khi nào chúng tôi có thể nhận được ClassCastException?

Truyền hoặc truyền kiểu , là quá trình chuyển đổi một loại dữ liệu sang loại dữ liệu khác: theo cách thủ công (truyền ngầm) hoặc tự động (truyền kiểu rõ ràng). Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 7Việc chuyển đổi tự động được thực hiện bởi trình biên dịch và việc chuyển đổi thủ công được thực hiện bởi nhà phát triển. Việc truyền kiểu cho các kiểu nguyên thủy và các lớp có phần khác nhau, vì vậy chúng ta sẽ xem xét chúng một cách riêng biệt. Các kiểu nguyên thủy Một ví dụ về việc truyền tự động các kiểu nguyên thủy:
int value = 17;
double convertedValue = value;
Như bạn có thể thấy, không cần thao tác bổ sung nào ngoài dấu = ở đây. Ví dụ về việc truyền thủ công các kiểu nguyên thủy:
double value = 17.89;
int convertedValue = (int)value;
Trong trường hợp này, chúng ta có thể quan sát việc truyền thủ công, được triển khai bằng cách sử dụng (int) , theo đó phần sau dấu phẩy sẽ bị loại bỏ và giá trị được chuyển đổi sẽ có giá trị - 17. Đọc thêm về việc truyền các kiểu nguyên thủy trong bài viết này . Bây giờ chúng ta hãy chuyển sang đồ vật. Các kiểu tham chiếu Đối với các kiểu tham chiếu, có thể tự động chuyển các lớp con sang các lớp cha. Điều này còn được gọi là đa hình . Giả sử chúng ta có lớp Lion kế thừa từ lớp Cat . Trong trường hợp này, quá trình chuyển đổi tự động sẽ như thế này:
Cat cat = new Lion();
Nhưng với một dàn diễn viên rõ ràng , mọi thứ có phần phức tạp hơn vì không có chức năng cắt bỏ phần thừa, giống như với các kiểu nguyên thủy. Và chỉ thực hiện chuyển đổi rõ ràng dưới dạng:
Lion lion= (Lion)new Cat();
Bạn sẽ gặp lỗi: Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 8Trên thực tế, bạn có thể thêm các phương thức vào lớp con cháu Lion mà ban đầu không có trong lớp Cat , sau đó thử gọi chúng, vì kiểu đối tượng của bạn sẽ trở thành Lion . Vâng, không có logic trong việc này. Do đó, việc thu hẹp loại chỉ có thể thực hiện được khi đối tượng ban đầu thuộc loại Lion nhưng sau đó được chuyển sang lớp cha:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
Ngoài ra, để có độ tin cậy cao hơn, nên thu hẹp phạm vi cho các đối tượng bằng cách sử dụng cấu trúc instanceOf :
if (cat instanceof Lion) {
 newLion = (Lion)new Cat();
}
Đọc thêm về kiểu tham chiếu trong bài viết này .

21. Tại sao các framework hiện đại chủ yếu chỉ sử dụng các ngoại lệ không được kiểm tra?

Tôi nghĩ tất cả là do việc xử lý các ngoại lệ được kiểm tra vẫn là mã spaghetti được lặp lại ở mọi nơi nhưng không thực sự cần thiết trong mọi trường hợp. Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 9Trong những trường hợp như vậy, việc xử lý bên trong khung sẽ dễ dàng hơn để không một lần nữa chuyển việc này sang vai các nhà phát triển. Có, tất nhiên, một tình huống khẩn cấp có thể phát sinh, nhưng những trường hợp ngoại lệ không được kiểm tra tương tự này có thể được xử lý theo cách thuận tiện hơn mà không cần bận tâm đến việc xử lý trong try-catch và không cần chuyển chúng đi xa hơn thông qua các phương pháp. Chỉ cần chuyển đổi ngoại lệ thành một số phản hồi HTTP trongExceptionHandler là đủ .

22. Nhập tĩnh là gì?

Khi sử dụng dữ liệu tĩnh (phương thức, biến), bạn không thể tự tạo đối tượng mà thực hiện bằng tên của lớp, nhưng ngay cả trong trường hợp này chúng ta cũng cần tham chiếu đến lớp. Mọi thứ đều đơn giản với nó: nó được thêm vào bằng cách nhập thông thường. Nhưng điều gì sẽ xảy ra nếu chúng ta sử dụng một phương thức tĩnh mà không viết tên lớp, như thể nó là một phương thức tĩnh của lớp hiện tại? Điều này có thể thực hiện được với tính năng nhập tĩnh! Trong trường hợp này, chúng ta phải viết nhập tĩnh và liên kết đến phương thức đó. Ví dụ như thế này, một phương thức tĩnh của lớp Math để tính giá trị cosine:
import static java.lang.Math.cos;
Kết quả là chúng ta có thể sử dụng phương thức này mà không cần chỉ định tên lớp:
double result = cos(60);
Chúng ta cũng có thể tải tất cả các phương thức tĩnh của một lớp cùng một lúc bằng cách sử dụng tính năng nhập tĩnh:
import static java.lang.Math.*;
Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 10

23. Mối quan hệ giữa phương thức hashCode() và Equals() là gì?

Theo Oracle , quy tắc là: Nếu hai đối tượng bằng nhau (tức là phương thức Equals() trả về true ), chúng phải có cùng mã băm. Đồng thời, đừng quên rằng hai đối tượng khác nhau có thể có cùng mã băm. Để hiểu lý do tại sao Equals()hashCode() luôn bị ghi đè theo cặp, hãy xem xét các trường hợp sau:
  1. Cả hai phương pháp đều bị ghi đè.

    Trong trường hợp này, hai đối tượng khác nhau có cùng trạng thái bên trong sẽ trả về bằng() - true , trong khi hashCode() cả hai sẽ trả về cùng một số.

    Hóa ra mọi thứ đều ổn, bởi vì quy tắc đang được tuân theo.

  2. Cả hai phương pháp đều không bị ghi đè.

    Trong trường hợp này, hai đối tượng khác nhau có cùng trạng thái bên trong sẽ trả về sai khi bằng() , vì việc so sánh được thực hiện bằng cách tham chiếu thông qua toán tử == .

    Phương thức hashCode() cũng sẽ trả về các giá trị khác nhau (rất có thể) vì nó tạo ra giá trị được chuyển đổi của địa chỉ vị trí bộ nhớ. Nhưng đối với cùng một đối tượng, giá trị này sẽ giống nhau, giống như bằng() trong trường hợp này sẽ chỉ trả về true khi các tham chiếu trỏ đến cùng một đối tượng.

    Hóa ra trong trường hợp này mọi thứ đều ổn và quy tắc được đáp ứng.

  3. Được ghi đè bằng() , không được ghi đè hashCode() .

    Trong trường hợp này, đối với hai đối tượng khác nhau có cùng trạng thái bên trong, bằng() sẽ trả về truehashCode() sẽ trả về (rất có thể) các giá trị khác nhau.

    Đây là hành vi vi phạm nội quy nên không khuyến khích thực hiện.

  4. Equals() không bị ghi đè , hashCode() bị ghi đè .

    Trong trường hợp này, đối với hai đối tượng khác nhau có cùng trạng thái bên trong, Equals() sẽ trả về falsehashCode() sẽ trả về cùng một giá trị.

    Có sự vi phạm quy tắc nên cách tiếp cận không chính xác.

Như bạn có thể thấy, quy tắc chỉ có thể được thực thi khi bằng()hashCode() đều bị ghi đè hoặc cả hai đều không bị ghi đè. Đọc Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 11thêm về Equals()hashCode() trong bài viết này .

24. Lớp BufferedInputStream và BufferedOutputStream được sử dụng khi nào?

inputStream được sử dụng để đọc dữ liệu theo từng byte từ một số tài nguyên và OutputStream được sử dụng để ghi dữ liệu theo từng byte. Nhưng các thao tác byte-byte có thể rất bất tiện và yêu cầu xử lý bổ sung (để đọc/ghi văn bản bình thường). Trên thực tế, để đơn giản hóa các bản ghi byte như vậy, BufferedOutputStream đã được giới thiệu và BufferedInputStream đã được giới thiệu để đọc . Các lớp này không gì khác hơn là bộ đệm tích lũy dữ liệu, cho phép bạn làm việc với dữ liệu không phải theo từng byte mà bằng toàn bộ gói dữ liệu (mảng). Khi được tạo, BufferedInputStream sẽ đưa vào hàm tạo của nó một phiên bản của loại inputStream , từ đó dữ liệu được đọc:
BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in là một đối tượng inputStream đọc dữ liệu từ bảng điều khiển. Nghĩa là, bằng cách sử dụng đối tượng BufferedInputStream này , chúng ta có thể đọc dữ liệu từ inputStream bằng cách ghi nó vào mảng đã truyền. Điều này hóa ra là một loại trình bao bọc của lớp inputStream . Mảng mảng trong ví dụ này là mảng nhận dữ liệu từ BufferedInputStream . Ngược lại, nó sẽ đọc dữ liệu từ inputStream với một mảng khác, theo mặc định có kích thước là 2048 byte. Điều này cũng đúng với BufferedOutputStream : một thể hiện của loại OutputStream phải được chuyển đến hàm tạo , trong đó chúng ta sẽ ghi dữ liệu vào toàn bộ mảng:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out là một đối tượng OutputStream ghi dữ liệu vào bảng điều khiển. Phương thức tuôn ra() gửi dữ liệu từ BufferedOutputStream đến OutputStream , xóa BufferedOutputStream trong quy trình . Nếu không có phương pháp này, sẽ không có gì được ghi lại. Và tương tự như ví dụ trước: arr là mảng mà dữ liệu được ghi vào BufferedOutputStream . Từ đó chúng được ghi vào OutputStream trong một mảng khác, theo mặc định có kích thước là 512 byte. Đọc thêm về hai lớp này trong bài viết .

25. Sự khác biệt giữa các lớp java.util.Collection và java.util.Collections là gì?

Bộ sưu tập là một giao diện đứng đầu hệ thống phân cấp bộ sưu tập. Nó giới thiệu các lớp cho phép bạn tạo, chứa và sửa đổi toàn bộ nhóm đối tượng. Có nhiều phương thức được cung cấp cho việc này, như add() , Remove() , contains() và các phương thức khác. Giao diện chính của lớp Collection :
  • Set là một giao diện mô tả một tập hợp chứa các phần tử duy nhất (không lặp lại) không có thứ tự.

  • Danh sách là một giao diện mô tả cấu trúc dữ liệu lưu trữ một chuỗi các đối tượng được sắp xếp. Các đối tượng này nhận được chỉ mục (số) riêng của chúng, bằng cách sử dụng mà bạn có thể tương tác với chúng: lấy, xóa, thay đổi, ghi đè.

  • Hàng đợi là giao diện mô tả cấu trúc dữ liệu với các phần tử lưu trữ dưới dạng hàng đợi tuân theo quy tắc - FIFO - First In First Out .

Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 12Đọc thêm về Bộ sưu tập . Bộ sưu tập là một lớp tiện ích cung cấp nhiều phương thức tiện ích khác nhau. Ví dụ:
  • addAll(Collection<? super T> Collection, T...element) - thêm các phần tử được truyền thuộc loại T vào bộ sưu tập .

  • copy(List<? super T> dest, List<?extend T> src) - sao chép tất cả các thành phần từ danh sách src vào danh sách trong dest .

  • EmptyList() - trả về một danh sách trống.

  • max(Bộ sưu tập<? mở rộng T> bộ sưu tập, Bộ so sánh<? super T> comp) - Trả về phần tử tối đa của một bộ sưu tập nhất định theo thứ tự được chỉ định bởi bộ so sánh đã chỉ định.

  • unmodibilityList(List<?extend T> list) - trả về một biểu diễn không thể sửa đổi của danh sách đã chuyển.

Và có rất nhiều phương pháp tiện lợi khác nhau như vậy trong Bộ sưu tập . Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 13Danh sách đầy đủ các phương pháp này có thể được tìm thấy trên trang web của Oracle . Không phải vô cớ mà tôi nói rằng họ cảm thấy thoải mái. Rốt cuộc, tất cả chúng đều tĩnh. Nghĩa là, bạn không cần phải tạo một đối tượng của lớp này mỗi lần để gọi phương thức cần thiết trên đó. Bạn chỉ cần nhập tên của lớp, gọi phương thức mong muốn trên đó và truyền tất cả các đối số được yêu cầu. Tóm lại, Bộ sưu tập là giao diện gốc của khung bộ sưu tập. Bộ sưu tập là một lớp trợ giúp để xử lý thuận tiện hơn các đối tượng thuộc một loại từ cấu trúc bộ sưu tập. Vâng, đó là tất cả cho ngày hôm nay. Mọi điều tốt đẹp nhất!Phân tích các câu hỏi và câu trả lời từ các cuộc phỏng vấn dành cho nhà phát triển Java.  Phần 16 - 14
Các tài liệu khác trong loạt bài:
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION