CodeGym /Blog Java /Ngẫu nhiên /Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java...
John Squirrels
Mức độ
San Francisco

Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core. Phần 1

Xuất bản trong nhóm
Xin chào tất cả các anh chị em kỹ sư phần mềm! Hãy nói về các câu hỏi phỏng vấn. Về những gì bạn cần chuẩn bị và những gì bạn cần biết. Đây là thời điểm tuyệt vời để xem lại hoặc nghiên cứu những điểm này lần đầu tiên. Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 1 Tôi đã kết thúc với một bộ sưu tập khá phong phú các câu hỏi thường gặp về OOP, cú pháp Java, ngoại lệ Java, bộ sưu tập và đa luồng, mà tôi sẽ chia thành nhiều phần để tiện theo dõi. Thật khó để bao quát mọi thứ cùng một lúc, nhưng tôi hy vọng tài liệu này sẽ cung cấp nền tảng tốt cho những người chuẩn bị tìm công việc đầu tiên với tư cách là một lập trình viên. Để hiểu và lưu giữ tốt nhất, tôi cũng khuyên bạn nên xem qua các nguồn khác. Bạn có thể hiểu sâu hơn về một khái niệm bằng cách tiếp cận nó từ nhiều góc độ khác nhau. Quan trọng:Chúng tôi sẽ chỉ nói về Java trước phiên bản 8. Tất cả những đổi mới có trong phiên bản 9, 10, 11, 12 và 13 sẽ không được xem xét ở đây. Mọi ý tưởng/nhận xét về cách cải thiện câu trả lời đều được hoan nghênh . Thưởng thức đọc của bạn. Đi nào!

Phỏng vấn Java: câu hỏi về OOP

1. Java có những đặc điểm gì?

Trả lời:
  1. Khái niệm OOP:

    1. hướng đối tượng
    2. di sản
    3. đóng gói
    4. tính đa hình
    5. trừu tượng
  2. Đa nền tảng: Một chương trình Java có thể chạy trên bất kỳ nền tảng nào mà không có bất kỳ thay đổi nào. Tất nhiên, điều này yêu cầu một JVM (máy ảo Java) đã cài đặt.

  3. Hiệu suất cao: Trình biên dịch Just-In-Time (JIT) giúp đạt được hiệu suất cao. Trình biên dịch JIT chuyển đổi mã byte thành mã máy và sau đó JVM bắt đầu thực thi.

  4. Đa luồng: JVM tạo một luồng thực thi được gọi là tệp main thread. Một lập trình viên có thể tạo nhiều luồng bằng cách bắt nguồn từ lớp Thread hoặc triển khai Runnablegiao diện.

2. Thừa kế là gì?

Kế thừa có nghĩa là một lớp có thể kế thừa một lớp khác (sử dụng từ khóa mở rộng ). Điều này có nghĩa là bạn có thể sử dụng lại mã từ lớp mà bạn kế thừa. Lớp hiện có được gọi là lớp superclassvà lớp mới được tạo là lớp subclass. Mọi người cũng nói rằng hãy sử dụng các thuật ngữ cha mẹ và child.

public class Animal {
   private int age;
}

public class Dog extends Animal {

}
đâu AnimalparentDogchild.

3. Đóng gói là gì?

Câu hỏi này thường được hỏi trong các cuộc phỏng vấn cho các vị trí nhà phát triển Java. Đóng gói đang ẩn việc triển khai bằng cách sử dụng các công cụ sửa đổi truy cập, getters và setters. Điều này được thực hiện để ngăn chặn truy cập bên ngoài bất cứ nơi nào các nhà phát triển nghĩ rằng nó là cần thiết. Một ví dụ đơn giản từ cuộc sống thực là chiếc xe hơi. Chúng tôi không có quyền truy cập trực tiếp vào hoạt động của động cơ. Tất cả những gì chúng ta cần làm là tra chìa khóa vào ổ điện và nổ máy. Các quy trình diễn ra bí mật không phải việc của chúng tôi. Hơn nữa, nếu chúng ta can thiệp vào hoạt động của động cơ, điều đó có thể dẫn đến một tình huống khó lường, có thể làm hỏng xe và dẫn đến tổn thương cơ thể. Chính xác điều tương tự xảy ra trong lập trình. Điều này được mô tả tốt trên Wikipedia. Ngoài ra còn có một bài viết về đóng gói trên CodeGym .

