Trình khởi tạo thể hiện khác với một phương thức khởi tạo như thế nào?


81

Nói cách khác, tại sao bạn cần một trình khởi tạo phiên bản? Bạn có sự khác biệt hoặc lợi thế nào khi viết một trình khởi tạo thể hiện so với một phương thức khởi tạo?


2
Các khởi tạo phiên bản khá hiếm (trừ khi bạn gặp mã từ ai đó quan tâm đến thành ngữ dấu ngoặc kép).
Tom Hawtin - tackline

Câu trả lời:


108

Điều này dường như giải thích nó tốt:

Bộ khởi tạo phiên bản là một sự thay thế hữu ích cho bộ khởi tạo biến phiên bản bất cứ khi nào:

  • mã khởi tạo phải bắt các ngoại lệ, hoặc

  • thực hiện các phép tính ưa thích mà không thể được thể hiện bằng bộ khởi tạo biến phiên bản. Tất nhiên, bạn có thể luôn viết mã như vậy trong các hàm tạo.

Nhưng trong một lớp có nhiều hàm tạo, bạn sẽ phải lặp lại mã trong mỗi hàm tạo. Với trình khởi tạo thể hiện, bạn chỉ có thể viết mã một lần và nó sẽ được thực thi bất kể phương thức khởi tạo nào được sử dụng để tạo đối tượng. Các trình khởi tạo phiên bản cũng hữu ích trong các lớp ẩn danh bên trong, các lớp này không thể khai báo bất kỳ hàm tạo nào.

Từ: Khởi tạo đối tượng JavaWorld trong Java .


17
Mặt khác, bạn có thể viết mã một lần trong một hàm tạo và chỉ cần gọi nó từ tất cả các hàm tạo khác. Nhưng các lớp bên trong ẩn danh tạo nên một điểm tốt.
Tadeusz Kopec,

11
Bạn có thể gọi nó từ các hàm tạo khác - nhưng sau đó bạn lại lặp lại lệnh gọi. Nếu bạn thêm một hàm tạo mới, bạn phải nhớ thêm lệnh gọi vào nó. Không phải như vậy với trình khởi tạo phiên bản.
talonx

5
@talonx, tôi đồng ý với lập luận của bạn về việc quên, nhưng việc sử dụng hành vi mặc định cũng nguy hiểm không kém. Khi một người hỗ trợ đang đọc qua một hàm tạo trong mã kế thừa, người đó sẽ không phải lúc nào cũng nhớ kiểm tra các bộ khởi tạo phiên bản có thể có. Trong khi init () được sử dụng rõ ràng sẽ nổi bật.
Assambar

1
@ javamonkey79: Bạn có nói rằng nếu tôi chọn constructor qua dụ initializer cho lớp học của tôi, nơi duy nhất Ví dụ initializer rất hữu ích là khi làm việc với các lớp nặc danh?
realPK

4
@Assambar Bạn không thể gán các trường cuối cùng trong phương thức init () của mình, nhưng bạn có thể trong một khối khởi tạo.
Timmos

22

Về vòng đời đối tượng, không có sự khác biệt. Cả hai đều được gọi tại thời điểm xây dựng và về mặt logic, khối khởi tạo có thể được coi là một phần của quá trình xây dựng.

Về mặt ngữ nghĩa, trình khởi tạo là một công cụ tuyệt vời vì một số lý do:

trình khởi tạo có thể cải thiện khả năng đọc mã bằng cách giữ logic khởi tạo bên cạnh biến đang được khởi tạo:

   public class Universe {
       public int theAnswer;
       {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;
       }

       // a bunch of other vars
   }

vs

   public class Universe {
       public int theAnswer;

       // a bunch of other vars

       public Universe() {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;

         // other constructor logic
       }
   }

Bộ khởi tạo được gọi bất kể phương thức khởi tạo nào được sử dụng.

Bộ khởi tạo có thể được sử dụng trong các lớp ẩn danh bên trong, nơi mà các hàm tạo không thể.


