7 błędów popełnianych przez początkujących programistów Javy!
Jeśli jesteś Junior Java Developerem, musisz zrozumieć pewne koncepty, aby nie popełniać błędów.
Jeśli Java to dla Ciebie coś nowego, to ten artykuł pomoże Ci lepiej zrozumieć pewne niejasne koncepty.
1. Ignorowanie przekazywania przez wartość w Javie
Czy zauważyłeś tutaj błąd?
Tutaj, person
nie będzie wartością null
, ponieważ Java jest przekazywana przez wartość, a nie przez referencję. Co to oznacza?
Można wyróżnić tutaj trzy części, rzeczywisty obiekt person jest tworzony na stosie, następnie referencja, która wskazuje na ten właśnie obiekt person na stosie, a sama referencja ma wartość, która reprezentuje położenie obiektu na stosie, bob to referencja, która wskazuje na obiekt person na stosie, a bob jako referencja ma również pewną wartość reprezentującą położenie obiektu (to jeszcze nie jest dokładny adres).
Tak więc, gdy bob
jest przekazywany jako parametr do metody makeItNull()
, wartość referencji bob jest kopiowana do referencji bob1
, a nie do rzeczywistego obiektu stosu. Gdy bob1
jest przypisywany do wartości null
, wartość referencji bob1
to null
, ale wartość oryginalnej referencji bob
nie zmienia się.
Podczas przekazywania prymitywnych danych, JVM kopiuje wartość prymitywnego typu danych. Jeśli int x=5, to skopiowane zostanie samo 5.
Podczas przekazywania obiektu, wartość referencji jest kopiowana do innej zmiennej, a nie do rzeczywistego obiektu.
I dlatego Java jest uważana za pass-by-value, a nie pass-by-reference.
W językach typu pass-by-reference oryginalny bob
miałby wartość null
.
2. Łamanie kontraktu Hashcode-Equals
Czego tu brakuje?
Jeśli nie nadpiszesz metody hashcode-equals
w klasie Person
, Bob nie będzie tam obecny, nawet jeśli z biznesowego punktu widzenia są to te same obiekty. Dlaczego? HashMap
, HashSet
i Hashtable
identyfikują zduplikowane klucze za pomocą metod hashcode
i equals
.
Podczas przechowywania obiektów, liczba całkowita kodu skrótu jest obliczana za pomocą metody hashcode
, a jeśli mapa zawiera już ten kod skrótu, wówczas metoda equals
zostanie wywołana w celu sprawdzenia, czy obiekty są rzeczywiście takie same, a jeśli są takie same, wówczas wartości zostaną zastąpione.
Podobnie podczas pobierania obiektów, jeśli hashcode()
jest taki sam, wywoływana jest metoda equals( )
w celu sprawdzenia, czy jest to ten sam klucz, a jeśli tak, zwracana jest wartość.
Tutaj, podczas przechowywania obiektów, ponieważ metoda hashcode()
i equals()
nie jest nadpisana, zostanie wywołana metoda hashcode
i equals
klasy Object
Metoda hashcode
klasy Object
zwraca inną wartość dla każdego nowego obiektu w JVM.
Metoda equal
klasy Object
porównuje referencje.
Tak więc kod skrótu dla bob i bob1 będzie inny. Hash bob1
nie będzie obecny na mapie.
Pamiętaj o prostym kontrakcie:
Jeśli metoda equals
jest nadpisana w jakiejś klasie,
- musisz nadpisać metodę
hashcode
w taki sposób, że jeśli metodaequals
zwróci wartośćtrue
dla dwóch obiektów, to metodahashcode
musi zwrócić tę samą liczbę całkowitą dla tych obiektów. - jeśli obiekty nie są takie same za pomocą metody
equals
, to mogą, ale nie muszą zwrócić ten sam kod skrótu.
Nawet jeśli nadpisałeś hashcode-equals
, nie jest dobrą praktyką używanie zmiennych obiektów jako kluczy, ponieważ później, jeśli klucze zostaną zmodyfikowane, nie będzie można ich odzyskać, ponieważ hashcode-equals
zmieni wyniki z powodu modyfikacji.
3. Modyfikowanie kolekcji podczas iteracji
- Jeśli spróbujesz dodać lub usunąć obiekty podczas iteracji używając iteratora techniki fail-fast, otrzymasz
ConcurrentModificationException
. - Iterator typu
fail-fast
działa na oryginalnej kolekcji i używa wewnętrznej flagimodCount
, aby sprawdzić, czy w kolekcji zaszły jakiekolwiek zmiany strukturalne. Jeśli tak, wykonanie nie powiedzie się.
Rozwiązanie:
- można użyć bezpiecznych dla wątków odpowiedników kolekcji j
ava.util.concurrent.Copy OnWriteArrayList
lubjava.util.concurrent.CopyOnWriteArraySet
, w których iterator używa kopii oryginalnej tablicy do przechodzenia i dokonuje modyfikacji oryginalnej tablicy. - Można również użyć
ConcurrentHashMap
, chociaż nie używa ona kopii oryginalnej kolekcji do przechodzenia. Nie posiada implementacji typu fail-fast.
4. Niezamykanie zasobów systemowych
- Aplikacja Javy wykorzystuje kilka rodzajów zasobów, takich jak pliki, strumienie, porty i połączenia z bazami danych. Musimy zadbać o to, by były one udostępniane nawet w przypadku błędów.
- Ponieważ każda aplikacja ma ograniczoną liczbę przypisanych zasobów, ich nadużywanie prowadzi do wielokrotnego ponownego uruchamiania aplikacji i wpływania na inne aplikacje w tym samym środowisku.
Jak zamknąć zasoby?
Możesz użyć bloku finally
lub bloku try-with-resource
wprowadzonego w Javie 7. Podczas korzystania z try-with-resource
można zadeklarować wiele zasobów, a każdy zasób musi implementować interfejs java.io.AutoCloseable
, który ma metodę close()
określającą sposób zamykania zasobów. Metoda ta będzie zawsze wywoływana przez JVM po pomyślnym zakończeniu bloku try lub po wystąpieniu wyjątku.
5. Modyfikowanie niezmiennych obiektów
- Tutaj
Arrays.asList()
daje nam niezmienną listę, do której próbujemy dodać kolejną nazwę, dlatego otrzymamy tutajUnsupportedOperationException
. - Stan niezmiennych obiektów nie może i nie powinien być zmieniany. Jeśli jest to wymagane, sam obiekt nie powinien być niezmienny.
6. Widoczność zmiennej w środowisku wielowątkowym
Czy uważasz, że MyThread
zawsze będzie w tym miejscu?
- Jeśli metoda
stop( )
jest wykonywana przez inny wątek, możliwe, że wartośćtrue
zmiennej stop nigdy nie będzie widoczna dlaMyThread
, jeśli cache’ował on wartość zmiennej. - Jeśli zmienna jest współdzielona przez wiele wątków, zmiany wprowadzone przez jeden wątek mogą, ale nie muszą być widoczne dla drugiego wątku. Dlaczego?
- Gdy wątek próbuje zmodyfikować wartość zmiennej, w zależności od architektury procesora, wartość zmiennej może zostać skopiowana do lokalnej pamięci podręcznej rdzenia procesora zamiast zawsze odczytywać ją z pamięci głównej w celu zwiększenia wydajności, co nie będzie widoczne dla innego wątku pracującego nad nią w tym samym czasie.
Rozwiązanie:
Użycie modyfikatora volatile
gwarantuje więc, że wątki zawsze będą widzieć zaktualizowane wartości.
7. Używanie == zamiast equals do porównywania obiektów
- Tutaj
bob
ibobAgain
odnoszą się do różnych obiektów na stosie, stąd porównanie referencji(==)
zwrócifalse
i zostanie wykonany blokelse
. Zwróci on wartość true tylko wtedy, gdy oba punkty odniesienia wskazują na ten sam obiekt w pamięci. - metoda
equals(other)
sprawdza rzeczywistą zawartość obiektu. Oczywiście zależy to od sposobu nadpisania metodyequals
.
Wyliczenia są domyślnie singleton w Javie, więc wszystkie odniesienia do tego samego wyliczenia będą wskazywać na ten sam obiekt, a zatem porównanie referencyjne działa dla wyliczeń.
w takim więc przypadku blok If
zostanie wykonany
To już chyba wszystko. Jeśli podobał Ci się artykuł, zaobserwuj mnie po więcej treści! I koniecznie sprawdź mój artykuł na temat Java Streams!
Dzięki!
Oryginał tekstu w języku angielskim przeczytasz tutaj.