4. Đa hình là gì?

Tính đa hình là khả năng của chương trình xử lý các đối tượng có cùng giao diện theo cùng một cách mà không cần thông tin về loại cụ thể của đối tượng. Như đã nói, "một giao diện — nhiều triển khai". Với tính đa hình, bạn có thể kết hợp và sử dụng các loại đối tượng khác nhau dựa trên các hành vi được chia sẻ. Ví dụ, chúng ta có một lớp Động vật có hai hậu duệ: Chó và Mèo. Lớp Động vật chung có một hành vi chung cho tất cả, khả năng tạo ra âm thanh. Chúng tôi sử dụng các khả năng đa hình khi chúng tôi cần thu thập mọi thứ kế thừa lớp Animal và thực thi phương thức "tạo âm thanh". Đây là giao diện của nó:

List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Nói cách khác, đa hình là hữu ích. Và điều này cũng áp dụng cho các phương thức đa hình (quá tải). Cách sử dụng đa hình

Câu hỏi phỏng vấn về cú pháp Java

5. Hàm tạo trong Java là gì?

Constructor có các đặc điểm sau:
  1. Khi một đối tượng mới được tạo, chương trình sẽ sử dụng hàm tạo thích hợp để tạo đối tượng đó.
  2. Một hàm tạo giống như một phương thức. Đặc điểm nổi bật của nó nằm ở chỗ không có giá trị trả về (kể cả void) và tên của nó giống với tên của lớp.
  3. Nếu không có hàm tạo nào được tạo một cách rõ ràng, thì một hàm tạo trống sẽ được tạo tự động.
  4. Một hàm tạo có thể được ghi đè.
  5. Nếu bạn khai báo một hàm tạo có tham số nhưng cũng cần một hàm tạo không có tham số, thì bạn phải tạo riêng nó, vì nó sẽ không được tạo tự động.

6. Hai lớp nào không kế thừa Object?

Đừng để bị lừa bởi những câu hỏi mẹo - không có những lớp học như vậy. Tất cả các lớp kế thừa lớp Object trực tiếp hoặc thông qua tổ tiên!

7. Biến cục bộ là gì?

Đây là một câu hỏi phỏng vấn phổ biến khác dành cho các nhà phát triển Java. Biến cục bộ là một biến được định nghĩa bên trong một phương thức và tồn tại miễn là phương thức đó đang được thực thi. Ngay sau khi quá trình thực thi kết thúc, biến cục bộ sẽ không còn tồn tại. Đây là chương trình sử dụng biến cục bộ có tên helloMessage trong phương thức main():

public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. Biến thể hiện là gì?

Biến thể hiện là một biến được khai báo bên trong một lớp. Nó tồn tại miễn là một đối tượng tồn tại. Ví dụ: chúng ta có một lớp Bee, lớp này có hai biến thể hiện — nectarLoad và maxNectarLoad:

public class Bee {

   /**
    * Current nectar load
    */
   private double nectarLoad;

   /**
    * Maximum nectar that can the bee can collect.
    */
   private double maxNectarLoad = 20.0;
 
  ...
}

9. Công cụ sửa đổi truy cập là gì?

