10.05.20225 min

Dave AndreaComputer Programmer

Dlaczego Java jest głupia

Sprawdź, dlaczego Java jest głupia i frustrująca. Czy to efekt złożoności, dużych i szybkich aplikacji, wielowątkowości czy może kompatybilności krzyżowej i wstecznej?

Dlaczego Java jest głupia

Dla jasności: Jest to artykuł z przymrużeniem oka, celowo nieco przesadzony humorem, przeznaczony dla początkujących użytkowników Javy, dla których złożoność tego języka jest frustrująca i/lub przerażająca.

Mój domniemany wniosek jest taki, że Java nie jest głupia, wbrew temu co widzicie w tytule, ponieważ jej złożoność jest zamierzona i stanowi część tego, co czyni ją tak silną. Więcej szczegółów przeczytasz w moim komentarzu w odpowiedziach.


Czy zadałeś sobie któreś z poniższych pytań:

  • Dlaczego Java tak mąci nam w głowie?
  • Dlaczego w Javie potrzeba tak dużo kodu, aby osiągnąć tak niewiele?
  • Dlaczego w Javie istnieje 8000 różnych sposobów deklarowania liczby?


Jeśli tak, to bardzo dobrze trafiłeś. Nie będę próbował nikogo przekonywać, że Java to niekończąca się perfekcja. Bo tak nie jest (chyba że masz na myśli kawę, to tak). Nie jest jednak tak źle, jeśli wiesz, dlaczego jest tak, a nie inaczej.

Nauczyłeś się już trochę JavasScriptu, a może nawet Pythona i myślisz sobie: „To programowanie nie jest takie złe”. Potem masz to nieszczęście zobaczyć swój pierwszy kod w Javie. Oczywiście, jak większość ludzi żyjących na tej planecie, Twoja reakcja jest zrozumiała i całkowicie uzasadniona: UCIEKAJ!

A jeśli tak jak ja masz awersję do aktywności fizycznej i przekroczyłeś 30. rok życia, być może pomyślałeś sobie: „Wiedziałem, że powinienem był zaplanować tę kolonoskopię na dzisiaj!”

Nawet doświadczeni w bojach programiści wiedzą, że należy unikać Javy jak ognia.

Jeśli jednak przyjrzymy się kilku przykładom kodu w Javie i spróbujemy zrozumieć uzasadnienie sposobu działania, to być może następnym razem ten język będzie dla ciebie nieco mniej przerażający.


A co dokładnie Java robi dobrze?

Gdzie jest więc ta błyszcząca gwiazda Javy? No bo gdzieś musi być, prawda?

Czy aby na pewno?


No tak, gdzieś jest!

  1. Duże aplikacje: ale tak, naprawdę, naprawdę absurdalnie duże aplikacje.
  2. Szybkie aplikacje: może nie tak szybkie, jak w C++, ale szybsze niż przeciętny miś.
  3. Kompatybilność krzyżowa i kompatybilność wsteczna: nie, nie chodzi o pozycje jogi.
  4. Wielowątkowość i paralelizacja: czekaj na więcej info.

Jeśli jesteś na takim etapie swojej programistycznej drogi, że powyższe punkty nie są dla Ciebie ważne, możesz nie docenić zalet Javy bez dodatkowych wyjaśnień. Poznajmy więc na początek bogactwo typów danych w Javie.


O co chodzi z tymi wszystkimi typami danych?

Java ma wiele prymitywnych typów danych. Dlaczego? Służą one przede wszystkim do zapewnienia elastyczności i optymalizacji.

Przyjrzyjmy się przez chwilę typom danych Number. Dla liczb całkowitych mamy short, int i long , a dla liczb ułamkowych — float i double. Dlaczego tak dużo? W większości przypadków można sobie poradzić, używając int dla liczb całkowitych i double dla dziesiętnych. Ale kwestia jest taka:

Short to tylko 2 bajty, ale zarówno long, jak i double to 8 bajtów. Jeśli więc starasz się zoptymalizować kod pod kątem zajmowanego miejsca, to użycie najmniejszego typu danych o wystarczającym rozmiarze spowoduje, że obciążenie programu będzie mniejsze.


Dla porównania wszystkie liczby w języku JavaScript mają rozmiar 8 bajtów, niezależnie od ich wielkości liczbowej. Oczywiście pojedyncza liczba sama w sobie nie stanowi większego problemu, ale jeśli przechowywane są miliardy liczb, to mamy do czynienia z gigabajtami zaoszczędzonej pamięci.


Klasy opakowujące

