2013-08-14 63 views
20

Tại sao các biến được khai báo trong một lớp có giá trị mặc định, nhưng các biến được khai báo bên trong các phương thức, được gọi là "biến cục bộ", không có giá trị mặc định trong Java?Tại sao các biến mẫu có giá trị mặc định trong java?

Ví dụ

class abc 
{ 
    int a; 

    public static void main(String ss[]) 
    { 
     int b; 

      abc aa=new abc(); 
      System.out.println(aa.a); 
      System.out.println(b); 
    } 
} 

Trong ví dụ ở trên này biến a có giá trị mặc định là 0 nhưng biến b cho lỗi rằng nó có thể không được khởi tạo.

+14

'int a' thực sự có giá trị mặc định của '0 '. – Vulcan

+1

toàn bộ khối bộ nhớ của đối tượng luôn luôn được lấp đầy bằng số không, đó là lý do tại sao tất cả các biến trong một đối tượng mặc định là 0, 'b' là biến cục bộ và đơn giản là không được khởi tạo ở phần đó. nó được khởi tạo khi một giá trị được gán – x4rf41

+1

nhưng y b biến không có giá trị mặc định? –

Trả lời

12

Tất cả biến thành viên phải tải vào heap để chúng phải được khởi tạo với giá trị mặc định khi một thể hiện của lớp được tạo. Trong trường hợp các biến cục bộ, chúng không được nạp vào đống chúng được lưu trữ trong ngăn xếp cho đến khi chúng được sử dụng trước java 7, vì vậy chúng ta cần phải khởi tạo chúng một cách rõ ràng. Bây giờ "Trình biên dịch máy chủ Java Hotspot" thực hiện "phân tích thoát" và quyết định phân bổ một số biến trên ngăn xếp thay vì đống.

+0

Biến cục bộ được tải vào heap? Bạn có nhận thấy OP đang nói về một * int *, đó là một kiểu nguyên thủy? o.O –

+0

Anh ta đang dùng 'int' làm ví dụ. – Ashwani

+0

Tuy nhiên, nếu bạn có một biến cục bộ 'Object o', tham chiếu * vẫn tồn tại trong ngăn xếp, không phải trong vùng heap. Nếu bạn không khởi tạo 'o', đó là lỗi khi sử dụng nó - vấn đề đang sử dụng tham chiếu * mà không cần khởi tạo. Nếu bạn chỉ đơn giản là 'o = null', thì bạn có thể sử dụng nó: bạn đã khởi tạo tham chiếu, trong đó, một lần nữa, sống trong ngăn xếp (không phải trong heap) và không có gì" được nạp vào heap "sau khi khởi tạo này. Nếu bạn làm 'o = new Object()', thì bạn đã chạm vào heap, nhưng điều này một mình không liên quan gì đến thực tế là bạn không thể sử dụng các biến cục bộ chưa được khởi tạo. –

2

biến địa phương khởi

Biến khai báo trong phương pháp và trong các khối được gọi là các biến cục bộ. Biến cục bộ không được khởi tạo khi chúng được tạo tại lời gọi phương thức. Do đó, một biến cục bộ phải được khởi tạo một cách rõ ràng trước khi được sử dụng. Nếu không trình biên dịch sẽ gắn cờ nó là lỗi khi phương thức hoặc khối chứa được thực thi.

Ví dụ:

public class SomeClassName{ 

public static void main(String args[]){ 
int total; 
System.out.println("The incremented total is " + total + 3); //(1) 
} 
} 

Trình biên dịch phàn nàn rằng tổng biến địa phương được sử dụng trong tuyên bố println tại (1) có thể không được khởi tạo. Khởi tạo tổng biến địa phương trước khi sử dụng giải quyết vấn đề:

public class SomeClassName{ 

public static void main(String args[]){ 
int total = 45; //Local variable initialized with value 45 System.out.println("The incremented total is " + total+ 3); //(1) 
} 
} 

Fields khởi

Nếu không khởi tạo được cung cấp cho một thể hiện hoặc biến tĩnh, hoặc là khi tuyên bố hoặc trong một khối khởi tạo, sau đó là được khởi tạo ngầm với giá trị mặc định của loại. Biến số cá thể được khởi tạo với giá trị mặc định của loại của nó mỗi khi lớp được khởi tạo, đó là cho mọi đối tượng được tạo từ lớp. Biến tĩnh được khởi tạo với giá trị mặc định của loại của nó khi lớp được tải lần đầu tiên.

+2

Nhưng câu hỏi vẫn chưa được trả lời.Ngay cả khi các biến cục bộ được lưu trữ trên ngăn xếp, tại sao chúng không được gán giá trị mặc định. –

+2

Xem Đặc tả Ngôn ngữ Java: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5 Trích: "Biến cục bộ (§14.4, §14.14) phải được cung cấp một cách rõ ràng một giá trị trước khi nó được sử dụng, bằng cách khởi tạo (§14.4) hoặc gán (§15.26), theo cách có thể được xác minh bằng cách sử dụng các quy tắc để gán rõ ràng (§16). " –

+0

@Ankur Nhìn vào phần cuối cùng --- Khởi tạo trường – Dileep

0

tl; dr: Đó là nhiều hơn hoặc ít hơn một sự lựa chọn tùy ý