Công cụ sửa đổi truy cập là một cơ chế để tùy chỉnh quyền truy cập vào các lớp, phương thức và biến. Các công cụ sửa đổi sau tồn tại, được liệt kê theo thứ tự tăng quyền truy cập:
  1. private— Công cụ sửa đổi truy cập này được sử dụng trên các phương thức, trường và hàm tạo. Quyền truy cập được giới hạn trong lớp mà chúng được khai báo.
  2. package-private (default)— Đây là mức truy cập mặc định cho các lớp học. Quyền truy cập được giới hạn trong gói cụ thể trong đó một lớp, phương thức, biến hoặc hàm tạo được khai báo.
  3. protected— Công cụ sửa đổi truy cập này cung cấp mức độ truy cập giống như package-privatevới việc bổ sung quyền truy cập cho các lớp kế thừa một lớp có công protectedcụ sửa đổi.
  4. public— Mức truy cập này cũng được sử dụng cho các lớp học. Cấp truy cập này có nghĩa là có toàn quyền truy cập trong toàn bộ ứng dụng.
Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 2

10. Ghi đè phương thức là gì?

Chúng ta ghi đè các phương thức khi một lớp con muốn thay đổi hành vi của lớp cha của nó. Nếu chúng ta cũng cần thực hiện những gì trong phương thức gốc, chúng ta có thể sử dụng super.methodName() ở phương thức con, phương thức này sẽ thực thi phương thức gốc. Chúng ta có thể thêm logic bổ sung sau đó. Yêu cầu phải được quan sát:
  • chữ ký phương thức phải giống nhau
  • giá trị trả về phải giống nhau

11. Chữ ký phương thức là gì?

Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 3Chữ ký phương thức là sự kết hợp giữa tên phương thức và các đối số mà phương thức nhận. Chữ ký phương thức là mã định danh duy nhất của phương thức khi quá tải phương thức.

12. Nạp chồng phương thức là gì?

Nạp chồng phương thức là một tính năng của tính đa hình trong đó chúng ta thay đổi chữ ký phương thức để tạo ra nhiều phương thức thực hiện cùng một hành động:
  • cùng tên
  • lập luận khác nhau
  • có thể có các loại trả về khác nhau
Ví dụ, phương thức ArrayListcủa lớp add()có thể được nạp chồng, cho phép chúng ta thêm theo nhiều cách khác nhau tùy thuộc vào các đối số đầu vào:
  • add(Object o)— Phương pháp này chỉ đơn giản là thêm một đối tượng
  • add(int index, Object o)— Phương thức này thêm một đối tượng tại một chỉ mục cụ thể
  • add(Collection<Object> c)— Phương thức này thêm một danh sách các đối tượng
  • add(int index, Collection<Object> c)— Phương thức này thêm một danh sách các đối tượng bắt đầu từ một chỉ mục cụ thể.

13. Giao diện là gì?

Java không hỗ trợ đa kế thừa. Để khắc phục hạn chế này, các giao diện đã được thêm vào ở dạng mà chúng ta biết và yêu thích;) Trong một thời gian dài, các giao diện chỉ có các phương thức mà không có bất kỳ triển khai nào. Trong bối cảnh của câu trả lời này, hãy nói về chúng. Ví dụ:


public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Một số chi tiết sau đây:
  • Tất cả các phương thức trong một giao diện là công khai và trừu tượng
  • Tất cả các biến là cuối cùng tĩnh công khai
  • Các lớp không kế thừa giao diện (tức là chúng ta không sử dụng từ khóa mở rộng). Thay vào đó, các lớp thực hiện chúng (tức là chúng ta sử dụng từ khóa implements). Hơn nữa, bạn có thể triển khai bao nhiêu giao diện tùy thích.
  • Các lớp triển khai một giao diện phải cung cấp việc triển khai tất cả các phương thức có trong giao diện.
Như thế này:

public class Cat implements Animal {
   public void makeSound() {
       // Method implementation
   }

   public void eat() {
       // Implementation
   }

   public void sleep() {
       // Implementation
   }
}

14. Phương thức mặc định trong giao diện là gì?