Nie należy zapominać, że Java posiada również klasy opakowujące, które zapewniają nowe funkcje prymitywnym typom danych. Integer, Short, Double, Float, Boolean itd. Krótsza wersja tej historii jest taka, że klasy te mają wbudowane pewne przydatne funkcje statyczne.

To jak noszenie eleganckiej kurtki z naprawdę fajnymi kieszeniami, rzepami i zamkiem błyskawicznym, a może nawet ze wbudowanym uchwytem na kubek. Owijasz się w specjalny pakiet, który pozwala Ci robić więcej rzeczy, mimo że w środku nadal jesteś tylko starym, nudnym człowiekiem.

Klasy opakowujące umożliwiają także przypisywanie wartości null, co może być zaskakująco pomocne, zwłaszcza kiedy kod łączy się z Internetem. Nie wiesz, jako co zainicjalizować liczby lub typy logiczne we wrapperze?

Słońce, ustaw je na null. Tylko nie zapomnij ich później zaktualizować... bo pożałujesz.

Zastrzeżenie jest jednak takie, że gdy programy mają do czynienia z ogromnymi ilościami danych, prymitywy są znacznie szybsze w działaniu.

Na przykład List<Integer> będzie miała gorszą wydajność niż tablica prymitywnych liczb całkowitych (int[]). Czy będzie to miało znaczenie dla przeciętnego użytkownika? Niekoniecznie, ale elastyczność i możliwości są tutaj zapewnione na wszelki wypadek.


Atomowe co?

Jeśli to nie wystarczyło, by wywołać w Twoim mózgu eksplozję termojądrową, istnieją również atomowe wersje niektórych klas opakowujących (zobacz, co zrobiłem). Pamiętasz, jak wcześniej wspominałem o wielowątkowości i paralelizacji?

Co, jeśli zmienna ma być dostępna dla wielu wątków w różnych momentach, ale chcesz się upewnić, że tylko jeden wątek może ją zmienić w danym momencie?

Pomyśl o tym, jak o otwartej toalecie. Każdy może z niej skorzystać, jeśli jest dostępna, ale jeśli ktoś inny chce w tym samym czasie załatwić swoje sprawy... rozumiecie, do czego zmierzam? Znacznie przyjemniejsza jest sytuacja, gdy jedna osoba wchodzi, zamyka drzwi, załatwia swoje sprawy, a następnie odblokowuje drzwi, gdy wychodzi.

W ten sposób każde skorzystanie z łazienki jest wydarzeniem dyskretnym lub “atomowym”. Podobnie klasy atomowe, takie jak AtomicInteger, AtomicBoolean, AtomicLong, AtomicReference itp. pozwalają tylko jednemu wątkowi jednocześnie zmieniać ich obiekty¹.


BigInteger i BigDecimal

Nie chciałbym zbytnio zagłębiać się w ten temat, ale klasy BigInteger i BigDecimal są zbyt ważne, aby o nich nie wspomnieć. Jak można się domyślić patrząc po nazwach, BigIntegers i BigDecimals są używane dla naprawdę dużych liczb, które nie mieszczą się w konwencjonalnym 8-bajtowym typie danych. Jeśli więc potrzebujesz arbitralnie dużych liczb, to Ci goście to Twoi kompani.

Jedną z zabawnych rzeczy w komputerach jest to, że czasami mają one problemy z liczbami dziesiętnymi. Jeśli jesteś kujonem to wiesz, że komputery zapisują liczby w systemie dwójkowym (binarnym), podczas gdy współcześni ludzie, a być może także neandertalczycy, myślą w systemie dziesiętnym (dziesiętnym).

Mówiąc konkretnie, BigDecimal jest klasą, której należy używać, jeśli wymagana jest absolutna precyzja. Jeśli więc programujesz coś związanego z finansami, to tylko z BigDecimal. Nie mów później, że Cię nie ostrzegałem.


Wnioski

O typach danych w Javie można by powiedzieć znacznie więcej, ale mam nadzieję, że dowiedziałeś się, dlaczego jest ich tak wiele. Nie będziesz ich oczywiście potrzebował wszystkich naraz.

Niektórych z nich w ogóle nie będziesz potrzebował. Ale kiedy znajdziesz się w jednej z sytuacji, do rozwiązywania których zostały stworzone, będziesz sobie dziękował, że je masz.


Przypisy końcowe

  1. Analogia, której użyłem w odniesieniu do klas atomowych, jest pewnym uproszczeniem. Współbieżność w Javie to bardzo trudny temat, dlatego zdecydowałem się na prostą analogię, która łączy w sobie kilka zamysłów. Jeśli dokładniej przestudiujesz wielowątkowość, spostrzeżesz, że sprawa jest nieco bardziej skomplikowana.


Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>