Nếu bạn hỏi tôi, nó là một sai lầm rằng Java có giá trị mặc định cho các biến ví dụ. Trình biên dịch nên đã buộc các lập trình viên khởi tạo nó trước khi như nó là trường hợp cho các biến cục bộ.

Lý do đằng sau các giá trị mặc định là an toàn. Khi một đối tượng được khởi tạo, một đoạn bộ nhớ sẽ được cấp phát cho đối tượng chứa các biến thể hiện trỏ đến vv Các nhà thiết kế Java đã quyết định xóa một phần bộ nhớ này bằng các số không và các giá trị rỗng. Bằng cách này bạn sẽ không bao giờ đọc rác xảy ra ở đó trước khi đối tượng được phân bổ. Họ có thể buộc phải khởi tạo; không có gì cơ bản về sự lựa chọn.Nó có thể làm cho mọi việc trở nên dễ dàng thực hiện và có ý nghĩa đủ với các nhà thiết kế Java. Trong trường hợp các biến cục bộ, các nhà thiết kế đã chọn bắt buộc khởi tạo (hoặc có lẽ chính xác hơn khi nói rằng họ đã chọn không khởi tạo bất kỳ kiểu biến cục bộ nào, và do đó hành vi logic nhất của trình biên dịch là để buộc khởi tạo biến trước khi sử dụng).

+0

Nó sẽ là không thể cho trình biên dịch để biết khi một thành viên dụ đã được sử dụng trước khi chuyển nhượng. Trình biên dịch không biết phương thức nào của lớp sẽ được gọi đầu tiên. Tuy nhiên, cùng một phân tích về phương pháp chắc chắn là có thể, vì chỉ có một điểm vào. – EJP

+0

@EJP Kotlin dường như đang làm một công việc khá tốt ... –

+0

Java không cần phải thông minh. – weakish

1

Khi các biến cục bộ được cấp phát trên ngăn xếp, đoạn bộ nhớ cho một biến cục bộ được cấp phát khi biến được gán với một giá trị.

Hãy ví dụ đơn giản

class Abc { 
    int i = -111; 
    int e; 

    int doSomething() { 
     int a = 10; 
     int b = a + i;  
     int c = b + 100; 

     Abc d = new Abc(); 

     e = b + c + d.a; 

     return e + 1000; 
    } 
} 

và bytecode từ javap -c Abc

Compiled from "Abc.java" 
class Abc { 
    int i; 
    int e; 

    Abc(); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: aload_0 
     5: bipush  -111 
     7: putfield  #2     // Field i:I 
     10: return 

    int doSomething(); 
    Code: 
     0: bipush  10 
     2: istore_1 
     3: iload_1 
     4: aload_0 
     5: getfield  #2     // Field i:I 
     8: iadd 
     9: istore_2 
     10: iload_2 
     11: bipush  100 
     13: iadd 
     14: istore_3 
     15: new   #3     // class Abc 
     18: dup 
     19: invokespecial #4     // Method "<init>":()V 
     22: astore  4 
     24: aload_0 
     25: iload_2 
     26: iload_3 
     27: iadd 
     28: aload   4 
     30: getfield  #2     // Field i:I 
     33: iadd 
     34: putfield  #5     // Field e:I 
     37: aload_0 
     38: getfield  #5     // Field e:I 
     41: sipush  1000 
     44: iadd 
     45: ireturn 
} 

Khi một phương pháp được inovked một không gian bộ nhớ trong ngăn xếp gọi khung hiện được phân bổ

Nếu bạn xem xét kỹ lưỡng việc chuyển nhượng int a=-111; xảy ra trong hàm init ngầm định Abc()!

 int a = -111; 

     5: bipush  -111 
     7: putfield  #2     // Field a:I 

Như lĩnh vực biến e không được gắn bất kỳ giá trị nó sẽ là 0 nếu nguyên thủy hoặc null nếu một tham chiếu Object

Và nếu bạn nhìn vào doSomething()

 int a = 10; 
     0: bipush  10 

cho một địa phương được sử dụng giá trị ban đầu cần được đẩy vào ngăn xếp trong trường hợp này 10. mà không có 'đẩy' này [khởi tạo] giá trị của a không thể truy cập được tới các câu lệnh tiếp theo (vì giá trị không nằm trong ngăn xếp). một khi giá trị được đẩy để ngăn xếp các hoạt động khác như iadd istore, vv được thực hiện trên ngăn xếp

câu lệnh dưới đây thực sự tạo một đối tượng trên không gian heap và gọi phương thức init. Đây là nơi un khởi tạo các biến như 'e' được giá trị mặc định

 15: new   #3     // class Abc 
     18: dup 

tôi để lại tiếp tục so sánh bytecode tối đa bạn;) nhưng tôi hy vọng nó là rõ ràng

+0

Hãy quyết định. Hoặc là "bộ nhớ" cho một biến địa phương được phân bổ khi nó được gán với một giá trị '* hoặc * nó được phân bổ khi phương thức được nhập vào. Không phải cả hai cùng một lúc. Câu lệnh 'ipush' đẩy giá trị 10 mà cửa sổ lệnh 'istore' tiếp theo ngay lập tức vào trong ngăn xếp cho' a'. Việc đẩy không 'tạo' 'a'. – EJP

Các vấn đề liên quan