Câu hỏi Việc sử dụng khối không tĩnh trong java là gì? [bản sao]


Có thể trùng lặp:
Làm thế nào là một khởi tạo thể hiện khác nhau từ một constructor? 

Khi tất cả các công việc cần thiết có thể được thực hiện bên trong constructor, thì tại sao chúng ta vẫn cần khối không tĩnh trong Java?

EDIT: Điều gì về các lớp học bình thường mà các khối không tĩnh chạy mọi lúc trước khi các nhà xây dựng?


7
2017-08-08 17:29


gốc




Các câu trả lời:


Bổ sung vào câu trả lời của @ Bohemian.

Nếu bạn có nhiều hàm tạo, một khối intialiser tránh trùng lặp.

public class A {
     final Map<String, String> map = new HashMap<String, String>(); {
        // put things in map.
     }
     final int number; {
        int number;
        try {
            number = throwsAnException();
        } catch (Exception e) {
            number = 5;
        }
        this.number = number;
     }

     public A() { /* constructor 1 */ }
     public A(int i) { /* constructor 2 */ }
}

Điều gì về các lớp học bình thường mà khối không tĩnh chạy mọi lúc trước khi các nhà xây dựng?

Về mặt kỹ thuật, trật tự là

  • các nhà xây dựng siêu luôn luôn được gọi là đầu tiên
  • tất cả các khối khởi tạo theo thứ tự xuất hiện.
  • mã hàm khởi tạo

Trong thực tế tất cả các mã này nằm trong mã byte cho mỗi hàm tạo nên không có gì trước hoặc sau hàm tạo tại thời gian chạy.


Trước khi có thêm bất kỳ sự nhầm lẫn nào đối với thứ tự khởi tạo

public class Main extends SuperClass {
    {
        System.out.println("Initialiser block before constructor");
    }

    Main() {
        System.out.println("Main constructor");
    }

    {
        System.out.println("Initialiser block after constructor");

    }

    public static void main(String... args) {
        new Main() {{
            System.out.println("Anonymous initalizer block");
        }};
    }
}

class SuperClass {
    SuperClass() {
        System.out.println("SuperClass constructor");
    }
}

bản in

SuperClass constructor
Initialiser block before constructor
Initialiser block after constructor
Main constructor
Anonymous initalizer block

12
2017-08-08 17:36



Có vẻ hơi khó hiểu khi đặt dấu ngoặc đơn hàng đầu trên cùng một dòng với tuyên bố number. - oldrinb
@veer Đó là một vấn đề về kiểu dáng. Nó cho thấy khối liên quan đặc biệt đến trường đó. - Peter Lawrey
Nó có thể bằng cách lặp lại tất cả các mã trong mỗi nhà xây dựng hoặc tìm cách sử dụng this() Vấn đề là nhớ thêm các bit trước và sau nếu bạn thêm một hàm tạo. Nó đơn giản hơn nhiều không cần phải làm điều này. - Peter Lawrey
@ Ashwyn, điểm tốt., Sửa chữa. - Peter Lawrey
Các khối @PeterLawrey Instance cho các lớp ẩn danh thực thi sau các nhà xây dựng, theo thứ tự xuất hiện. Có thể phân biệt trong câu trả lời của bạn từ thứ tự thực hiện cho các khối mẫu trong một lớp. (ps congrats trên 100K, và cho câu trả lời ký tự kết nối java của bạn làm cho bản tin!). - Bohemian♦


Bạn có thể sử dụng nó với một lớp ẩn danh:

new MyClass() {
    {
         // do something extra on construction (after the constructor executes)
    }
}

Tôi thấy điều này đặc biệt hữu ích khi khởi tạo bản đồ "tra cứu" (tức là nội dung cố định) tại chỗ:

Map<String, String> map = new HashMap<String, String>() {
    {
        put("foo", "bar");
        put("one", "two");
        // etc
    }
};

FYI, điều này đôi khi (kém) được gọi là "khởi tạo cú đúp đôi", trong khi thực tế nó chỉ đơn giản là sử dụng một khối khởi tạo.

Mặc dù một lớp vô danh như vậy về mặt kỹ thuật là lớp con, vẻ đẹp của điều này được thể hiện khi so sánh sử dụng kỹ thuật này với kỹ thuật truyền thống hơn trong việc tạo ra không thể sửa đổi bản đồ:

So sánh một lớp lót đơn giản này, nơi đặt dữ liệu và phân công cùng nhau:

private static final Map<String, String> map = Collections.unmodifiableMap(
    new HashMap<String, String>() {{
        put("foo", "bar");
        put("one", "two");
        // etc
    }});

Với mớ hỗn độn này, phải tạo ra một vật thể riêng biệt do final chỉ cho phép một bài tập:

private static final Map<String, String> map;

static {
    Map<String, String> tempMap = new HashMap<String, String>();
    tempMap.put("foo", "bar");
    tempMap.put("one", "two");
    // etc
    map = Collections.unmodifiableMap(tempMap);
}

Cũng lưu ý rằng với phiên bản lộn xộn, hai câu lệnh không cần phải liền kề, vì vậy nó có thể trở nên ít rõ ràng hơn những gì nội dung của bản đồ không thể sửa đổi được.


7
2017-08-08 17:30



Lưu ý rằng việc khởi tạo các vùng chứa như thế này (được gọi là "double brace initialization") tạo một lớp con thay vì không thích hợp - tương tự như cách phân lớp Thread được coi là không phù hợp khi bạn chỉ muốn triển khai Runnable. Cẩn thận khi lạm dụng thừa kế; một ví dụ được đề cập ở đây là làm thế nào nó có thể phá vỡ một sự thực thi chung equals. Bạn có lẽ nên xem xét sử dụng phương pháp thu gom nhà máy. - oldrinb
@veer Nice liên kết / đọc. Tôi nghĩ tốt hơn nên kiểm tra CTT của lớp (và / hoặc bất kỳ lớp học được hỗ trợ nào) bằng, mặc dù ..
@ tôi hoàn toàn không coi đây là "lạm dụng thừa kế"; nó chỉ đơn giản là populating một đối tượng trong dòng. Tôi sử dụng thường xuyên trong mã của tôi mà không có bất kỳ vấn đề. Xem câu trả lời đã chỉnh sửa. - Bohemian♦
@Bohemian, bạn có thể đơn giản hóa mã của mình hơn nữa bằng cách sử dụng phương pháp nhà máy, ví dụ: Collections.unmodifiableMap(newHashMap<String, String>("foo", "bar", "one", "two", ...)). Bên cạnh đó, tôi nghĩ bạn nên nhìn vào Nguyên lý thay thế Liskov. - oldrinb
@Bohemian Tôi đã mắc lỗi, Maps.<String, String>newHashMap*. Nhưng bất kể, nó có thể trả lại Map<String, String> (mặc dù nó sẽ là vô nghĩa cho các hợp đồng phương pháp). Tôi không cố gắng trở thành người đi bộ, nhưng người ta nên cẩn thận khi sử dụng thừa kế một cách độc đáo như thế. - oldrinb