Khi nào thì khởi tạo lớp tĩnh?


110

Các trường tĩnh được khởi tạo khi nào? Nếu tôi không bao giờ khởi tạo một lớp, nhưng tôi truy cập một trường tĩnh, thì TẤT CẢ các khối tĩnh và phương thức tĩnh riêng được sử dụng để khởi tạo các trường tĩnh riêng tư được gọi (theo thứ tự) tại thời điểm đó có đúng không?

Điều gì sẽ xảy ra nếu tôi gọi một phương thức tĩnh? Nó cũng chạy tất cả các khối tĩnh? Trước khi phương pháp?


Câu trả lời:


156

Khởi tạo tĩnh của một lớp thường xảy ra ngay lập tức trước khi lần đầu tiên một trong các sự kiện sau xảy ra:

  • một thể hiện của lớp được tạo,
  • một phương thức tĩnh của lớp được gọi,
  • một trường tĩnh của lớp được gán,
  • một trường tĩnh không cố định được sử dụng, hoặc
  • đối với một lớp cấp cao nhất, một câu lệnh khẳng định được lồng từ vựng trong lớp được thực thi 1 .

Xem JLS 12.4.1 .

Cũng có thể buộc một lớp khởi tạo (nếu nó chưa được khởi tạo) bằng cách sử dụng Class.forName(fqn, true, classLoader)hoặc biểu mẫu ngắnClass.forName(fqn)


1 - Dấu đầu dòng cuối cùng đã có trong JLS cho Java 6 đến Java 8, nhưng rõ ràng đó là một lỗi trong đặc tả. Cuối cùng nó đã được sửa trong Java 9 JLS: xem nguồn .


9
Tuy nhiên, có một cạm bẫy chung. Các số nguyên và Strings được thay thế và không được tham chiếu. Nếu bạn tham chiếu a class Other { public static final int VAL = 10; }từ một số lớp MyClass { private int = Other.VAL; }, lớp đó Othersẽ không được tải. Thay vào đó, trình biên dịch sẽ thay thế trường cuối cùng tại thời điểm biên dịch.
Rafael Winterhalter

6
@RafaelWinterhalter - vâng ... đó là trường hợp trường tĩnh không đổi .
Stephen C

2
@RafaelWinterhalter, điều này không đúng với tất cả các Stringbiến hoặc biến nguyên thủy 'cuối cùng tĩnh' , chỉ những biến được khởi tạo bằng biểu thức hằng.
Lew Bloch

1
Có, và trường thậm chí không cần phải có statictrong khi đây là trường hợp phổ biến.
Rafael Winterhalter

1
Nó là cùng một ngôn ngữ lập trình. Đúng.
Stephen C

14

Trường tĩnh được khởi tạo trong "giai đoạn" khởi tạo của quá trình tải lớp (tải, liên kết và khởi tạo) bao gồm các trình khởi tạo tĩnh và khởi tạo các trường tĩnh của nó. Các bộ khởi tạo tĩnh được thực thi theo thứ tự văn bản như được định nghĩa trong lớp.

Hãy xem xét ví dụ:

public class Test {

   static String sayHello()  {
      return a;
   }

   static String b = sayHello(); // a static method is called to assign value to b.
                                 // but its a has not been initialized yet.

   static String a = "hello";

   static String c = sayHello(); // assignes "hello" to variable c

    public static void main(String[] arg) throws Throwable {
         System.out.println(Test.b); // prints null
         System.out.println(Test.sayHello()); // prints "hello"
    }
}

Test.b in ra nullvì khi sayHellođược gọi trong phạm vi tĩnh, biến tĩnh akhông được khởi tạo.


6
Nói một cách chính xác, khởi tạo không phải là một "giai đoạn" của quá trình tải lớp. Thật vậy, một số lớp có thể được tải nhưng không bao giờ được khởi tạo nếu ứng dụng không thực sự sử dụng chúng.
Stephen C

@Stephen C Bạn nói đúng, tôi đã sử dụng nó vì thiếu một thuật ngữ tốt hơn, có lẽ tôi sẽ trích dẫn nó.
naikus

@StephenC có nghĩa là trong khi quá trình tải Lớp diễn ra, nó sẽ gán bộ nhớ cho các biến tĩnh (& phương thức) nhưng các biến tĩnh đó không được khởi tạo bằng các giá trị được cung cấp trong mã? vì ở đây dường như khi b-> sayHello () -> a, 'a' nằm trong bộ nhớ nhưng giá trị cho nó vẫn chưa được gán.
Shabbir Essaji

Về cơ bản, có.
Stephen C

1

Có, tất cả các trình khởi tạo tĩnh đều được chạy trước khi bạn truy cập lớp lần đầu tiên. Nếu nó là bất kỳ cách nào khác, tôi sẽ gọi nó là một lỗi.


Có nhiều cách để tham chiếu đến một lớp mà không cần khởi tạo nó.
Lew Bloch
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.