Bây giờ hãy nói về các phương thức mặc định. Chúng dùng để làm gì? Họ dành cho ai? Các phương pháp này đã được thêm vào để phục vụ "cả hai tay". Tôi đang nói về cái gì vậy? Chà, một mặt, cần phải thêm chức năng mới: lambdas và API Stream. Mặt khác, cần phải giữ lại những gì Java nổi tiếng - khả năng tương thích ngược. Để làm được điều này, các giao diện cần một số giải pháp làm sẵn mới. Đây là cách các phương thức mặc định đến với chúng tôi. Phương thức mặc định là phương thức được triển khai trong giao diện, được đánh dấu bằng defaulttừ khóa. Ví dụ, phương pháp nổi tiếng stream()trong Collectiongiao diện. Tin tôi đi, giao diện này không đơn giản như vẻ ngoài của nó đâu. Hoặc cũng là forEach()phương pháp nổi tiếng không kém trongIterablegiao diện. Nó cũng không tồn tại cho đến khi các phương thức mặc định được thêm vào. Nhân tiện, bạn cũng có thể đọc về nó trên CodeGym tại đây .

15. Làm thế nào để chúng ta kế thừa hai phương thức mặc định giống hệt nhau?

Câu trả lời trước về phương thức mặc định là gì đặt ra một câu hỏi khác. Nếu bạn có thể triển khai các phương thức trong giao diện, thì về mặt lý thuyết, bạn có thể triển khai hai giao diện với cùng một phương thức. làm sao chúng ta làm việc đó bây giờ? Đây là hai giao diện khác nhau với cùng một phương thức:

interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
Và chúng ta có một lớp cài đặt hai giao diện này. Nhưng làm thế nào để chúng ta chọn một phương pháp cụ thể trong giao diện A hoặc B? Cấu trúc đặc biệt sau đây cho phép điều này: A.super.foo():

public class C implements A, B {
   public void fooA() {
       A.super.foo();
   }

   public void fooB() {
       B.super.foo();
   }
}
Do đó, fooA()phương thức sẽ sử dụng foo()phương thức mặc định của Agiao diện, trong khi fooB()phương thức sẽ sử dụng foo()phương thức của Bgiao diện.

16. Các phương thức và lớp trừu tượng là gì?

Trong Java, abstractlà một từ dành riêng. Nó được sử dụng để biểu thị các lớp và phương thức trừu tượng. Đầu tiên, chúng ta cần định nghĩa. Một phương thức trừu tượng là một phương thức được khai báo bằng cách sử dụng abstracttừ khóa mà không cần triển khai trong một lớp trừu tượng. Đó là, đây là một phương thức như trong một giao diện, nhưng có thêm từ khóa, ví dụ:

public abstract void foo();
Một lớp trừu tượng là một lớp cũng được đánh dấu bằng abstracttừ khóa:

public abstract class A {

}
Một lớp trừu tượng có một số tính năng:
  • bạn không thể tạo một đối tượng của một lớp trừu tượng
  • nó có thể có các phương thức trừu tượng
  • nó cũng có thể không có các phương thức trừu tượng
Các lớp trừu tượng là cần thiết để trừu tượng hóa (xin lỗi vì tautology) có một tập hợp các hành vi và trạng thái chung (nghĩa là các phương thức và biến). Cuộc sống thực đầy những ví dụ. Mọi thứ xung quanh chúng ta. "Động vật", "Xe hơi", "Hình học", v.v.

17. Đâu là sự khác biệt giữa String, StringBuilder và StringBuffer?

Stringcác giá trị được lưu trữ trong một nhóm chuỗi không đổi. Ngay sau khi một chuỗi được tạo, nó sẽ xuất hiện trong nhóm này. Và bạn không thể xóa nó. Ví dụ:

String name = "book";
Biến sẽ trỏ đến hằng string pool Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 4Đặt tên biến thành một giá trị khác, ta có:

name = "pen";
Nhóm chuỗi không đổi trông như thế này: Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 5Nói cách khác, cả hai giá trị vẫn ở đó. Bộ đệm chuỗi:
  • Stringcác giá trị được lưu trữ trong một ngăn xếp. Nếu một giá trị bị thay đổi, thì giá trị mới sẽ thay thế giá trị cũ.
  • String Bufferđược đồng bộ hóa và do đó luồng an toàn.
  • Do sự an toàn của luồng, hiệu suất của nó kém.
