JavaRush /Blog Java /Random-VI /Nghỉ giải lao #143. Các lớp kín trong Java 17. 4 cách tri...

Nghỉ giải lao #143. Các lớp kín trong Java 17. 4 cách triển khai Singleton

Xuất bản trong nhóm

Các lớp kín trong Java 17

Nguồn: Codippa Trong bài đăng này, chúng ta sẽ xem xét các lớp niêm phong, một tính năng mới được giới thiệu trong Java 17 cũng như cách khai báo và sử dụng chúng cùng với các ví dụ. Nghỉ giải lao #143.  Sealed class trong Java 17. 4 cách triển khai Singleton - 1Các lớp kín xuất hiện lần đầu tiên trong Java 15 dưới dạng tính năng xem trước và sau đó là trong Java 16 với cùng trạng thái. Tính năng này có đầy đủ chức năng khi phát hành Java 17 ( JEP 409 ).

Lớp học kín là gì?

Một lớp kín cho phép bạn hạn chế hoặc chọn các lớp con. Một lớp không thể mở rộng một lớp riêng trừ khi nó nằm trong danh sách các lớp con được phép của lớp cha. Lớp được niêm phong bằng từ khóa seal . Sau lớp seal phải có từ khóa allow , cùng với danh sách các lớp có thể mở rộng nó. Đây là một ví dụ:
public sealed class Device permits Computer, Mobile {
}
Tuyên bố này có nghĩa là Thiết bị chỉ có thể được mở rộng bởi các lớp Máy tínhDi động . Nếu bất kỳ lớp nào khác cố gắng mở rộng nó, một lỗi trình biên dịch sẽ được đưa ra. Một lớp mở rộng một lớp được niêm phong phải có từ khóa cuối cùng , được niêm phong hoặc không được niêm phong trong khai báo của nó . Vì vậy chúng ta có một hệ thống phân cấp lớp cố định. Điều này liên quan thế nào đến việc tạo một lớp con?
  1. cuối cùng có nghĩa là nó không thể được phân lớp thêm.

  2. seal có nghĩa là chúng ta cần khai báo các lớp con có giấy phép .

  3. không được niêm phong có nghĩa là ở đây chúng tôi kết thúc hệ thống phân cấp cha-con .

Ví dụ: Máy tính cho phép các lớp Máy tính xách tay và Máy tính để bàn miễn là Bản thân Máy tính xách tay vẫn chưa được niêm phong . Điều này có nghĩa là Laptop có thể được mở rộng bởi các lớp như Apple , Dell , HP, v.v.

Mục tiêu chính của việc giới thiệu các lớp kín là:

  1. Cho đến thời điểm hiện tại, bạn chỉ có thể giới hạn phần mở rộng của một lớp bằng từ khóa cuối cùng . Một lớp niêm phong kiểm soát những lớp nào có thể mở rộng nó bằng cách đưa chúng vào danh sách được phép.

  2. Nó cũng cho phép lớp kiểm soát xem lớp nào sẽ là lớp con của nó.

Quy tắc

Một số quy tắc cần nhớ khi sử dụng các lớp kín:
  1. Một lớp được niêm phong phải xác định các lớp có thể mở rộng nó bằng cách sử dụng giấy phép . Điều này là không bắt buộc nếu các lớp con được định nghĩa trong lớp cha là lớp bên trong.

  2. Lớp con phải là lớp cuối cùng , được niêm phong hoặc không được niêm phong .

  3. Một lớp con được phép phải mở rộng lớp niêm phong cha của nó.

    Nghĩa là, nếu lớp A kín cho phép lớp B thì B phải mở rộng A.

  4. Nếu lớp được niêm phong nằm trong một mô-đun thì các lớp con cũng phải nằm trong cùng một mô-đun hoặc trong cùng một gói nếu lớp được niêm phong cha nằm trong một mô-đun chưa được đặt tên.

  5. Chỉ các lớp được phép trực tiếp mới có thể mở rộng một lớp kín. Nghĩa là, nếu A là lớp kín cho phép B mở rộng nó thì B cũng là lớp kín cho phép C.

    Khi đó C chỉ có thể mở rộng B, nhưng không thể mở rộng trực tiếp A.

Giao diện kín

Giống như các lớp được niêm phong, các giao diện cũng có thể được niêm phong. Một giao diện như vậy có thể cho phép lựa chọn các giao diện hoặc lớp con của nó, những giao diện này có thể mở rộng nó bằng cách sử dụng các giấy phép . Đây là một ví dụ điển hình:
public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
Ở đây, giao diện Thiết bị cho phép giao diện Điện tửVật lý mở rộng nó và lớp DeviceImpl để triển khai tiếp theo.

Hồ sơ niêm phong

Các lớp kín có thể được sử dụng với các mục được giới thiệu trong Java 16. Một mục nhập không thể mở rộng một lớp thông thường nên nó chỉ có thể triển khai một giao diện riêng tư. Ngoài ra, ký hiệu ngụ ý cuối cùng . Do đó, một mục không thể sử dụng từ khóa cho phép vì nó không thể được phân lớp. Tức là chỉ có một hệ thống phân cấp một cấp với các bản ghi. Đây là một ví dụ:
public sealed interface Device permits Laptop {
}
public record Laptop(String brand) implement Device {
}

