Czy był jakiś powód, dla którego projektanci Java uznali, że zmiennym lokalnym nie należy nadawać wartości domyślnej? Poważnie, jeśli zmiennym instancji można nadać wartość domyślną, to dlaczego nie możemy zrobić tego samego dla zmiennych lokalnych?
Prowadzi to również do problemów, jak wyjaśniono w tym komentarzu do wpisu na blogu :
Cóż, ta zasada jest najbardziej frustrująca, gdy próbuje się zamknąć zasób w ostatecznym bloku. Jeśli utworzę wystąpienie zasobu podczas próby, ale spróbuję go zamknąć w końcu, pojawia się ten błąd. Jeśli przenoszę instancję poza try, pojawia się kolejny błąd informujący, że a musi znajdować się w try.
Bardzo frustrujące.
java
variables
initialization
Shivasubramanian A
źródło
źródło
Odpowiedzi:
Zmienne lokalne są deklarowane głównie w celu wykonania pewnych obliczeń. Więc to decyzja programisty o ustawieniu wartości zmiennej i nie powinna ona przyjmować wartości domyślnej. Jeśli programista przez pomyłkę nie zainicjował zmiennej lokalnej i przyjęła ona wartość domyślną, wówczas na wyjściu mogłaby pojawić się nieoczekiwana wartość. Tak więc w przypadku zmiennych lokalnych kompilator poprosi programistę o zainicjowanie z pewną wartością, zanim uzyska dostęp do zmiennej, aby uniknąć użycia niezdefiniowanych wartości.
źródło
„Problem”, do którego prowadzi link, wydaje się opisywać tę sytuację:
Komentator skarży się, że kompilator wzdraga się przed linią w
finally
sekcji, twierdząc, żeso
może nie być zainicjowany. W komentarzu wspomniano wówczas o innym sposobie pisania kodu, prawdopodobnie mniej więcej tak:Komentator jest niezadowolony z tego rozwiązania, ponieważ kompilator mówi następnie, że kod „musi być w trakcie próby”. Myślę, że oznacza to, że część kodu może zgłosić wyjątek, który nie jest już obsługiwany. Nie jestem pewny. Żadna wersja mojego kodu nie obsługuje żadnych wyjątków, więc wszystko związane z wyjątkami w pierwszej wersji powinno działać tak samo w drugiej.
W każdym razie ta druga wersja kodu jest poprawnym sposobem jej zapisania. W pierwszej wersji komunikat o błędzie kompilatora był poprawny.
so
Zmienna może być niezainicjowanymi. W szczególności, jeśliSomeObject
konstruktor zawiedzie,so
nie zostanie zainicjowany, więc próba wywołania będzie błędemso.CleanUp
. Zawsze wchodź dotry
sekcji po zdobyciu zasobu, któryfinally
sekcja finalizuje.try
-finally
blok poso
inicjalizacji jest nie tylko ochronaSomeObject
przykład, aby upewnić się, że zostanie oczyszczony bez względu na to, co jeszcze się dzieje. Jeśli są inne rzeczy, które muszą zostać uruchomione, ale nie są one związane z tym, czySomeObject
instancja została przydzielona jako właściwość, powinny przejść do innegotry
-finally
bloku, prawdopodobnie takiego, który zawija ten, który pokazałem.Wymaganie ręcznego przypisywania zmiennych przed użyciem nie prowadzi do prawdziwych problemów. Prowadzi to tylko do drobnych problemów, ale twój kod będzie do tego lepszy. Będziesz mieć zmienne o bardziej ograniczonym zakresie i
try
-finally
bloki, które nie próbują chronić zbytnio.Gdyby zmienne lokalne miały wartości domyślne, to
so
w pierwszym przykładzie byłybynull
. To naprawdę niczego by nie rozwiązało. Zamiast pojawiać się wfinally
bloku błąd kompilacji , mógłbyś tamNullPointerException
czaić się, który mógłby ukryć każdy inny wyjątek, który mógłby wystąpić w sekcji kodu „Wykonaj jakąś pracę”. (A może wyjątki wfinally
sekcjach są automatycznie łączone z poprzednim wyjątkiem? Nie pamiętam. Mimo to miałbyś dodatkowy wyjątek na drodze prawdziwego).źródło
Ponadto w poniższym przykładzie wyjątek mógł zostać zgłoszony wewnątrz konstrukcji SomeObject, w którym to przypadku zmienna „so” miałaby wartość null, a wywołanie CleanUp wyrzuci wyjątek NullPointerException
Zwykle robię to:
źródło
Zauważ, że końcowe zmienne instancji / składowe nie są domyślnie inicjowane. Ponieważ są one ostateczne i nie można ich później zmienić w programie. To jest powód, dla którego Java nie podaje dla nich żadnej wartości domyślnej i zmusza programistę do jej zainicjowania.
Z drugiej strony, inne zmienne składowe można później zmienić. Dlatego kompilator nie pozwala im pozostać niezainicjowanymi, właśnie dlatego, że można je później zmienić. Jeśli chodzi o zmienne lokalne, zakres zmiennych lokalnych jest znacznie węższy. Kompilator wie, kiedy jest używany. Dlatego wymuszenie na programatorze zainicjowania zmiennej ma sens.
źródło
Właściwa odpowiedź na twoje pytanie jest taka, że zmienne metody są tworzone przez proste dodanie liczby do wskaźnika stosu. Wyzerowanie ich byłoby dodatkowym krokiem. W przypadku zmiennych klasowych są one umieszczane w zainicjowanej pamięci na stercie.
Dlaczego nie zrobić dodatkowego kroku? Cofnij się o krok - nikt nie wspomniał, że „ostrzeżenie” w tym przypadku jest bardzo dobrą rzeczą.
Nigdy nie powinieneś inicjalizować swojej zmiennej na zero lub null w pierwszym przebiegu (podczas pierwszego kodowania). Albo przypisz go do rzeczywistej wartości, albo w ogóle go nie przypisuj, ponieważ jeśli tego nie zrobisz, java może ci powiedzieć, kiedy naprawdę schrzaniłeś. Weźmy odpowiedź Electric Monk jako świetny przykład. W pierwszym przypadku zadziwiająco przydatne jest to, że mówi ci, że jeśli metoda try () nie powiedzie się, ponieważ konstruktor SomeObject rzucił wyjątek, to ostatecznie otrzymasz NPE w pliku. Jeśli konstruktor nie może zgłosić wyjątku, nie powinien znajdować się w try.
To ostrzeżenie jest niesamowitym wielościeżkowym narzędziem do sprawdzania złego programisty, który uchronił mnie przed robieniem głupich rzeczy, ponieważ sprawdza każdą ścieżkę i upewnia się, że jeśli użyłeś zmiennej w jakiejś ścieżce, musisz ją zainicjalizować w każdej ścieżce, która do niej prowadzi . Teraz nigdy jawnie nie inicjalizuję zmiennych, dopóki nie stwierdzę, że jest to właściwe.
Poza tym, czy nie lepiej jest powiedzieć wprost „int size = 0” niż „int size” i zmusić następnego programistę do zorientowania się, że zamierzasz wynosić zero?
Z drugiej strony nie mogę wymyślić ani jednego ważnego powodu, aby kompilator zainicjował wszystkie niezainicjowane zmienne na 0.
źródło
Myślę, że głównym celem było zachowanie podobieństwa z C / C ++. Jednak kompilator wykrywa i ostrzega przed użyciem niezainicjowanych zmiennych, co ograniczy problem do minimum. Z punktu widzenia wydajności nieco szybciej można zadeklarować niezainicjowane zmienne, ponieważ kompilator nie będzie musiał pisać instrukcji przypisania, nawet jeśli nadpiszesz wartość zmiennej w następnej instrukcji.
źródło
(Opublikowanie nowej odpowiedzi tak długo po zadaniu pytania może wydawać się dziwne, ale pojawił się duplikat ).
Dla mnie przyczyna sprowadza się do tego: Cel zmiennych lokalnych jest inny niż cel zmiennych instancji. Zmienne lokalne mają być używane jako część obliczeń; Zmienne instancji mają zawierać stan. Jeśli używasz zmiennej lokalnej bez przypisywania jej wartości, prawie na pewno jest to błąd logiczny.
To powiedziawszy, mogłem całkowicie zostać w tyle, wymagając, aby zmienne instancji były zawsze jawnie inicjalizowane; błąd wystąpiłby w każdym konstruktorze, którego wynik zezwala na niezainicjowaną zmienną instancji (np. niezainicjowana w deklaracji, a nie w konstruktorze). Ale to nie jest decyzja Gosling, et. al., wziął się na początku lat 90-tych, więc oto jesteśmy. (I nie mówię, że wykonali zły telefon.)
Nie mogłem jednak wyjść poza domyślne zmienne lokalne. Tak, nie powinniśmy polegać na kompilatorach, aby podwójnie sprawdzać naszą logikę, a nie jest to możliwe, ale nadal jest to przydatne, gdy kompilator złapie jedną. :-)
źródło
Bardziej efektywne jest nie inicjowanie zmiennych, aw przypadku zmiennych lokalnych jest to bezpieczne, ponieważ inicjalizację może śledzić kompilator.
W przypadkach, gdy potrzebujesz zainicjalizować zmienną, zawsze możesz to zrobić samodzielnie, więc nie stanowi to problemu.
źródło
Ideą zmiennych lokalnych jest to, że istnieją one tylko w ograniczonym zakresie, dla którego są potrzebne. W związku z tym nie powinno być powodów do niepewności co do wartości lub przynajmniej skąd ta wartość pochodzi. Mogłem sobie wyobrazić wiele błędów wynikających z posiadania domyślnej wartości dla zmiennych lokalnych.
Na przykład rozważmy następujący prosty kod ... ( Uwaga: załóżmy dla celów demonstracyjnych, że zmiennym lokalnym przypisywana jest wartość domyślna, jak określono, jeśli nie została ona jawnie zainicjowana )
Kiedy wszystko jest powiedziane i zrobione, zakładając , że kompilator przypisał domyślną wartość „\ 0” do letterGrade , ten kod, tak jak napisano, działałby poprawnie. A co by było, gdybyśmy zapomnieli o stwierdzeniu else?
Uruchomienie testowe naszego kodu może spowodować następujące skutki
Taki wynik, choć można się było spodziewać, z pewnością nie był zamiarem programisty. Rzeczywiście, prawdopodobnie w zdecydowanej większości przypadków (lub przynajmniej w znacznej ich liczbie) wartość domyślna nie byłaby wartością pożądaną , więc w zdecydowanej większości przypadków wartość domyślna powodowałaby błąd. Bardziej sensowne jest wymuszenie na koderze przypisania wartości początkowej do zmiennej lokalnej przed jej użyciem, ponieważ problemy związane z debugowaniem spowodowane zapomnieniem o
= 1
infor(int i = 1; i < 10; i++)
znacznie przewyższają wygodę związaną z brakiem konieczności dołączania= 0
infor(int i; i < 10; i++)
.Prawdą jest, że bloki try-catch-final mogą się trochę zabrudzić (ale tak naprawdę nie jest to catch-22, jak wydaje się sugerować cytat), gdy na przykład obiekt zgłasza sprawdzony wyjątek w swoim konstruktorze, ale dla jednego czy z innego powodu, na końcu bloku trzeba coś zrobić z tym obiektem. Doskonałym tego przykładem jest sytuacja z zasobami, które należy zamknąć.
Jednym ze sposobów radzenia sobie z tym w przeszłości może być taki ...
Jednak od wersji Java 7 ta ostateczna blokada nie jest już konieczna przy użyciu zasobów próbnych.
To powiedziawszy (jak sugeruje nazwa) działa to tylko z zasobami.
I chociaż poprzedni przykład jest trochę obrzydliwy, to prawdopodobnie bardziej mówi o sposobie implementacji try-catch-final lub tych klas niż o zmiennych lokalnych i ich implementacji.
Prawdą jest, że pola są inicjowane do wartości domyślnej, ale jest to trochę inne. Kiedy mówisz, na przykład,
int[] arr = new int[10];
gdy tylko zainicjujesz tę tablicę, obiekt istnieje w pamięci w podanej lokalizacji. Załóżmy na chwilę, że nie ma wartości domyślnych, ale zamiast tego wartość początkowa jest dowolną serią jedynek i zer, która znajduje się w danej chwili w tej lokalizacji pamięci. W wielu przypadkach może to prowadzić do niedeterministycznych zachowań.Załóżmy, że mamy ...
Byłoby całkowicie możliwe, że
Same.
mogłoby to zostać wyświetlone w jednym przebiegu iNot same.
w innym. Problem może stać się jeszcze poważniejszy, gdy zaczniesz mówić o zmiennych referencyjnych.Zgodnie z definicją, każdy element s powinien wskazywać na String (lub jest pusty). Jeśli jednak wartość początkowa jest dowolną serią 0 i 1, która zdarzy się w tej lokalizacji pamięci, nie tylko nie ma gwarancji, że za każdym razem otrzymasz te same wyniki, ale także nie ma gwarancji, że obiekt s [0] wskazuje to (zakładając, że wskazuje na coś znaczącego) nawet jest Stringiem (być może jest to Królik,: p )! Ten brak troski o typ byłby sprzeczny z prawie wszystkim, co czyni Java Java. Tak więc, chociaż posiadanie wartości domyślnych dla zmiennych lokalnych może być w najlepszym przypadku postrzegane jako opcjonalne, posiadanie wartości domyślnych, na przykład zmiennych, jest bliższe konieczności .
źródło
jeśli się nie mylę, może być inny powód
Podanie domyślnej wartości zmiennych składowych jest częścią ładowania klasy
Ładowanie klasy jest czynnością wykonywaną w Javie, co oznacza, że podczas tworzenia obiektu klasa jest ładowana z ładowaniem klasy tylko zmienne składowe są inicjowane z wartością domyślną JVM nie zajmuje czasu, aby nadać domyślną wartość zmiennym lokalnym, ponieważ niektóre metody nigdy nie będą wywoływane, ponieważ wywołanie metody może być warunkowe, więc po co poświęcać czas na nadanie im wartości domyślnej i zmniejszyć wydajność, jeśli te wartości domyślne nigdy nie będą używane.
źródło
Eclipse ostrzega nawet przed niezainicjowanymi zmiennymi, więc i tak staje się to dość oczywiste. Osobiście uważam, że to dobrze, że jest to zachowanie domyślne, w przeciwnym razie twoja aplikacja może używać nieoczekiwanych wartości, a zamiast rzucać błąd kompilator nic nie zrobi (ale być może da ostrzeżenie) i wtedy będziesz drapać zastanawiasz się, dlaczego pewne rzeczy nie zachowują się tak, jak powinny.
źródło
Zmienne lokalne są przechowywane na stosie, ale zmienne instancji są przechowywane na stercie, więc są pewne szanse, że zostanie odczytana poprzednia wartość na stosie zamiast wartości domyślnej, jak to ma miejsce w stercie. Z tego powodu jvm nie pozwala na użycie zmiennej lokalnej bez jej zainicjowania.
źródło
Zmienna instancji będzie miała wartości domyślne, ale zmienne lokalne nie mogą mieć wartości domyślnych. Ponieważ zmienne lokalne są zasadniczo w metodach / zachowaniu, ich głównym celem jest wykonanie pewnych operacji lub obliczeń. Dlatego nie jest dobrym pomysłem ustawianie wartości domyślnych dla zmiennych lokalnych. W przeciwnym razie sprawdzenie przyczyn nieoczekiwanych odpowiedzi jest bardzo trudne i czasochłonne.
źródło
Odpowiedź brzmi: zmienne instancji można zainicjować w konstruktorze klasy lub dowolnej metodzie klasowej, ale w przypadku zmiennych lokalnych, po zdefiniowaniu w metodzie cokolwiek, co pozostaje na zawsze w klasie.
źródło
Mógłbym wymyślić dwa powody
źródło