Ví dụ:

StringBuffer name = “book”;
Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 6Ngay khi giá trị của biến tên thay đổi, giá trị trong ngăn xếp sẽ thay đổi: Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 7StringBuilder hoàn toàn giống với StringBuffer, chỉ có điều nó không an toàn cho luồng. Kết quả là, nó nhanh hơn đáng kể so với StringBuffer.

18. Sự khác biệt giữa lớp trừu tượng và giao diện là gì?

lớp trừu tượng:
  • Các lớp trừu tượng có một hàm tạo mặc định. Nó được gọi mỗi khi lớp con của lớp trừu tượng được tạo.
  • Chúng có thể bao gồm cả phương thức trừu tượng và phương thức không trừu tượng. Nói chung, một lớp trừu tượng không nhất thiết phải có các phương thức trừu tượng.
  • Một lớp kế thừa một lớp trừu tượng chỉ phải thực hiện các phương thức trừu tượng.
  • Một lớp trừu tượng có thể có các biến thể hiện (xem Câu hỏi #5).
Giao diện:
  • Một giao diện không có hàm tạo và không thể được khởi tạo.
  • Chỉ có thể thêm các phương thức trừu tượng (ngoại trừ các phương thức mặc định).
  • Các lớp triển khai giao diện phải triển khai tất cả các phương thức (ngoại trừ các phương thức mặc định).
  • Giao diện chỉ có thể có hằng số.

19. Tại sao truy xuất một phần tử trong mảng là O(1)?

Câu hỏi này thực sự đã được hỏi trong cuộc phỏng vấn cuối cùng của tôi. Sau này tôi mới biết, mục đích của câu hỏi này là để xem suy nghĩ của một người như thế nào. Rõ ràng, có rất ít giá trị thực tế trong kiến ​​thức này. Chỉ cần biết nó là đủ. Đầu tiên, chúng ta cần làm rõ rằng O(1) là ký hiệu cho độ phức tạp thời gian của thuật toán "thời gian không đổi". Nói cách khác, chỉ định này cho biết thời gian thực hiện nhanh nhất. Để trả lời câu hỏi này, chúng ta cần xem xét những gì chúng ta biết về mảng. Để tạo một intmảng, chúng ta phải viết như sau:

int[] intArray = new int[100];
Một số kết luận có thể được rút ra từ cú pháp này:
  1. Khi một mảng được khai báo, kiểu của nó được biết. Nếu loại được biết, thì kích thước của mỗi ô trong mảng được biết.
  2. Kích thước của toàn bộ mảng được biết đến.
Do đó, để hiểu ô nào sẽ ghi vào, chúng ta chỉ cần tính toán vùng bộ nhớ nào sẽ ghi vào. Đối với một máy tính, điều này thật dễ dàng. Máy tính biết nơi bộ nhớ được phân bổ bắt đầu, số lượng phần tử và kích thước của mỗi ô. Tất cả điều này có nghĩa là vị trí để viết sẽ bằng vị trí bắt đầu của mảng + kích thước của mỗi ô nhân với chỉ mục.

Vậy làm thế nào để chúng ta đến O(1) khi truy cập các đối tượng trong ArrayList?

Câu hỏi này ngay sau câu hỏi trước. Sự thật là khi làm việc với một mảng chứa các nguyên hàm, chúng ta biết trước (tại thời điểm tạo) kích thước của kiểu phần tử. Nhưng chúng ta sẽ làm gì nếu chúng ta có loại phân cấp thừa kế này và Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 1 - 8chúng ta muốn tạo một bộ sưu tập cho các phần tử loại A và thêm các triển khai khác nhau (B, C và D):

List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Trong tình huống này, làm thế nào để chúng ta tính toán kích thước của mỗi ô? Rốt cuộc, mỗi đối tượng sẽ khác nhau, có thể với các trường bổ sung khác nhau. phải làm gì? Ở đây, câu hỏi được đặt ra theo cách có nghĩa là làm bạn bối rối. Chúng tôi biết rằng bộ sưu tập không trực tiếp lưu trữ các đối tượng. Nó chỉ lưu trữ các tham chiếu đến các đối tượng. Và tất cả các tài liệu tham khảo có cùng kích thước, và nó được biết đến. Do đó, chúng tôi tính toán các địa chỉ ở đây theo cách tương tự như trong câu hỏi trước.

21. Tự động đóng hộp và mở hộp

Bối cảnh lịch sử: autoboxing và unboxing là một số cải tiến chính trong JDK 5. Autoboxing là quá trình chuyển đổi tự động từ một loại nguyên thủy sang một lớp trình bao bọc tương ứng. Unboxing hoàn toàn ngược lại với autoboxing. Đó là quá trình chuyển đổi một lớp bao bọc thành một lớp nguyên thủy. Nhưng nếu giá trị của một trình bao bọc là null, thì a NullPointerExceptionsẽ bị ném ra trong quá trình mở hộp.

Nguyên thủy và trình bao bọc tương ứng của chúng

nguyên thủy lớp bao bọc
boolean Boolean
int số nguyên
byte byte
than Tính cách
trôi nổi Trôi nổi
dài Dài
ngắn Ngắn
gấp đôi Gấp đôi

// Autoboxing xảy ra:

  • khi gán một nguyên hàm cho một tham chiếu đến một lớp bao bọc:

    TRƯỚC Java 5:

    
    // Manual boxing (the way it was BEFORE Java 5).
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // And so on for other types
    }
    
    After Java 5:
    // Automatic boxing (the way it became in Java 5).
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // And so on for other types
    }
    
  • khi một nguyên thủy được truyền dưới dạng đối số cho một phương thức mong đợi một trình bao bọc:

    
    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }
    

