2013-08-14 12 views
20

Dlaczego zmienne zadeklarowane w klasie mają wartości domyślne, ale zmienne zadeklarowane w metodach, o których mówi się, że są "zmiennymi lokalnymi", nie mają wartości domyślnych w Javie?Dlaczego zmienne instancji mają wartości domyślne w java?

Na przykład

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); 
    } 
} 

W tym powyższym przykładzie zmiennej a ma domyślną wartość 0, ale zmienna b daje błąd, że nie mogła zostać zainicjowana.

+14

'int A' rzeczywiście ma wartość domyślną' 0 '. – Vulcan

+1

cały blok pamięci obiektu jest zawsze wypełniony zerami, dlatego wszystkie zmienne w obiekcie mają domyślnie wartość 0, "b" jest zmienną lokalną i po prostu nie jest inicjowana w tej części. jest inicjowany po przypisaniu wartości – x4rf41

+1

, ale zmienna y b ma domyślną wartość? –

Odpowiedz

12

Wszystkie zmienne składowe muszą zostać załadowane do sterty, więc muszą być zainicjowane wartościami domyślnymi podczas tworzenia instancji klasy. W przypadku zmiennych lokalnych nie są one ładowane do sterty, są przechowywane w stosie, dopóki nie zostaną użyte przed wersją Java 7, więc musimy je jawnie zainicjować. Teraz "Java Hotspot Compiler serwera" wykonuje "analizę ucieczki" i postanawia przydzielić niektóre zmienne na stosie zamiast sterty.

+0

Zmienne lokalne załadowane do sterty? Czy zauważyłeś, że OP mówi o * int *, który jest typem pierwotnym? o.O –

+0

On bierze "int" tylko jako przykład. – Ashwani

+0

Nadal, jeśli masz lokalną zmienną 'Object o', * referencja * nadal żyje w stosie, a nie w stercie. Jeśli nie zainicjujesz 'o', jest to błąd, aby go użyć - problem polega na użyciu * reference * bez inicjalizacji. Jeśli po prostu wykonasz 'o = null', możesz go użyć: zainicjalizowałeś referencję, która znowu żyje w stosie (nie w stercie) i po tej inicjalizacji nie ma nic" załadowanego do sterty ". Jeśli zrobisz 'o = new Object()', to dotknąłeś sterty, ale to samo nie ma nic wspólnego z tym, że nie możesz użyć niezainicjowanych zmiennych lokalnych. –

2

zmienne lokalne inicjalizacji

Zmienne zadeklarowane w metodach iw blokach nazywane są zmienne lokalne. Zmienna lokalna nie jest inicjowana, gdy są tworzone podczas wywoływania metody. Dlatego zmienna lokalna musi zostać zainicjowana jawnie przed użyciem. W przeciwnym razie kompilator zgłosi go jako błąd, gdy wykonywana jest metoda lub blok zawierający.

przykład:

public class SomeClassName{ 

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

Kompilator twierdzi, że lokalna zmienna TOTAL w println rachunku w punkcie (1) może być zainicjowana. Inicjowanie zmiennej lokalnej sumie przed użyciem rozwiązuje problem:

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 inicjalizacji

przypadku braku inicjalizacji przewidzianego dla instancji lub zmiennej statycznej, albo gdy zadeklarowana lub w bloku inicjatora, to jest niejawnie zainicjalizowane z domyślną wartością typu. Zmienna instancji jest inicjowana z domyślną wartością jej typu za każdym razem, gdy tworzona jest klasa, czyli dla każdego obiektu utworzonego z klasy. Zmienna statyczna jest inicjowana z domyślną wartością jej typu, gdy klasa jest ładowana po raz pierwszy.

+2

Ale pytanie wciąż pozostaje bez odpowiedzi.Nawet jeśli zmienne lokalne są przechowywane na stosie, dlaczego nie są przypisane wartości domyślne. –

+2

Patrz Specyfikacja języka Java: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5 Cytat: "Zmienna lokalna (§14.4, §14.14) musi zostać jawnie nadana wartość przed jej użyciem, poprzez inicjalizację (§ 14.4) lub przypisanie (§15.26), w sposób, który można zweryfikować za pomocą reguł dla określonego przypisania (§16). " –

+0

@Ankur Spójrz na ostatnią część --- Inicjalizacja pól – Dileep

0

tl; dr: To było mniej lub bardziej arbitralnym wyborem

Jeśli o mnie chodzi, to była pomyłka, że ​​Java ma wartości domyślne dla zmiennych instancji. Kompilator powinien zmusić programistę do zainicjowania go wcześniej, tak jak ma to miejsce w przypadku zmiennych lokalnych.

Uzasadnieniem domyślnych wartości jest bezpieczeństwo. Kiedy obiekt zostanie utworzony, zostanie przydzielony fragment pamięci dla obiektu, który zawiera wskazania zmiennych instancji itp. Projektanci Javy uznali, że dobrym pomysłem będzie wyczyszczenie tej części pamięci za pomocą zer i wartości null. W ten sposób nigdy nie będziesz czytał śmieci, które się tam znajdowało, zanim obiekt został przydzielony. Mogli wymusić inicjalizację; nie ma nic podstawowego w wyborze.Prawdopodobnie ułatwiło to wdrożenie i nadało sens projektantom Javy.

W przypadku zmiennych lokalnych, projektanci zdecydowali się na wymuszenie inicjalizacji (a może dokładniej jest powiedzieć, że nie podjęli żadnej inicjalizacji, gdy deklarowana jest tylko zmienna lokalna, a zatem najbardziej logiczne zachowanie kompilatora było wymuszenie inicjalizacji zmiennej przed użyciem).

+0

Kompilator nie może wiedzieć, kiedy członek został użyty przed przypisaniem. Kompilator nie wie, która metoda klasy zostanie wywołana jako pierwsza. Ta sama analiza metody z drugiej strony jest oczywiście możliwa, ponieważ istnieje tylko jeden punkt wejścia. – EJP

+0

@EJP Kotlin wydaje się robić całkiem dobrą robotę ... –

+0

Java nie musi być tak inteligentna. – weakish

1

Ponieważ zmienne lokalne są przydzielane na stosie, fragment pamięci dla zmiennej lokalnej jest alokowany, gdy jest przypisany z wartością.

Weźmy prosty przykład

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; 
    } 
} 

i kodu bajtowego z 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 
} 

Gdy metoda jest inovked przestrzeń pamięci w stosie nazwie prąd rama przeznaczono

Jeśli spójrz uważnie nawet na int a=-111; przypisanie odbywa się w niejawnej funkcji init Abc()!

 int a = -111; 

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

W polu Zmienna e nie przypisano żadnej wartości to będzie 0 jeżeli prymitywny lub null, jeśli obiekt odniesienia

A jeśli spojrzeć na doSomething()

 int a = 10; 
     0: bipush  10 

dla lokalnej być w tym przypadku wartość początkowa musi zostać przesunięta do stosu 10. bez tej "wypychania" [inicjalizacja] wartość a nie jest dostępna dla kolejnych instrukcji (ponieważ wartość nie znajduje się na stosie). po przesunięciu wartości w celu ułożenia stosu innych operacji, takich jak iadd istore itd., są one wykonywane na stosie, a poniższe instrukcje faktycznie tworzą obiekt na obszarze sterty i wywołują metodę init. To gdzie un zainicjowany zmienne, takie jak „e” pobiera wartości domyślne

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

zostawiam dalszego porównania kodu bajtowego zapisu do Ciebie;), ale mam nadzieję, że jest jasne

+0

Uzupełnij zdanie. Albo "porcja pamięci" dla zmiennej lokalnej jest przydzielana, gdy jest przypisana z wartością "* lub * jest alokowana, gdy metoda jest wprowadzona. Nie oboje w tym samym czasie. Instrukcja "ipush" przesyła wartość 10, która zaraz po instrukcji "istore" zapisuje w szczelinie stosu dla 'a'. Push nie "tworzy" 'a'. – EJP

Powiązane problemy