9 błędów, których należy unikać podczas programowania w Javie
Rafał Sawicki
Java Developer
Jakich błędów należy unikać podczas programowania w Javie? W poniższym artykule odpowiemy na to pytanie.
Java to popularny język o ugruntowanej pozycji w świecie rozwój oprogramowania. Jest to silny i wszechstronny język programowania. Około 3 miliardy urządzeń na całym świecie działa na Java i dlatego popełniono co najmniej 3 miliardy błędów podczas korzystania z niego. W tym artykule skupimy się na tym, jak nie popełniać ich więcej.
1. Uzyskiwanie wyjątku jednoczesnej modyfikacji
To zdecydowanie najczęstszy błąd, z jakim się spotykam. Na początku mojej kariery również popełniałem go wiele razy. Ten błąd pojawia się, gdy próbujesz zmodyfikować kolekcję podczas jej iteracji. The Wyjątek ConcurrentModificationException może również zostać podniesiona podczas pracy z wieloma wątkami, ale na razie skupmy się na podstawowym scenariuszu.
Załóżmy, że masz Kolekcja użytkowników, z których część jest dorosła, a część nie. Twoim zadaniem jest odfiltrowanie dzieci.
for (User : users) {
if (!user.isAdult()) {
users.remove(user);
}
}
Uruchomienie wspomnianego wcześniej kod kończy się uzyskaniem Wyjątek ConcurrentModificationException. Gdzie popełniliśmy błąd? Przed zakończeniem iteracji próbowaliśmy usunąć niektóre elementy. To właśnie spowodowało wyjątek.
Jak mogę tego uniknąć?
Istnieje kilka podejść, które mogą pomóc w takim przypadku. Po pierwsze i najważniejsze, skorzystaj z Java 8's goodness - Strumień.
List adults = users.stream()
.filter(User::isAdult)
.toList();
Korzystanie z Predykat filter, wykonaliśmy odwrotność poprzedniego warunku - teraz określamy elementy do uwzględnienia. Zaletą takiego podejścia jest łatwość łączenia innych funkcji po usunięciu, np. mapa. Ale na litość boską, nie próbuj robić czegoś takiego jak poniżej:
Może również trafić do Wyjątek ConcurrentModificationException ponieważ modyfikujesz źródło strumienia. Może to również powodować dodatkowe wyjątki, które nie będą łatwe do debugowania.
Aby rozwiązać Wyjątek ConcurrentModificationException w scenariuszu jednowątkowym. można również przełączyć się na używanie bezpośrednio Iterator i jego remove() lub po prostu nie usuwać elementów podczas iteracji. Moim zaleceniem jest jednak użycie Strumienie - jest rok 2022.
2. Przechowywanie haseł jako ciągów znaków
Ponieważ coraz bardziej angażuję się w cyberbezpieczeństwo, nie byłbym szczery, gdybym nie wspomniał o co najmniej jednym z nich. Błąd Javy co może prowadzić do naruszenia bezpieczeństwa. Przechowywanie haseł otrzymanych od użytkowników w pliku String Obiekt jest dokładnie tym, czego powinieneś się obawiać.
Problem (a może zaleta) String jest to, że jest niezmienna. W cybernetycznym świecie stwarza to potencjalne zagrożenie, ponieważ nie można wyczyścić wartości raz utworzonego obiektu String obiekt. Atakujący, który uzyska dostęp do pamięci komputera, może znaleźć tam hasła w postaci zwykłego tekstu.
Po drugie, ciągi znaków w Java są internowane przez maszynę JVM i przechowywane w przestrzeni PermGen lub w przestrzeni sterty. Po utworzeniu pliku String obiekt, jest buforowany i usuwany dopiero, gdy Garbage Collector zacznie wykonywać swoją pracę. Nie można mieć pewności, kiedy hasło zostanie usunięte z puli String, ponieważ Garbage Collector działa w sposób niedeterministyczny.
Jak tego uniknąć?
Zalecanym podejściem jest użycie char[] lub, jeszcze lepiej, biblioteka obsługująca przechowywanie haseł jako char[]np.Password4j. The char[] jest zmienna i może być modyfikowana po jej zainicjowaniu. Po przetworzeniu hasła można po prostu usunąć tablicę char[] zapisując w niej losowe znaki. W przypadku, gdy atakujący uzyskają dostęp do pamięci komputera, zobaczą tylko losowe wartości, które nie mają nic wspólnego z hasłami użytkowników.
3. (Nie)obsługa wyjątków
Zarówno początkujący, jak i bardziej zaawansowani programiści nie wiedzą, jak poprawnie obsługiwać wyjątki. Ich głównym grzechem w tej kwestii jest ich ignorowanie. TO NIGDY NIE JEST DOBRE PODEJŚCIE.
Niestety, nie możemy zaoferować rozwiązania, które będzie pasować do każdej sytuacji. Wyjątekscenariusz, z którym się zetkniesz. Każdy przypadek należy rozpatrywać osobno. Możemy jednak dać ci kilka wskazówek, jak zacząć ten temat.
Jak mogę tego uniknąć?
Ignorowanie Wyjąteknigdy nie jest dobrą praktyką. Wyjąteksą dodawane z jakiegoś powodu, więc nie należy ich ignorować.
try {...} catch(Exception e) { log(e); } rzadko jest właściwym podejściem do Wyjątek obsługa.
Rzut Wyjątek, wyświetlić użytkownikowi okno dialogowe błędu lub przynajmniej dodać obszerny komunikat do dziennika.
Jeśli nie obsłużyłeś wyjątków (czego nie powinieneś robić), przynajmniej wyjaśnij to w komentarzu.
4. Używanie wartości null
Niestety, dość często można znaleźć funkcję Java, która w niektórych przypadkach zwraca wartość null. Problem polega na tym, że taka funkcja wymusza na swoim kliencie sprawdzenie wyniku pod kątem wartości null. Bez tego, funkcja Wyjątek NullPointerException jest rzucany.
Inną rzeczą jest przekazanie null wartość. Dlaczego w ogóle o tym pomyślałeś? W takim przypadku funkcja musi wykonać sprawdzenie wartości null. Kiedy korzystasz z bibliotek innych firm, nie możesz zmienić wnętrza funkcji. Co wtedy?
Co ważniejsze, inni programiści, którzy czytają twój kod i widzą, że przekazujesz null prawdopodobnie będzie zdezorientowany, dlaczego wybrałeś tak dziwaczny sposób implementacji swojej funkcji.
Jak mogę tego uniknąć?
Nie zwracaj wartości null wartość! Nigdy! Jeśli twoja funkcja zwraca jakiś typ Kolekcjamożna po prostu zwrócić pusty Kolekcja. Jeśli masz do czynienia z pojedynczymi obiektami, możesz skorzystać z wzorca projektowego obiektu zerowego. Ponieważ Java 8, jest zaimplementowany jako Opcjonalnie. Poza tym, najmniej zalecanym podejściem jest podniesienie Wyjątek.
5. Ciężkie łączenie ciągów
Miejmy nadzieję, że nie popełnisz tego błędu, ponieważ jest to najpopularniejsze (a może drugie po FizzBuzz) pytanie na rozmowie kwalifikacyjnej. Jak powinieneś już wiedzieć, a String jest niezmienny w Java - raz utworzony, nie może być modyfikowany. Tak więc konkatenacja String literałów oznacza dużo niepotrzebnej alokacji pamięci. Łączenie String obiektów za każdym razem wymaga utworzenia tymczasowego StringBuilder i zmieniając go z powrotem na ciąg znaków. Z tego powodu rozwiązanie to absolutnie nie nadaje się do łączenia dużej liczby znaków.
Jak mogę tego uniknąć?
Aby rozwiązać ten problem, użyj StringBuilder. Tworzy to zmienny obiekt, którym można łatwo manipulować. Oczywiście zawsze można użyć StringBuffer jeśli projekt jest używana w kontekście współbieżnym.
6. Nieużywanie istniejących rozwiązań
Podczas tworzenia oprogramowania poznanie podstaw języka, w którym piszemy, jest koniecznością, ale nie jest wystarczające. Wiele problemów algorytmicznych, na które natknąłeś się podczas implementacji nowej funkcji, zostało już rozwiązanych przez kogoś innego. Zbyt wiele razy widziałem, jak ktoś implementował algorytm bezpieczeństwa od zera. Takie podejście jest podatne na błędy. Jedna osoba nie jest w stanie dokładnie przetestować tak złożonego rozwiązania. Zbiorowa wiedza całego zespołu zespół która składa się z średnio zaawansowanych programistów jest prawie zawsze lepsza niż wielkość jednego cudownego dziecka Programista Java. Nie ma potrzeby wymyślania koła na nowo - wystarczy dostosować istniejące rozwiązanie do swoich potrzeb.
Jak mogę tego uniknąć?
Spróbuj wyszukać biblioteki, które zajmują się problemem, nad którym pracujesz. Spróbuj znaleźć podobne rozwiązania. Wiele bibliotek dostępnych w sieci jest darmowych i zostało dopracowanych i przetestowanych przez doświadczonych programistów i całą społeczność Java. Nie bój się z nich korzystać.
7. Brak czasu na pisanie testów
Kuszące jest przekonanie, że nasz kod zawsze będzie działał idealnie. Niepisanie testów dla kodu jest najgorszym grzechem Java programiści. Wielu z nas woli testy manualne i eksploracyjne zamiast testów jednostkowych, co jest szalone. Po co marnować czas na pisanie testów, skoro można skupić się na dostarczaniu najlepszego na świecie kodu dla swojego projektu, który DEFINITYWNIE nie zawiera błędów? Okazuje się, że rzeczywistość jest brutalna i nie możemy dostarczyć wysokiej jakości kodu bez pisania testów.
Jak mogę tego uniknąć?
Powinieneś zawsze przygotowywać testy dla swojego kodu. Wiem, że podejście TDD nie jest łatwe w utrzymaniu, ale powinieneś przynajmniej zapewnić testy, które obejmują wszystkie warunki, w których twój kod może zostać uruchomiony. Obejmuje to testowanie wyjątkowych sytuacji. Testy jednostkowe są niezbędne. Musisz zapewnić je dla każdej funkcji twojego projektu, jeśli chcesz mieć pewność, że twój kod jest łatwy do refaktoryzacji i rozszerzenia w dalszym rozwoju.
Jeszcze jedno. Utrzymuj wysoki standard kodu testowego - będzie warto. To rada wujka Boba i całkowicie się z nią zgadzam.
Co więcej, nie należy zapominać o innych rodzajach testów. Testy integracyjne to rzecz, którą należy rozważyć w każdym projekcie.
8. Zapominanie o modyfikatorach dostępu
Prywatne i publiczne, prawda? Jak możemy o nich zapomnieć. Okazuje się, że jest ich więcej. Kiedy po raz pierwszy zacząłeś się uczyć Javana pewno dowiedziałeś się o modyfikatorach dostępu chronionego. Mogą one być przydatne w niektórych przypadkach, więc warto wiedzieć o ich istnieniu.
Programiści Java często zdają się zapominać o zakresie pakietu. Łatwo jest nie pamiętać o korzystaniu z niego, ponieważ jest on niejawny i nie wymaga żadnego Java słowa kluczowe. Zakres pakietu jest ważny. Umożliwia on testowanie metod chronionych. Elementy chronione są dostępne ze ścieżki klasy testowej, o ile pakiet jest taki sam.
Jak mogę tego uniknąć?
Pamiętaj o modyfikatorze protected i o tym, że zakres pakietu pozwala na jego testowanie.
9. Używanie czystej JavaEE zamiast Spring
Następny krok po nauce Java SE ma nauczyć się działać Java na serwerach, jak stworzyć aplikację klasy korporacyjnej.
Nowicjusze często wpadają w pułapkę nauki JavaEE, ponieważ istnieje ogromna liczba samouczków na ten temat. Nawet "Thinking in Java", książka Programiści Java', wspomina o JavaEE i nie mówi nic o innych opcjach.
Jak mogę tego uniknąć?
JavaEE to pieśń przeszłości. W dzisiejszych czasach Spring jest czymś oczywistym, a Java EE po prostu miło jest mieć. Każda nowoczesna aplikacja na poziomie przedsiębiorstwa korzysta ze Springa, więc powinieneś zdecydowanie rozważyć naukę tutaj.