3
Những gì bạn có về mặt kỹ thuật là một "trình khởi tạo biến phiên bản" không phải là "trình khởi tạo phiên bản" (một khối được lồng trực tiếp trong một lớp). Xem JLS 3 phần 8.6.
Tom Hawtin - tackline

Khối khởi tạo phiên bản không có tên như được hiển thị ở đây . Phải không? Bạn đã đánh dấu mã khởi tạo phiên bản của mình bằng tên theAnswer. Đúng không? hoặc tôi đang thiếu một cái gì đó.
RBT

1
@RBT theAnswerlà một biến cá thể được khai báo. Nó được khởi tạo trong một khối khởi tạo ẩn danh. Lưu ý dấu chấm phẩy sau phần khai báo biến.
ykaganovich

9

Khi bạn có nhiều hàm tạo và muốn một số mã chung được thực thi cho từng hàm tạo, bạn có thể sử dụng bộ khởi tạo cá thể, vì nó được gọi cho tất cả các hàm tạo.


4

Nói chung, tôi sẽ tránh thành ngữ bộ khởi tạo phiên bản - lợi thế thực sự duy nhất mà nó mang lại so với bộ khởi tạo biến là xử lý ngoại lệ.

Và vì một phương thức init (có thể gọi từ phương thức khởi tạo) cũng có thể xử lý ngoại lệ và cũng tập trung mã thiết lập phương thức khởi tạo, nhưng có lợi thế là nó có thể hoạt động trên các giá trị tham số của phương thức khởi tạo, tôi muốn nói rằng trình khởi tạo thể hiện là dư thừa, và do đó tránh được.


1
Bạn sẽ phải gọi thủ công phương thức init trong TẤT CẢ các hàm tạo của bạn.
Stephan

2
@Alex Điểm tốt, nhưng vì hầu hết các lớp có nhiều hàm tạo đều có logic chung, chúng có xu hướng gọi lẫn nhau. Ít nhất là trong hầu hết các lớp học của tôi.
CPerkins

3

Các lợi thế thực sự của initializers dụ qua nhà xây dựng được nhìn thấy khi chúng tôi sử dụng một lớp bên trong vô danh .

Các lớp ẩn danh bên trong không thể có một phương thức khởi tạo (vì chúng ẩn danh) , vì vậy chúng rất phù hợp tự nhiên cho các trình khởi tạo ví dụ .


1

Tại thời điểm tạo đối tượng, nếu chúng ta muốn thực hiện khởi tạo các biến thể hiện, thì chúng ta nên sử dụng Constructor, khác với hoạt động khởi tạo nếu chúng ta muốn thực hiện bất kỳ hoạt động nào tại thời điểm tạo đối tượng thì chúng ta nên sử dụng khối ví dụ.

Chúng ta không thể thay thế hàm tạo bằng khối cá thể vì hàm tạo có thể nhận đối số nhưng khối cá thể không thể nhận đối số.

Chúng ta không thể thay thế phương thức khởi tạo của khối thể hiện bởi vì một lớp có thể chứa nhiều hơn một phương thức khởi tạo. Nếu chúng ta muốn thay thế khối thể hiện bằng khối khởi tạo thì trong mọi hàm tạo, chúng ta phải viết mã khối cá thể vì trong thời gian chạy mà chúng ta không thể mong đợi hàm tạo nào sẽ được gọi, điều này sẽ làm tăng mã trùng lặp.

Thí dụ :

class MyClass{

    static int object_count = 0;

    MyClass(){
        object_count++;
    }

    MyClass(int i){

        object_count++;
    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Đầu ra: 2

class MyClass{

    static int object_count = 0;

    {
        object_count++;
    }

    MyClass(){

    }

    MyClass(int i){     

    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Đầu ra: 2


0

Bộ khởi tạo là cách để chia sẻ mã giữa các hàm tạo và nó làm cho mã dễ đọc hơn nếu bộ khởi tạo được sử dụng với khai báo biến.

Trình biên dịch Java sao chép các khối khởi tạo vào mọi phương thức khởi tạo. Do đó, cách tiếp cận này có thể được sử dụng để chia sẻ một khối mã giữa nhiều hàm tạo. Tài liệu Oracle

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.