7 błędów popełnianych przez początkujących programistów Javy!
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.