Hỗ trợ phản chiếu

Java Reflection cung cấp hỗ trợ cho các lớp kín. Hai phương thức sau đã được thêm vào java.lang.Class :

1. getPermitSubclasses()

Điều này trả về một mảng java.lang.Class chứa tất cả các lớp được đối tượng lớp này cho phép. Ví dụ:
Device c = new Device();
Class<? extends Device> cz = c.getClass();
Class<?>[] permittedSubclasses = cz.getPermittedSubclasses();
for (Class<?> sc : permittedSubclasses){
  System.out.println(sc.getName());
}
Phần kết luận:
Máy tính di động

2.isSealed()

Điều này trả về true nếu lớp hoặc giao diện mà nó được gọi bị niêm phong. Hiện tại đó là tất cả những gì về các lớp niêm phong được thêm vào trong Java 17. Tôi hy vọng bài viết này cung cấp nhiều thông tin.

4 cách để triển khai Singleton

Nguồn: Medium Hôm nay bạn sẽ tìm hiểu một số cách để triển khai mẫu thiết kế Singleton. Mẫu thiết kế Singleton được sử dụng rộng rãi trong các dự án Java. Nó cung cấp khả năng kiểm soát quyền truy cập vào các tài nguyên, chẳng hạn như ổ cắm hoặc kết nối cơ sở dữ liệu. Tôi đã từng được yêu cầu triển khai một singleton trong một cuộc phỏng vấn cho vị trí nhà phát triển web tại một công ty chip lớn. Đây là lần đầu tiên tôi phỏng vấn vị trí web, cũng không chuẩn bị gì nhiều nên tôi đã chọn giải pháp khó nhất: Khởi tạo lười biếng. Mã của tôi chỉ đúng 90% và không đủ hiệu quả, cuối cùng tôi đã thua ở vòng cuối cùng... Vì vậy, tôi hy vọng bài viết của mình sẽ hữu ích cho bạn.

Khởi tạo sớm

class Singleton {
    private Singleton() {}
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}
Vì đối tượng đã được tạo khi khởi tạo nên không có vấn đề gì về an toàn luồng ở đây nhưng sẽ gây lãng phí tài nguyên bộ nhớ nếu không có ai sử dụng nó.

Lười thực hiện

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
Khi sử dụng mẫu khởi tạo lười biếng, đối tượng sẽ được tạo theo yêu cầu. Tuy nhiên, phương pháp này có một vấn đề về an toàn luồng: nếu hai luồng được bắt đầu trên dòng 5 cùng lúc, chúng sẽ tạo ra hai phiên bản Singleton. Để tránh điều này, chúng ta cần thêm một khóa:
class Singleton {
    private Singleton() {}
    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
Double-Checked Locking (DCL): Không có khóa trên dòng 6 nên dòng này sẽ hoạt động rất nhanh nếu đối tượng đã được tạo. Tại sao chúng ta cần kiểm tra kỹ instance == null ? Bởi vì có lẽ có hai thread được giới thiệu ở dòng 7: thread đầu tiên khởi tạo đối tượng, thread thứ hai đang chờ khóa Singleton.class . Nếu không có kiểm tra thì luồng thứ hai sẽ tạo lại đối tượng singleton. Tuy nhiên, phương pháp này vẫn nguy hiểm cho thread. Dòng 9 có thể được chia thành ba dòng mã byte:
  1. Phân bổ bộ nhớ.
  2. Đối tượng khởi tạo.
  3. Gán đối tượng cho tham chiếu cá thể.
Vì JVM có thể không hoạt động đúng thứ tự nên máy ảo có thể gán một đối tượng cho một tham chiếu phiên bản trước khi khởi tạo. Một luồng khác đã nhìn thấy phiên bản != null , nó sẽ bắt đầu sử dụng nó và gây ra sự cố. Vì vậy, chúng ta cần thêm biến thể vào thể hiện, sau đó mã sẽ như thế này:
class Singleton {
    private Singleton() {}
    private volatile static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Sử dụng lớp bên trong tĩnh

public class Singleton {
  private Singleton() {}
  private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}
SingletonHolder là một lớp bên trong tĩnh, nó chỉ được khởi tạo khi phương thức getInstance được gọi . Lớp init trong JVM sẽ chạy <clinit> cmd , sau đó chính JVM sẽ đảm bảo rằng chỉ một luồng có thể gọi <clinit> trên lớp đích, các luồng khác sẽ chờ.

Enum như một singleton

public enum EnumSingleton {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public int setValue(int v) {
        this.value = v;
    }
}
Theo mặc định, một phiên bản enum là an toàn theo luồng, do đó không cần phải lo lắng về việc khóa được kiểm tra hai lần và nó khá dễ viết.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION