14

Natknąłem się na zachowanie, o którym wcześniej nie wiedziałem, w kodzie śledzenia.Różnica w zachowaniu: "Null" inicjalizowany końcowy element statyczny i "null" inicjowana ostateczna zmienna lokalna

Rozważmy 1 st sprawy:

public static void main(String[] args) {  
    final String str = null; 
    System.out.println(str.length()); // Compiler Warning: NullPointerAccess 
} 

Zgodnie z oczekiwaniami, kompilator pokazuje mi następujące ostrzeżenie na str będąc zerowy - Null dostęp wskazówka: Zmienna str może być tylko wartość null w tym Lokalizacja.

Teraz, kiedy przeszło zmienną static final pole inicjowane zerową:

class Demo { 
    static final String str = null; 

    public static void main(String[] args) { 
     System.out.println(str.length()); // No Compiler Warning 
    } 
} 

Teraz kompilator nie wykazuje żadnego ostrzeżenia. AFAIK, kompilator powinien wiedzieć, że str jest ostateczny, nie zmieni jego wartości w żadnym miejscu kodu. A biorąc pod uwagę, że jest to null, z pewnością wyniknie później NullPointerException, co robi.

Mimo że kompilator skutecznie ostrzega mnie o tym w pierwszym przypadku, dlaczego nie może zidentyfikować tego w drugim przypadku. Dlaczego ta zmiana zachowania? Zachowanie jest takie samo, jeśli zmienię pole static na instance i uzyskam do niego dostęp za pomocą instancji Demo.

Myślałem, że to zachowanie mogło zostać określone w JLS, więc przeszedłem przez temat Definite Assignment, ale nie znalazłem nic związanego z tym problemem. Czy ktoś może wyjaśnić zmianę zachowania? Szukam jakiegoś mocnego punktu z jakimś łączem do JLS, jeśli to możliwe?

Oprócz, że dlaczego kompilator pokazuje mi tylko ostrzeżenie w pierwszej kolejności, jak myślę, że z tego samego powodu ja podanej powyżej, metoda wywołanie pewnością rzucać NPE w czasie wykonywania, ponieważ pole może” t być zmienione? Dlaczego nie wyświetlił mi się błąd kompilatora? Czy oczekuję zbyt wiele od kompilatora, ponieważ wydaje się dość oczywiste, że wynik runtime w postaci str.length() nie może być równy NPE?


Niestety dla brakujących że wcześniej:

Używam Eclipse Juno na Ubuntu 12.04 z OpenJDK 7.

+7

JLS nie * zwykle * zagłębia się w szczegóły ostrzeżeń - jest to szczegół implementacji kompilatora, nie byłbym zaskoczony, gdyby niektóre kompilatory * ostrzeżenia * o tym ostrzeżono: –

+0

@JonSkeet. każdy kompilator zachowuje się w taki sam sposób w obu przypadkach.W obu przypadkach uzyskuję dostęp do metody na określonym zerowym odwołaniu –

+0

Tak, byłoby miło to zrobić - ale mówię, że to kwestia Daleko idą implementacje kompilatora, nie oczekiwałbym, że będzie w JLS, a ty nie powiesz, który kompilator używasz, –

Odpowiedz

2

Wow! Okazało się, że był to problem specyficzny dla eclipse. Kiedy skompilowałam kod przy użyciu:

javac -Xlint:all Demo.java 

nie wyświetlał żadnego ostrzeżenia dla żadnego przypadku. Więc wróciłem do zaćmienia, aby sprawdzić wszelkie ustawienia włączone dla tej sprawy i znalazłem je.

W systemu Windows ->Preferencje ->Java ->kompilatora ->Błędy/ostrzeżenia, pod Null Analiza mogę zmienić sposób Null Pointer Dostęp powinny być traktowane przez Eclipse Compiler. Mogę ustawić na - Ignoruj ​​, Błąd lub Ostrzeżenie.

A teraz wydaje się zupełnie głupie pytanie. Wstyd mi. :(

1

W tym przypadku str jest zmienną lokalną i kompilator nie skompiluje się, jeśli spróbujesz wykonać na nim jakiekolwiek operacje przed jej zainicjowaniem. Analiza przepływu jest zupełnie inna, sprawdzi przepływ kodu, w którym wykryje, że w miejscu, w którym wykonywana jest operacja length(), zmienna lokalna str może mieć wartość zerową.

W tym przypadku str jest zmienną instancji i będzie mieć wartość NULL, nawet jeśli nie zostanie to wyraźnie przypisane.

Nadal czemu nie ma tu żadnego ostrzeżenia?

Można zainicjować zmienną instancji w konstruktorze. Lub możesz wywołać na nim metodę ustawiającą, zanim wywołasz na nim operację length(). Więc ucieka z analizy przepływu (kompilator nie jest pewien, czy zmienna instancji będzie w tym punkcie pusta, czy nie, ale w pierwszym przypadku kompilacja jest pewna, że ​​zmienna lokalna zawsze będzie miała wartość NULL).

+0

Kupiłbym to w przypadku pól nieostatecznych, w którym to przypadku zachowanie będzie się różnić w dwóch przypadkach. Ale biorąc pod uwagę obie są ostateczne zmienne, żadna z nich nie może być zmieniona po inicjalizacji. Tak więc, nie widzę, jak kompilator nie może zidentyfikować zerowego dostępu w jednym przypadku, podczas gdy w drugim może. –

+1

Myślę, że nie zauważyłeś, że pole to "ostateczny statyczny". Nie możesz go zainicjować w konstruktorze, ani w żadnym ustawniku? –

+0

Tak, nie widziałem ostatecznego!Nie wiem dokładnie, jak działa wykrywanie przepływu w języku Java, ale przypuszczam, że nie obsługuje twojej sprawy. Gdyby nie był statyczny i końcowy, mógłby zostać zainicjowany w konstruktorze, a gdyby był tylko statyczny, to w jakimś statycznym bloku. Twoja sprawa jest wyjątkowa i domyślam się, że nie jest obsługiwana. –

2

Nie jestem pewien w 100%, ale w drugim przypadku, gdy masz pole ostateczne względem zmiennej lokalnej, możliwe jest przyporządkowanie do tego pola końcowego pewnej wartości bezpośrednio w statycznej (lub bloku instancji zależnie od tego czy zmienna jest statyczna czy nie) blok inicjujący:

class Demo { 
... 
static { 
str = "some text"; 
} 
... 
} 

więc kompilator nie daje ostrzeżenia.

+1

Nie, jeśli jest już zainicjowany. –

Powiązane problemy