Techniki Zaawansowane
8 godz. 35 min · Java · Full-stack i Programowanie
Rafał SolarskiW poczatkowych lekcjach przyjrzymy sie pokladowi, na jakim bedziemy uruchamiali nasze programy. Wykorzystamy VisualVM do podgladania parametrów JVM. Zobaczymy równiez jakie zasoby sa tam dostepne oraz dowiesz sie jak monitorowac to, czy dobrze z nich korzystamy. Opowiemy Ci tez miedzy innymi o tym, jak zrobic heap i thread dump, oraz jak wlaczyc logi GC.
Kolejne lekcje kursu zostaly poswiecone wyrazeniom regularnym. Wyrazenia regularne mozemy wykorzystac nie tylko do walidacji danych wprowadzania przez uzytkownika, ale równiez do dzielenia tekstu oraz wyluskiwania wystapien wzorców w duzym tekscie. W tym rozdziale dowiemy sie miedzy innymi tego, jak mozemy wykorzystac ten potencjal z poziomu Javy.
Dzieki typom generycznym jestesmy w stanie osiagnac bezpieczenstwo typów w trakcie kompilacji przy zachowaniu elastycznosci pisanego kodu. W nastepnym rozdziale kursu przyjrzymy sie temu, jak mozemy je wykorzystac, oraz w jaki sposób czasami nalezy z tego bezpieczenstwa zrezygnowac.
Interfejsy funkcyjne to sposób na przeniesienie odrobiny swiata programowania funkcyjnego do Javy. Gdy polaczymy je z wyrazeniami lambda oraz API strumieni pozwola nam pisac te sama logike w duzo bardziej przejrzysty sposób.
Kolekcje, czyli listy, mapy, zbiory i kolejki to jedne z najczesciej wykorzystywanych klas w codziennej pracy. To jak dobrze poznamy ich mozliwosci ma ogromny wplyw na to, w jaki sposób bedziemy podchodzili do rozwiazywania wyzwan na naszej drodze.
System plików to zasób bez którego ciezko sobie poradzic. Pozwalaja nam na dostarczanie konfiguracji, danych wejsciowych i wyjsciowych, a przez to równiez integrowanie ze soba calych systemów.W tym rozdziale nauczymy sie korzystac z tych dobrodziejstw w bezpieczny sposób wykorzystujac m.in. IO Streams.
Trzymanie daty w String to niekoniecznie najwygodniejszy sposób. Juz od pewnego czasu Java dysponuje bardzo wygodnym Date&Time API - zobaczymy, co mozemy w nim znalezc.
Wielowatkowosc nie jest prosta... i dlugo tak jeszcze pozostanie. Nawet jesli korzystamy z frameworków, które próbuja ja przed nami ukryc, to ciagle musimy byc swiadomi problemów jakie sie z nia wiaza. W tym rozdziale poznamy glówne problemy na jakie mozna natrafic programujac wielowatkowo w Javie. Poznamy równiez klasy, które zdecydowanie pomoga nam zapanowac nad ta zlozonoscia.
JDBC to najbardziej podstawowy sposób laczenia sie z baza SQL z poziomu Javy. W wielu systemach/aplikacjach stosuje sie rozwiazania ORM takie jak JPA/Hibernate. Mimo wszystko warto wiedziec jak to pod spodem dziala oraz umiec poradzic sobie w aplikacjach, gdzie wybrano bardziej "lekkie" podejscie niz Hibernate.
Na koniec kursu wykorzystamy zdobyta wiedze, aby stworzyc prosta aplikacje pozwalajaca na przechowywanie danych o wydatkach. Po stworzeniu coreu aplikacji, mozesz spróbowac uzupelnic go za pomoca GUI.
Ten kurs stworzony zostal przede wszystkim z mysla o osobach, które juz poznaly podstawy jezyka, takie jak zmienne, mechanizmy kontroli wykonania, klasy, typy generyczne.Jezeli chcesz poczuc sie swobodnie nie tylko jesli chodzi o mechanizmy jezyka, ale równiez pod wzgledem znajomosci standardowej biblioteki Javy - to kurs w sam raz dla Ciebie. Materialy beda przydatne dla studentów, którzy znaja juz podstawowa skladnie Javy; programistów Javy chcacych poszerzyc lub uporzadkowac swoja wiedze; programistów innych jezyków, którzy chca poznac inny stack technologiczny.
Cześć w tej lekcji spróbujemy zaimplementować nasz
porównywacz do plików zacznijmy od tego że do naszego poma
dodałem kilka bibliotek dokładnie trzy jest to guava oraz biblioteczki
z apache commons są to biblioteki które mogą nam się przydać podczas
implementacji głównie to są jakieś takie klasy utilsowe przejdźmy
teraz do naszego directory comparator okej mamy naszą metodę
compare dirs która będzie takim punktem wyjściowym na razie ma ona
dwa parametry tak d1 d2 może później damy jeszcze jakieś
naszą implementację będziemy przeprowadzali w takim podejściu
app to bottom czyli będziemy zaczynali od jakichś takich ogólnych
konceptów i będziemy schodzili w implementacji coraz niżej rzeczą od
której możemy zacząć jest znalezienie plików w katalogu tak to pewnie
będzie metoda walk tak na danym path a następnie
wyliczenie wszystkich md5 dla nich prawda tak powiedzmy tutaj będziemy mieli
na przykład 20 plików tutaj będziemy mieć 30 dla nich wyliczymy sumę
md5 tak to będzie jakaś lista okej stwórzmy sobie na to jakąś metodę niech
będzie to metoda na przykład compute for files in directory czyli metoda
która wyliczy nam sumy dla wszystkich plików w danym directory tak przyjrzyjmy
się jaką sygnaturę metody tutaj zaproponowałem jest
to metoda która przyjmuje roota czyli powiedzmy director
od którego startujemy oraz powiedzmy zwraca listę
teraz uwaga future of pair path od string
teraz co jest co okej list no to wiemy future to
też wiem czyli powiedzmy to jest wynik jakiegoś obliczenia tak tutaj
chodzi o to że będziemy wyliczali powiedzmy współbieżnie dla
każdego path jego właśnie sumę kontrolną tak i
teraz czym jest pair pair to jest po prostu jakaś taka kolekcja
która zawiera zawsze dwa elementy to jest wzięte z apache commons tak
lang 3 jest to po prostu prawie to samo co m3
w mapie w javie tak czyli to jest coś co ma lewą i prawą stronę lewa
strona to jest path prawa strona to jest string tak w ten sposób
jesteśmy w stanie wyrazić listę takich
połączyć powiedzmy właśnie path oraz jego suma kontrolna i
to wszystko będziemy mieli dostępne kiedyś w przyszłości future tak okej
no dobra to myślę że sygnatura jest dość powiedzmy zrozumiała w miarę i
tutaj na razie wyliczmy to sobie wszystko dla pierwszej powiedzmy
dla pierwszego katalogu drugi będzie analogiczny okej
teraz musimy zdobyć wszystkie pliki z danego katalogu jeśli
pamiętamy to z rozdziału o plikach to możemy sobie wykorzystać metodę
walk na files to jest metoda która nam po prostu zwróci strumień
path'ów tak i to będą po prostu pathy które się znajdują w
danym katalogu on będzie schodził głębiej i głębiej i tak będzie nam zwracał po prostu po kolei
te path'y to jest z tego względu że interesują nas tylko
pliki tak regularne katalogi nas nie interesują to możemy zrobić is regular
file tak filter i następnie collectujemy to do jakiejś listy
tak łapiemy również wyjątek łapany przez walk i zamieniamy go
na runtime'owy ok na tym możemy już budować możemy lecieć
dalej okej w tym momencie możemy zacząć pracować nad obliczaniem
tego tej sumy md5 na razie zróbmy to w taki
synchroniczny sposób tak mamy stream'a i mapie będziemy po prostu wyliczali
tak daną sumę okej i zaimplementujmy
sobie jakąś metodę która nam sumę dla danego pliku wyliczy po prostu okej
wyliczanie sumy md5 może wyglądać w ten sposób w javie mamy coś
takiego jak message digest z tego jesteśmy w stanie wyciągnąć pewną
instancję to się właśnie nazywa message digest która będzie nam
w stanie wyliczyć sumę to działa w ten sposób że na tym czymś
co tutaj dostaniemy message digest musimy odwołać update możemy
nie musimy go po jednym dla całego pliku wywołać tylko możemy dla poszczególnych kawałków
tak ważne żeby przejechać po całym pliku tak wołam ten update a
następnie po prostu robimy md digest teraz nam to po prostu zwróci
array bajtów tak w którym coś później sobie zrobimy nie wiem zamienimy
go na stringa czy coś innego no okej to jest zasadniczo dosyć nudne
jedną ważną rzeczą jest to żeby tutaj wrzucić ten bufor prawda
i później będziemy mogli na przykład manipulować rozmiarem tego bufora i zobaczcie
jak nasza aplikacja powiedzmy się zachowuje to znaczy czy jest szybsza wolniejsza
i tak dalej więc warto sobie wyciągnąć do jakiejś stałej na górę okej
w mapie po prostu dajemy wyliczenie dla konkretnego patha i pliku tak naprawdę
wyliczenie sumy md5 i teraz to wyliczenie ta suma
jest w bajtach to nie jest powiedzmy najwygodniejszy obiekt takiego możemy używać
więc warto zamienić sobie to na stringa do zamiany tego
arraya przyda nam się klasa hex która jest po prostu z apache
commons tak wywołujemy na niej po prostu encode hex string
i to nam zamieni już na string przy okazji możemy
trochę zmienić ten path który mieliśmy do tej pory na razie
żeby wyliczyć tą sumę musieliśmy mieć pełen path do powiedzmy
do tego pliku teraz już przyda nam się bardziej path tego
pliku względem tego roota tak czyli względem tej tego katalogu
w którym się znajdujemy więc możemy sobie wykorzystać jako metodę i ona po prostu usunie
nam tego roota z samego przodu tego pathu i dzięki temu tam później będziemy
mogli porównywać te pathy odpowiadające sobie w różnych
katalogach prawda okej lecimy dalej już teraz
prawie nasze powiedzmy typy odpowiadają temu co jest w sygnaturze to
co nam pozostało w tej metodzie to po prostu odpalenie dla każdego
z tych plików wyliczenia sumy w oddzielnym wątku prawda jak
wiemy z poprzednich lekcji dobrze by było teraz to odpalić na jakimś executorze
żeby nie spawn'ować sobie tyle wątków ile mamy tych plików tylko
jakoś odgórnie kontrolować to jakimś egzekutorem tak jakąś pulą wątków więc
najlepiej dodajmy sobie właśnie executor tak no i co za tym idzie też dodajemy
go sobie w compare dirs jako parametr i teraz w momencie kiedy już
mamy executor service w ręku to nie będziemy blokując
tutaj tego kawałka tylko będziemy robili jakiś submit z jakimś callable
tak na executorze no a dalej już przekażemy
po prostu future który będzie potrzebny do otrzymania rezultatów czyli tej
sumy to będzie wyglądało w ten sposób już coś takiego możemy zwrócić
na zewnątrz z poziomu compare dirs możemy dodać nasz egzekutor jeszcze
najlepiej wyciągnąć go z klasy executors tutaj ta liczba wątków
na razie możemy ją tutaj tak z palca strzelić jakąkolwiek tak
z głowy ja dałem tutaj 7 ponieważ jest to liczba wątków które
obsługują procesor minus jeden tak później będziesz mógł poeksperymentować z
tą liczbą pamiętaj też o tym że jeżeli stworzyliśmy executor service to na
koniec musimy go zamknąć a właściwie zeschedulować jego zamknięcie tak z tego względu
że eksperymentujemy nie przejmujmy się dokładnym zamykanie go wywołując
shutdown now i powiedzmy cały ten pattern tak w tym momencie nam to wystarczy
okej skoro mamy już tutaj listę future'ów od tych przypisań
do patha check sumy tak no to musimy
jakoś zaczekać aż będziemy mieli wszystkie te future skompletowane i byśmy
woleli w tym miejscu mieć raczej listę wyników niż listę future'ów stwórzmy
sobie metodę która zaczeka na wyniki tych future'ów oraz od razu
collectuje wyniki do mapy będzie to łatwiej później obsłużyć
struktura mapy będzie trochę łatwiejsza przy porównaniu odpowiadających
sobie plików ja tę metodę nazywałem wait and collect to map wewnątrz
implementacja jest dość prosta tworzymy nową listę tak i jedziemy
po tych wszystkich future'ach które tutaj mamy i wołamy get tak tutaj warto
żeby wyspecyfikować jakiś timeout ja go tutaj ustawiłem na 60
sekund żeby jeżeli tutaj nam zawiśnie aplikacja to po jakimś czasie
dostanie po prostu wyjątek w tym miejscu tak wynik wyciągnięty z tego
future po prostu dodajemy do listy a następnie tą
listę jeszcze tutaj poprawiamy tak czyli po prostu robimy na tym
collect to map prawda no i odpowiednio z tego względu że ta krotka
która tu jest ten pair odpowiada powiedzmy temu kluczowi i wartości
którą chce umieścić w mapie to po prostu bierzemy get left get right okej
w tym momencie mamy mapę już gotową wyliczonych konkretnych obiektów
z przypisaniem do path jego check sumy teraz
zróbmy dokładnie coś takiego samego dla drugiego katalogu warto
tego nie robić na przykład w taki sposób tak w tym momencie gdybyśmy na
przykład dali tego white'a tutaj no to najpierw zlecamy
wyliczanie tych check sum czekamy a później dopiero zlecane wyliczenie
ich dla drugiego katalogu prawda może się okazać że na przykład pierwszy
katalog jest bardzo mały tak i w tym momencie już mogły
być wykorzystywane wątki do liczenia plików z drugiego katalogu dlatego
lepiej najpierw zaschedulować wszystko na executor serwisie a dopiero później
czekać no w ten sposób teraz tą część
współbieżną mamy za nami zostaje tylko zaimplementować metodę która znajdzie
różnice w tych check sumach niech ta metoda nazywa się find file
differences niech przyjmuje obie wyliczone przez nas mapy teraz
ją zaimplementujmy zacznijmy od znalezienia plików które
są różne w tym celu znajdźmy w ogóle sobie jakieś takie sety
samych plików w poszczególnych katalogach dzięki temu będziemy mogli
znaleźć te pliki które są wspólne takie zbiory możemy wyciągnąć
wykorzystując metodę key set na mapie następnie możemy wykorzystać metodę
intersection z klasy sets taka klasa utilsowa
z biblioteki którą założyliśmy czyli z guavy w ten sposób mamy wspólne
pliki z jednego i drugiego katalogu czyli tak naprawdę pliki które powinniśmy
między sobą porównywać okej zaimplementujmy teraz metodę która nam
porówna sumy z dwóch plików tak i stwierdzi czy tutaj jest jakaś
kolizja czy nie niech ta metoda nazywa się check sums niech przyjmuje
ten plik który chcemy porównać to jest relatywne czyli powiedzmy niezależna od żadnego
katalogu a właściwie zależne od obu katalogów ścieżka do tego pliku
tak do każdego z tych katalogów możemy ją przyłączyć oraz te dwie
mapy z tymi check sumami dzięki którym jesteśmy w stanie stwierdzić jakie są sumy tego
pliku w poszczególnych katalogach następnie po prostu bierzemy geta tutaj mamy
tutaj mamy gwarancję że ten plik znajduje się w tym w
tej mapie tak nie musimy tego sprawdzać no i sprawdzałem po prostu sumę
w tym miejscu mamy ją w postaci stringów ponieważ wykorzystaliśmy obiekt
hex prawda okej no i jeżeli nie są równe
no to dodajemy tutaj ten obiekcik tą reprezentację różnych sum
czyli powiedzmy to jest ten message o tym że sumy są różne
tak okej no i tutaj jest jakiś komunikat
no dobra dodajemy to a jak nie to zwracają pustego
optionala teraz pytanie czemu ja zwracam opcjonalna a nie na przykład boolean'a
tak albo nie samą tutaj tą komendę prawda pierwsza rzecz jest taka że
ten message on nie zawsze będzie zwracany prawda moglibyśmy
zwrócić nulla kiedy te pliki są identyczne tak
ja bym wolał użyć optionala z tego względu że tutaj jeżeli stworzymy
sobie stream z tego seta to możemy użyć
flatmap który jednocześnie może tak jakby zmieniać
typ tak tego obiektu czyli jest jak map trochę a
jednocześnie używając flatmap jesteśmy w stanie odsiać niektóre elementy tak tutaj
zrobię check sums to mogę później zrobić na tym stream tak
w ten sposób dzięki temu że robi tu stream to jeżeli ten stream
jest pusty tak czyli powiedzmy kiedy optional był pusty bo tak
stream na optionalu działa no to w tym momencie tak jakby zablokuję
ten element tak czyli sumy się zgadzały ja z tutaj zwróciłem
empty optional empty stream będzie pusty no dobra to odsiewamy
ten element no bo one nas nie interesują interesują nas tylko te gdzie one się różnią tak
a jeżeli się różnią no to ten optional zamieni się na stream
od jednego different sums tak no i po prostu poleci dalej
a dalej to już sobie zbierzemy kolektorem po prostu no
okej to sobie zbierzmy do jakiejś zmiennej different files tak czyli powiedzmy pliki
które są różne i teraz zajmijmy się jeszcze tymi plikami które w jednym
katalogu istnieją w drugim nie tak jak tutaj sobie robiliśmy intersect
czyli część wspólną dwóch setów tak tutaj możemy się posłużyć różnicą
symetryczną czyli powiedzmy zamiast brać część wspólną to weźmiemy
wszystko to co się różni z jednej i z drugiej strony tak co nie jest
wspólne między tymi setami w ten sposób będziemy mogli znaleźć te
wszystkie pliki które w jednym z setów istnieją a w drugim już ich nie
ma prawda okej no i jedziemy tutaj streamem po tym no
i tutaj wtedy możemy zrobić mapa bo każdy z tych plików które powiedzmy
tutaj dostaniemy w wyniku tej operacji już mówi że jest
jakiś jakaś różnica prawda okej to też sobie zapisujemy do jakiejś
zmiennej tak jakiejś kolekcji okej teraz pozostaje nam tylko
zsumować ze sobą obie te kolekcje tak i zwrócić
na zewnątrz moglibyśmy użyć czystej javy i po prostu stworzyć
array listę do której byśmy dodali jedną i drugą listę ale możemy używać iterables
concat i new array list po prostu też z guavy
w ten sposób połączyć ze sobą dwie kolekcje okej teraz możemy po prostu zwrócić
te różnice na zewnątrz okej i też to tak wygląda że zaimplementowaliśmy
naszą klasę teraz żeby położyć taką wisienkę na torcie z
tego względu że te metody które tutaj robiliśmy były statyczne tak i
tak jakby nie ma sensu tworzenia instancji tego obiektu tak tej
klasy to warto jeszcze stworzyć prywatny konstruktor tak żeby
to była po prosta taka klasa utilsowa której się nie da utworzyć używam po
prostu statycznych metod wygląda na to że nasz kod jest skończony jeszcze
warto sobie wygenerować nasze katalogi z plikami żeby
je sobie porównać odpalimy nasz powiedzmy program generujący
te pliki które on wygeneruje warto tutaj sobie coś usunąć
na przykład tutaj jeden plik tak tutaj jakiś inny zagnieżdżony
można jeszcze spróbować któregoś z nich zmodyfikować one są
na tyle dużo że w idei raczej ich edycja nie jest zbyt efektywna może się nad
nią dać i możemy jeszcze przetestować z najważniejsze czyli wyliczanie
naszych sum to możemy dodać powiedzmy po jednym pliku z jednej z drugiej strony
i po prostu wpisać do niego coś innego jakby na ile on jest wielki
nie ma znaczenia bo nasz program i tak tak jak będzie głównie
liczył na tych plikach które są takie same ale są duże przede wszystkim
tak czyli na nich się trochę zejdzie czasu okej możemy teraz uruchomić nasz program okej
jak widzimy mamy tutaj wyniki dwóch plików brakuje
tak czyli jest w jednej w jednym katalogu w drugim nie ma tak i
też powiedzmy dla tego pliku x widzimy że są różne sumy
tak to jest taka to jest taka okej i to jest zasadniczo
tyle z tego projektu możesz teraz spróbować na przykład modyfikować
tą liczbę threadów tak i w ten sposób spróbować na przykład czy
nie będzie szybszy trochę albo wolniejszy program jeżeli powiedzmy dasz więcej bądź mniej
możesz też poeksperymentować ze zmianą
tego bufora tutaj prawda w ten sposób powiedzmy na raz wczytywać
większą liczbę danych na heap prawda okej okej
mam nadzieję że wszystko ci się udało i masz satysfakcję z tego
że udało ci się to zaimplementować jak ci się część nie udało
i po prostu podejrzałeś jak ja to zrobiłem to nie ma problemu ok kończąc
ten projekt również kończy się rozdział o wielowątkowości w javie bardzo
ci gratuluję że dobrnąłeś właśnie aż do tego miejsca ten rozdział moim zdaniem
był jednym z najtrudniejszych też przygotowanie tego części takiego
merytorycznego materiału była dla mnie ciężka więc mam nadzieję że
udało ci się zebrać jak najwięcej wiedzy a no i mam
nadzieję że uda ci się ją w praktyce szczerze mówiąc na
codzień pracując powiedzmy z jakimiś takimi aplikacjami biznesowymi czy jakimiś
streamowymi no to tutaj zasadniczo musimy być świadomi
tego co się dzieje tak tej współbieżności ale często sami je implementujemy
tak czasami jak musimy zejść gdzieś nisko powiedzmy doimplementować
coś przejrzeć zobaczyć jak coś działa tak sprawdzić czy coś jest thread safety to
tak ale na takim poziomie powiedzmy biznesowym posługujemy się future'ami
tak executor service'ami raczej nie robimy takich
rzeczy z synchronizacją raczej też staramy się już teraz unikać
takich rzeczy jak synchronoize jakieś locki tak coś takiego okej
widzimy się w kolejnym rozdziale powodzenia
i do usłyszenia