Các biến tĩnh được khởi tạo khi nào?


87

Tôi đang tự hỏi khi nào các biến tĩnh được khởi tạo thành giá trị mặc định của chúng. Có đúng là khi một lớp được tải, các vars tĩnh được tạo (cấp phát), sau đó các trình khởi tạo tĩnh và khởi tạo trong các khai báo được thực thi? Các giá trị mặc định được đưa ra tại thời điểm nào? Điều này dẫn đến vấn đề tham chiếu chuyển tiếp.

Cũng xin vui lòng nếu bạn có thể giải thích điều này liên quan đến câu hỏi được hỏi về Tại sao các trường tĩnh không được khởi tạo kịp thời? và đặc biệt là câu trả lời được đưa ra bởi Kevin Brock trên cùng một trang web. Tôi không thể hiểu điểm thứ 3.


2
Vui lòng chỉnh sửa câu hỏi của bạn để đưa vào câu trích dẫn bạn đang đề cập.
Oliver Charlesworth

1
Bạn đã đọc đặc tả ngôn ngữ Java chưa? Đó là một tài liệu khá dễ đọc, cố tình như vậy. Nếu bạn đã đọc nó, bạn có thể hiểu chuyện gì đang xảy ra. Nếu không, bạn có thể đặt một câu hỏi cụ thể hơn ở mức tối thiểu ...
Maarten Bodewes

Tôi nghĩ phần Hỏi và Đáp này là sự trùng lặp của stackoverflow.com/questions/3499214 .
Stephen C

Câu trả lời:


72

Từ Xem các phương thức biến tĩnh trong Java :

  • Nó là một biến thuộc về lớp chứ không phải đối tượng (cá thể)
  • Các biến tĩnh chỉ được khởi tạo một lần khi bắt đầu thực thi. Các biến này sẽ được khởi tạo trước, trước khi khởi tạo bất kỳ biến phiên bản nào
  • Một bản sao duy nhất sẽ được chia sẻ bởi tất cả các phiên bản của lớp
  • Một biến tĩnh có thể được truy cập trực tiếp bằng tên lớp và không cần bất kỳ đối tượng nào.

Các biến phiên bản và lớp (tĩnh) được tự động khởi tạo thành các giá trị mặc định tiêu chuẩn nếu bạn không cố ý khởi tạo chúng. Mặc dù các biến cục bộ không được khởi tạo tự động, nhưng bạn không thể biên dịch một chương trình không khởi tạo được biến cục bộ hoặc gán giá trị cho biến cục bộ đó trước khi nó được sử dụng.

Những gì trình biên dịch thực sự làm là tạo nội bộ một quy trình khởi tạo lớp duy nhất kết hợp tất cả các trình khởi tạo biến tĩnh và tất cả các khối mã khởi tạo tĩnh, theo thứ tự xuất hiện trong khai báo lớp. Thủ tục khởi tạo đơn này được chạy tự động, một lần duy nhất, khi lớp được tải lần đầu tiên.

Trong trường hợp các lớp bên trong , chúng không thể có các trường tĩnh

Một lớp bên trong là một lớp lồng nhau không được khai báo rõ ràng hoặc ngầm định static.

...

Các lớp bên trong có thể không khai báo bộ khởi tạo tĩnh (§8.7) hoặc giao diện thành viên ...

Các lớp bên trong có thể không khai báo các thành viên tĩnh, trừ khi chúng là các biến hằng số ...

Xem JLS 8.1.3 Các lớp bên trong và các trường hợp bao bọc

finalCác trường trong Java có thể được khởi tạo tách biệt với nơi khai báo của chúng, tuy nhiên điều này không thể áp dụng cho static finalcác trường. Xem ví dụ bên dưới.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

Điều này là do chỉ có một bản sao của các staticbiến được liên kết với kiểu, chứ không phải là một bản sao được liên kết với từng thể hiện của kiểu như với các biến thể hiện và nếu chúng ta cố gắng khởi tạo zkiểu static finaltrong hàm tạo, nó sẽ cố gắng khởi động lại trường static finalkiểu zbởi vì hàm tạo được chạy trên mỗi phần khởi tạo của lớp không được xảy ra với các finaltrường tĩnh .


5
In case of static inner classes, they can not have static fieldscó vẻ như là một lỗi đánh máy. Các lớp bên trong là không tĩnh.
Daniel Lubarov

Bạn nên sử dụng tuy nhiên thay vì Mặc dù
Suraj Jain

Khi bạn kích hoạt JVM và tải một lớp lần đầu tiên (điều này được thực hiện bởi trình nạp lớp khi lớp được tham chiếu lần đầu theo bất kỳ cách nào) bất kỳ khối hoặc trường tĩnh nào được 'tải' vào JVM và có thể truy cập được.
nhoxbypass

1
Thật không may, câu trả lời này chứa một số điểm không chính xác thực tế về thời điểm các tĩnh được khởi tạo. Vui lòng xem stackoverflow.com/a/3499322/139985 .
Stephen C

15

Xem:

