Triển khai Singleton với Enum (bằng Java)


178

Tôi đã đọc được rằng có thể thực hiện Singletontrong Java bằng cách sử dụng Enumnhư:

public enum MySingleton {
     INSTANCE;   
}

Nhưng, làm thế nào để làm việc trên? Cụ thể, một Objectphải được khởi tạo. Ở đây, làm thế nào MySingletonđược khởi tạo? Ai đang làm new MySingleton()


24
Ai đang làm MySingleton mới () JVM là.
Sotirios Delimanolis

37
INSTANCEcũng giống như public static final MySingleton INSTANCE = new MySingleton();.
Pshemo

6
ENUM - Một singleton được đảm bảo.
Không phải là lỗi

Đọc dzone.com/articles/java-singletons-USE-enum , tại sao nên sử dụng enum chứ không phải các phương pháp khác. Short: vấn đề bắt đầu khi sử dụng serialization và phản chiếu.
Miyagi

Câu trả lời:


203

Điều này,

public enum MySingleton {
  INSTANCE;   
}

có một hàm tạo rỗng ẩn. Làm cho nó rõ ràng thay vào đó,

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

Nếu sau đó bạn đã thêm một lớp khác với một main()phương thức như

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}

Bạn sẽ thấy

Here
INSTANCE

enumcác trường là các hằng số thời gian biên dịch, nhưng chúng là các thể hiện của enumkiểu của chúng . Và, chúng được xây dựng khi loại enum được tham chiếu lần đầu tiên .


13
Bạn nên thêm rằng theo mặc định enums có hàm tạo riêng tư ẩn và việc thêm một hàm tạo riêng là không cần thiết trừ khi bạn thực sự có mã mà bạn cần chạy trong hàm tạo đó
Nimrod Dayan

mỗi trường enum chỉ tạo một thể hiện một lần, do đó không cần tạo hàm tạo riêng.
Panda Pravat

2
enum công khai MySingleton {INSTANCE, INSTANCE1; } và sau đó System.out.println (MySingleton.INSTANCE.hashCode ()); System.out.println (MySingleton.INSTANCE1.hashCode ()); Nó in các mã khác nhau. Có nghĩa là hai đối tượng của MySingleton được tạo ra?
scott dặm

@scottmiles Có. Bởi vì bạn có hai trường hợp. Một định nghĩa, theo định nghĩa, có một.
Elliott Frisch

Công cụ privatesửa đổi không có ý nghĩa đối với một nhà enumxây dựng và hoàn toàn dư thừa.
Dmitri Nesteruk

76

Một enumloại là một loại đặc biệt class.

Ý chí của bạn enumthực sự được biên dịch thành một cái gì đó như

public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){} 
}

Khi mã của bạn truy cập lần đầu tiên INSTANCE, lớp MySingletonsẽ được JVM tải và khởi tạo. Quá trình này khởi tạo statictrường ở trên một lần (uể oải).


Lớp này cũng có chứa constructor riêng () không? Tôi nghĩ rằng nó sẽ được
Yasir Shabbir Choudhary

public enum MySingleton { INSTANCE,INSTANCE1; }và sau đó System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());nó in các mã khác nhau. Có nghĩa là hai đối tượng của MySingleton được tạo ra?
scott dặm

2
@scottmiles Vâng, đó chỉ là hai enumhằng số khác nhau .
Sotirios Delimanolis


@SotiriosDelimanolis, cách tiếp cận này với chủ đề enum có an toàn không?
abg

63

Trong cuốn sách thực hành tốt nhất về Java này của Joshua Bloch, bạn có thể tìm thấy giải thích lý do tại sao bạn nên thực thi thuộc tính Singleton bằng một hàm tạo riêng hoặc loại Enum. Chương này khá dài, vì vậy hãy giữ nó tóm tắt:

Tạo một lớp Singleton có thể gây khó khăn cho việc kiểm tra các máy khách của nó, vì không thể thay thế việc triển khai giả cho một singleton trừ khi nó thực hiện một giao diện phục vụ như kiểu của nó. Cách tiếp cận được đề xuất là triển khai Singletons bằng cách tạo một kiểu enum với một phần tử:

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