// Mở hộp xảy ra:

  • khi chúng ta gán một thể hiện của lớp bao bọc cho một biến nguyên thủy:

    
    // BEFORE Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    // And after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
    
  • Trong các phép toán số học. Các hoạt động chỉ áp dụng cho các loại nguyên thủy, vì vậy việc mở hộp cho các nguyên thủy là cần thiết.

    
    // BEFORE Java 5:
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // A comparison used to require this:
    integerBox1.intValue() > integerBox2.intValue()
          
    // In Java 5
    integerBox1 > integerBox2
    
  • khi chuyển một thể hiện của lớp trình bao bọc sang một phương thức có nguyên hàm tương ứng:

    
    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }
    

22. Từ khóa cuối cùng là gì và nó được sử dụng ở đâu?

Từ finalkhóa có thể được sử dụng trên các biến, phương thức và lớp.
  1. Giá trị của biến cuối cùng không thể thay đổi sau khi nó được khởi tạo.
  2. Một lớp cuối cùng là vô trùng :) Nó không thể có con.
  3. Một phương thức cuối cùng không thể bị ghi đè bởi một hậu duệ.
Chúng tôi đã đề cập đến những thứ cấp cao. Bây giờ hãy lặn sâu hơn.

biến cuối cùng

Java cung cấp cho chúng ta hai cách để khai báo một biến và gán giá trị cho nó:
  1. Bạn có thể khai báo một biến và khởi tạo nó sau.
  2. Bạn có thể khai báo một biến và gán giá trị ngay lập tức.
Đây là một ví dụ minh họa những cách sử dụng biến cuối cùng này:

public class FinalExample {

   // A static final variable that is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";

   // A final variable that is not initialized, but will only work if you
   // initialize it in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // The final FinalExample.FINAL_EXAMPLE_NAME field cannot be accessed
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // The final Config.creationTime field cannot be accessed
//    finalExample.creationTime = 1L;
   }
}

Một biến cuối cùng có thể được coi là một hằng số?