Đặc biệt cuối cùng cung cấp các bước khởi tạo chi tiết giải thích khi nào các biến tĩnh được khởi tạo và theo thứ tự (với lưu ý rằng finalcác biến lớp và trường giao diện là hằng số thời gian biên dịch được khởi tạo trước).

Tôi không chắc câu hỏi cụ thể của bạn về điểm 3 (giả sử ý bạn là điểm lồng nhau?) Là gì. Trình tự chi tiết cho biết đây sẽ là một yêu cầu khởi tạo đệ quy vì vậy nó sẽ tiếp tục khởi tạo.


11

Trường tĩnh được khởi tạo khi lớp được tải bởi trình tải lớp. Giá trị mặc định được chỉ định tại thời điểm này. Điều này được thực hiện theo thứ tự chúng xuất hiện trong mã nguồn.


10

Thứ tự khởi tạo là:

  1. Khối khởi tạo tĩnh
  2. Khối khởi tạo phiên bản
  3. Người xây dựng

Các chi tiết của quy trình được giải thích trong tài liệu đặc tả JVM .


6

biến tĩnh

  • Nó là một biến thuộc về lớp chứ không phải đối tượng (cá thể)
  • Biến static chỉ được khởi tạo một lần khi bắt đầu thực thi (khi Classloader tải lớp lần đầu tiên).
  • Các biến này sẽ được khởi tạo trước, trước khi khởi tạo bất kỳ biến phiên bản nào
  • Một bản sao duy nhất sẽ được chia sẻ bởi tất cả các phiên bản của lớp
  • Một biến tĩnh có thể được truy cập trực tiếp bằng tên lớp và không cần bất kỳ đối tượng nào

4

Bắt đầu với mã từ câu hỏi khác:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Một tham chiếu đến lớp này sẽ bắt đầu khởi tạo. Đầu tiên, lớp sẽ được đánh dấu là đã khởi tạo. Sau đó, trường tĩnh đầu tiên sẽ được khởi tạo với một thể hiện mới của MyClass (). Lưu ý rằng myClass ngay lập tức được cung cấp một tham chiếu đến một cá thể MyClass trống . Không gian ở đó, nhưng tất cả các giá trị đều rỗng. Hàm tạo bây giờ được thực thi và in ra obj, giá trị này là null.

Bây giờ quay lại khởi tạo lớp: objđược tạo tham chiếu đến một đối tượng thực mới, và chúng ta đã hoàn tất.

Nếu điều này được thiết lập bởi một câu lệnh như: MyClass mc = new MyClass();không gian cho một cá thể MyClass mới lại được cấp phát (và tham chiếu được đặt trong mc). Hàm tạo lại được thực thi và lại in ra obj, bây giờ không phải là giá trị rỗng.

Bí quyết thực sự ở đây là khi bạn sử dụng new, như trong, WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiingay lập tức được cung cấp một tham chiếu đến một bit của bộ nhớ nulled. Sau đó JVM sẽ tiếp tục khởi tạo các giá trị và chạy hàm tạo. Nhưng nếu bạn tham chiếu bằng cách nào đó weii trước khi nó làm như vậy - bằng cách tham chiếu nó từ một luồng khác hoặc hoặc bằng cách tham chiếu từ khởi tạo lớp chẳng hạn - bạn đang xem một cá thể lớp chứa đầy các giá trị null.


1
Một lớp không được đánh dấu là đã khởi tạo cho đến khi quá trình khởi tạo hoàn tất - nếu làm theo cách khác sẽ không có ý nghĩa. Đánh dấu là đã khởi tạo gần như là bước cuối cùng được thực hiện. Xem JLS 12.4.2 .
Dave Newton

@DaveNewton: Khi một thứ gì đó tham chiếu đến lớp và bắt đầu khởi tạo nó, tất cả các tham chiếu khác sẽ coi lớp đó là đã khởi tạo. Họ sẽ không cố gắng khởi tạo nó, và họ sẽ không đợi nó được khởi tạo. Do đó, các trường có vẻ không phải là null khi bắt đầu chương trình thực sự có thể là rỗng trong một thời gian. Không hiểu đây là điều gì đang gây ra tất cả sự nhầm lẫn. Tôi nghĩ đơn giản nhất là nói một lớp chưa được khởi tạo được "đánh dấu" là đã khởi tạo trên tham chiếu đầu tiên, tất cả các tham chiếu khác coi nó là intialized và đó là lý do tại sao điều này xảy ra.
RalphChapin

Một sửa chữa cho nhận xét trước đó: như mô tả JLS 12.4.2 của Dave Newton, lớp bị khóa khi đang được khởi tạo. Các luồng khác sẽ đợi lớp được khởi tạo. Tuy nhiên, điều đó không ảnh hưởng đến trường hợp này, tất cả đều xảy ra trong một chuỗi.
RalphChapin

4

Biến static có thể được intialize theo ba cách sau đây, hãy chọn bất kỳ biến nào bạn thích

  1. bạn có thể phức tạp hóa nó tại thời điểm khai báo
  2. hoặc bạn có thể làm bằng cách tạo khối tĩnh, ví dụ:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. Có một giải pháp thay thế cho các khối tĩnh - bạn có thể viết một phương thức tĩnh riêng

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
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.