Cách tiếp cận này có chức năng tương đương với cách tiếp cận lĩnh vực công cộng, ngoại trừ nó ngắn gọn hơn, cung cấp máy móc tuần tự hóa miễn phí và cung cấp bảo đảm bằng sắt chống lại nhiều lần khởi tạo, ngay cả khi đối mặt với các cuộc tấn công tuần tự hoặc phản xạ tinh vi.

Trong khi phương pháp này vẫn chưa được áp dụng rộng rãi, một loại enum đơn yếu tố là cách tốt nhất để thực hiện một singleton.


Tôi nghĩ rằng, để tạo ra một bản thử nghiệm đơn lẻ, bạn có thể sử dụng một mẫu chiến lược và để tất cả các hành động của người độc thân đi qua chiến lược. Vì vậy, bạn có thể có một chiến lược "sản xuất" và một chiến lược "thử nghiệm" ...
Erk

9

Giống như tất cả các cá thể enum, Java khởi tạo từng đối tượng khi lớp được tải, với một số đảm bảo rằng nó được khởi tạo chính xác một lần cho mỗi JVM . Hãy nghĩ về INSTANCEkhai báo như là một trường cuối cùng tĩnh công khai: Java sẽ khởi tạo đối tượng ngay lần đầu tiên lớp được tham chiếu.

Các cá thể được tạo trong quá trình khởi tạo tĩnh, được định nghĩa trong Đặc tả ngôn ngữ Java, phần 12.4 .

Để biết giá trị của nó, Joshua Bloch mô tả chi tiết mẫu này như mục 3 của Ấn bản thứ hai hiệu quả của Java .


6

Singleton Pattern nói về việc có một hàm tạo riêng và gọi một số phương thức để kiểm soát các phần khởi tạo (như một số getInstance), trong Enums, chúng ta đã có một hàm tạo riêng ẩn.

Tôi không biết chính xác làm thế nào JVM hoặc một số container kiểm soát các thể hiện của chúng ta Enums, nhưng có vẻ như nó đã sử dụng một ẩn Singleton Pattern, sự khác biệt là chúng ta không gọi a getInstance, chúng ta chỉ gọi Enum.


3

Như đã, trong một chừng mực nào đó, đã được đề cập trước đó, enum là một lớp java với điều kiện đặc biệt là định nghĩa của nó phải bắt đầu bằng ít nhất một "hằng số enum".

Ngoài ra, và enums không thể được mở rộng hoặc sử dụng để mở rộng các lớp khác, enum là một lớp giống như bất kỳ lớp nào và bạn sử dụng nó bằng cách thêm các phương thức bên dưới các định nghĩa không đổi:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

Bạn truy cập các phương thức của singleton dọc theo các dòng này:

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();

Việc sử dụng một enum, thay vì một lớp, như đã được đề cập trong các câu trả lời khác, chủ yếu là về việc khởi tạo an toàn luồng của đơn và đảm bảo rằng nó sẽ luôn chỉ là một bản sao.

Và, có lẽ, quan trọng nhất là hành vi này được đảm bảo bởi chính JVM và đặc tả Java.

Đây là một phần từ đặc tả Java về cách ngăn chặn nhiều phiên bản của một cá thể enum:

Một loại enum không có trường hợp nào khác ngoài các trường hợp được xác định bởi hằng số enum của nó. Đó là một lỗi thời gian biên dịch để cố gắng khởi tạo một cách rõ ràng một kiểu enum. Phương pháp nhân bản cuối cùng trong Enum đảm bảo rằng các hằng số enum không bao giờ có thể được sao chép và việc xử lý đặc biệt bằng cơ chế tuần tự hóa đảm bảo rằng các trường hợp trùng lặp không bao giờ được tạo ra do quá trình khử lưu huỳnh. Phản xạ tức thời của các loại enum bị cấm. Cùng với nhau, bốn điều này đảm bảo rằng không có trường hợp nào của loại enum tồn tại ngoài những trường hợp được xác định bởi hằng số enum.

Đáng lưu ý là sau khi khởi tạo, mọi vấn đề về an toàn luồng phải được xử lý như trong bất kỳ lớp nào khác với từ khóa được đồng bộ hóa, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.