Vì chúng ta không thể gán giá trị mới cho các biến cuối cùng nên có vẻ như đây là các biến không đổi. Nhưng chỉ ở cái nhìn đầu tiên: Nếu kiểu dữ liệu của biến là immutable, thì vâng, đó là một hằng số. Nhưng nếu kiểu dữ liệu là mutable, nghĩa là có thể thay đổi được, thì có thể sử dụng các phương thức và biến để thay đổi giá trị của đối tượng được tham chiếu bởi một finalbiến. Bởi vì điều này, nó không thể được gọi là một hằng số. Ví dụ sau đây cho thấy rằng một số biến cuối cùng thực sự là hằng số, trong khi những biến khác thì không, vì chúng có thể thay đổi được.

public class FinalExample {

   // Immutable final variables
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // Mutable final variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("Constant?");
}

Biến cục bộ cuối cùng

Khi một finalbiến được tạo trong một phương thức, nó được gọi là một local finalbiến:

public class FinalExample {

   public static void main(String[] args) {
       // You can do this
       final int minAgeForDriveCar = 18;

       // Or you can do this, in a for-each loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Chúng ta có thể sử dụng từ khóa cuối cùng trong vòng lặp for nâng cao, vì một biến mới được tạo sau mỗi lần lặp lại vòng lặp. Hãy nhớ rằng điều này không áp dụng cho vòng lặp for bình thường, vì vậy chúng tôi sẽ gặp lỗi thời gian biên dịch.

// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

lớp học cuối cùng

Một lớp được khai báo là finalkhông thể mở rộng. Nói một cách đơn giản hơn, không lớp nào khác có thể kế thừa nó. Một ví dụ tuyệt vời về một finallớp trong JDK là String. Bước đầu tiên để tạo một lớp bất biến là đánh dấu nó là final, do đó ngăn không cho nó được mở rộng:

public final class FinalExample {
}

// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}

phương pháp cuối cùng

Khi một phương thức được đánh dấu là cuối cùng, nó được gọi là phương thức cuối cùng (có ý nghĩa, phải không?). Một phương thức cuối cùng không thể được ghi đè trong một lớp con. Ngẫu nhiên, các phương thức wait() và notify() của lớp Đối tượng là cuối cùng, vì vậy chúng tôi không có khả năng ghi đè chúng.

public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // Compilation error!
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

Làm thế nào và ở đâu để sử dụng cuối cùng trong Java

  • Sử dụng từ khóa cuối cùng để xác định một số hằng số cấp lớp;
  • Tạo các biến cuối cùng cho các đối tượng mà bạn không muốn thay đổi. Ví dụ: các thuộc tính dành riêng cho đối tượng mà chúng ta có thể sử dụng cho mục đích ghi nhật ký.
  • Nếu bạn không muốn mở rộng một lớp, hãy đánh dấu lớp đó là cuối cùng.
  • Nếu bạn cần tạo một lớp bất biến, bạn cần đặt nó ở dạng cuối cùng.
  • Nếu bạn muốn cách triển khai của một phương thức không thay đổi trong các phần tử con của nó, thì hãy đánh dấu phương thức đó là final. Điều này rất quan trọng để đảm bảo rằng việc triển khai không thay đổi.

23. Các loại có thể thay đổi và không thay đổi là gì?

có thể thay đổi

Các đối tượng có thể thay đổi là các đối tượng có thể thay đổi trạng thái và các biến sau khi tạo. Ví dụ về các lớp có thể thay đổi bao gồm StringBuilder và StringBuffer. Ví dụ:

public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // This setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("First address");
       System.out.println(obj.getAddress());

       // We are updating the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

bất biến

Đối tượng bất biến là đối tượng mà trạng thái và biến không thể thay đổi sau khi đối tượng được tạo. Một chìa khóa tuyệt vời cho HashMap, bạn có nghĩ vậy không? :) Ví dụ: String, Integer, Double, v.v. Ví dụ:

// We'll make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // We remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("Old address");
       System.out.println(obj.getAddress());

       // There is no way to change this field, so it is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}
Trong phần tiếp theo, chúng ta xem xét các câu hỏi và câu trả lời về các tập hợp. Hồ sơ của tôi trên GitHub Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core. Phần 2
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION