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 pokażę ci jak stworzyć drugi
wątek i odpalić na nim dodatkowy kod każdy kod który odpalamy
w javie jest podany na jakimś wątku nawet jak tworzymy jakiegoś prostego maina który
nam nie robi nic więcej jak tylko printuje foo mamy tutaj nasz wątek
tak on też dziś się odpala ten kod prawda okej i w każdym
miejscu jesteśmy w stanie dobrać się do tego obiektu wątku obiektem
wątku w javie jest thread tak to jest jakaś klasa tworzymy
sobie je i obiekty możemy stworzyć nowy ale na razie spróbujemy dowiedzieć
się co nieco o tym w którym miejscu jest ten kod który tutaj mamy to proste foo prawda
jesteśmy w stanie dobrać się do wątku na którym obecnie jest
wykonywany dany kod poprzez wykonanie thread current
thread tak to jest taka statyczna metoda na klasie thread ona pod spodem jest
zaimplementowana natywnie prawda okej jak już tutaj to
sobie wywołamy to w tym miejscu mamy thread i to jest dokładnie ten thread
na którym obecnie wykonuje się ten kod tak okej z niego
możemy wyciągnąć między innymi id i name a teraz sobie zobaczymy co to jest co z tego wyjdzie mamy
id 1 i thread name main on domyślnie
się nazywa właśnie main jeżeli tutaj to tutaj jest właśnie w obrębie main
okej to teraz pomyślmy sobie jak jesteśmy w stanie odpalić
jakąś akcję nawet jakimś prostym printowaniem w drugim wątku najprostszym
sposobem na uruchomienie jakiejś akcji w tle jest stworzenie
obiektu klasy thread robimy to po prostu przez konstruktor i tutaj w konstruktorze
możemy podać jakiś obiekt klasy runnable czyli w
tym wypadku możemy tak po prostu wstawić jakąś lambdę w której jest
kod który ma zostać wykonany na koniec jeszcze trzeba na tym wątku
dopiero co stworzony wywołać start tutaj tak długo jak to nie
stało na tym wywołane start to nic się nie dzieje ten wątek powiedzmy mamy go w ręku ale
on tam się nic nie wykonuje w tym momencie prawda okej sobie uruchommy
taki kod dobra i tutaj mamy ten fragment który był w main prawda
mamy id i mamy nazwę i tutaj widzimy stworzył się drugi thread
mamy tu już inną nazwę tego thread'a prawda to jest ten kod który został
tutaj wywołany prawda bardzo ważną rzeczą jest to żeby wywołać tutaj start
a nie run jest jeszcze jest taka metoda run tak ale ona wynika z
tego że thread jest runnable i ten run robi zupełnie coś innego ten
run naprawdę odpala to co my tu przekazujemy tak
tylko że on to robi zupełnie synchronicznie to jest dokładnie wywołanie tego
kodu który jest podstawiany drugim sposobem analogicznym
moim zdaniem mniej taki intuicyjny i fajny
ale ciągle możliwym jest tworzenie klasy jakieś na przykład
właśnie worker oraz rozszerzenie thread'a i w tym momencie możemy mu
nadpisać metodę run która domyślnie jest pusta tak jak wcześniej podstawialiśmy
runnable który był po prostu wrzucany tego runa tak
to teraz po prostu go nadpisujemy i działa to zupełnie analogicznie to czy wykorzystujemy
nadpisywanie tego thread'a tak rozszerzanie czy wykorzystują po prostu wstrzykiwanie
runnable to zależy od nas pamiętaj jednak że na przykład jeżeli rozszerzamy
thread'a tak to nie mamy multi dziedziczenia w javie więc jeżeli rozszerzymy thread'a to
nie możemy nic więcej rozszerzyć tak z drugiej strony tutaj w run mamy dostęp
do klas tego thread'a tak że do pull tego thread'a więc możemy coś
jeszcze sobie z niego wyciągnąć tak od razu tam musielibyśmy na przykład w
tym runnable robić to po prostu przez thread current thread wtedy się
dobijać do tych rzeczy prawda i też nie do każdej się możemy dobić tak bo tam
musimy mieć tylko teksty są publiczne tyle jeżeli chodzi powiedzmy
o stworzenie samego thread'a teraz jeżeli wrzucimy jakiś kod do tego naszego
runnable tutaj tak na przykład jakiegoś sleep'a
sleep robi to że blokuje dany wątek na jakiś określony czas prawda
tutaj jest 10 sekund z tego względu że to są milisekundy okej coś takiego
sobie wrzucimy teraz do naszego thread'a spróbujmy sobie uruchomić ten kod jak
widzimy main się skończył prawda zostało wywołane stworzenie tego thread'a
tak wywołany start i od razu tutaj mamy
wywołanie the end of main prawda inaczej widzimy to
że zostało wywołane the end of main nie sprawiło że program się zamknął
w tym momencie mimo że wyszliśmy już z main'a tak naprawdę okej później dopiero
jak są wywołane the end of i ten thread ta linijka
dopiero wtedy nasz program się zamknął tutaj wynika z tego taki
wniosek jeżeli jest jakiś thread w naszej aplikacji tak
ten thread który my wystartowaliśmy z maina i on ciągle żyje tam
się wykonuje jakiś kod tak to nasza aplikacja nie chce się zamknąć to
nie jest do końca prawda z tego względu że mamy dwa rodzaje threadów mamy
thready które są daemonami czyli powiedzmy takimi threadami w tle
trochę oraz mamy zwykłe thready tak możemy to
wystartować możemy tak zrobić aby ten thread mimo że powiedzmy on jeszcze trwa
i coś tam się dzieje ale wyszliśmy z naszego maina który był ostatnim
powiedzmy non daemon thread tak to nasza aplikacja wtedy się zamknie
możemy zrobić coś takiego thread set daemon i wtedy możemy ustawić
true tak i teraz jak odpalimy ten program to mamy
zupełnie inne zachowanie widzimy wyszliśmy the end of main mamy tak
czyli to miejsce i od razu później zamknęła się nam aplikacja prawda to
jest właśnie to rozróżnienie między daemons i non daemon thread warto dodać
że każdy thread którego stworzymy by default jest non daemon
więc jeżeli chcemy to zmienić wtedy po prostu ustawić
ostatni daemon do tej pory mieliśmy jakieś takie akcje w threadzie tak
tym drugim które powiedzmy coś tam się wykonywało ewentualnie coś powiedz na
chwilę zaczekało tak tam był sleep no ale możemy też mieć takie
akcje które powiedzmy powodują że ten thread działa w nieskończoność prawda no
i teraz możemy się zastanowić jak wyłączyć takiego threada na
threadzie jest taka operacja jak thread stop tak
ona jest duplicated i nie powinniśmy jej używać jest pewien problem
związany z tym że jeżeli mamy na przykład tak napisanego thread'a
w środku który ma tutaj while true i w środku ma coś co
nie rzuci wyjątkiem tak no to tego niestety się nie da zatrzymać
też jeżeli ten thread nie jest daemonem tak
to jeżeli wyjdziemy z naszego main'a i powiedzmy skończą
się pozostałe jakieś fragmenty logiki biznesowej która jest wykonywana
na jakichś innych threadach a ten thread ciągle będzie żył to naszej aplikacji za
bardzo się nie da wyłączyć po prostu rzucimy jakimś wyjątkiem który nie zostanie nigdzie
handlowane albo na przykład wywołamy exit możemy jednak
tutaj w tym powiedzmy while'u tak wrzucić jakąś flagę która
będzie decydowała o tym że dany thread ma skończyć pracę do
tego świetnie nadaje się taki mechanizm interrupted tak na
każdym threadzie którego stworzymy możemy wywołać interrupted prawda tak na prawdę
na każdym threadzie który mamy w ręku prawda i w momencie kiedy my
wywołujemy interrupt to tutaj w tym threadzie zostaje
ustawiona flaga interrupted i możemy ją sobie sprawdzić tak i
teraz jaka jest logika w momencie kiedy to nie
jest na niczym zablokowane to po prostu ta flaga się podstawia tak jest ustawiony true
i możemy to sobie sprawdzić tak w tym is interrupted spróbujmy
sobie teraz odpalić jak widzimy mamy
tick printowane tak długo aż po prostu jest wywołane the end
of main czyli tutaj czyli chwilę wcześniej został wywołany interrupt
tak w tym miejscu czyli odczekaliśmy sekundę był interrupt w tym
momencie ten while tutaj już było false tak no
i po prostu zakończył pracę z tego threada main już był zakończony
ten się skończył nie był daemonem tak więc musieliśmy na niego zaczekać ale
koniec końców nasza aplikacja się zamknęła po prostu może się tak jednak zdarzyć że
w naszym threadzie tak tutaj w który tutaj wpiszemy wywołujemy
na przykład sleep czy jakąś inną operację która po prostu blokuje i czeka
na coś tak to jest ważne żeby ona obsługi mechanizm interrupted
i w tym momencie jeżeli mam na przykład tutaj sleep
który przerzuca interrupted exeption tak to w momencie kiedy my
zrobimy na niej interrupt to nie jest ustawiona żadna flaga tak
to is interrupted w tym momencie zwróci im dalej false
tak czyli ta pętla będzie dalej wywoływana ale
już rzucany wyjątek i teraz w momencie gdy my łapiemy taki wyjątek to
poza tym że jakoś go obsłużymy tak w momencie musimy tutaj stwierdzić co dla nas znaczy
że dany wątek został powiedzmy właśnie przerwany w tym momencie tak
się tym mechanizmem interrupted i poza tym że to obsłużymy
to koniecznie powinniśmy ustawić interrupt teraz tak czyli powiedzmy przerywać
sam wątek tak w sensie to jest tylko ustawienie w tym momencie flagi ale
mogą być jakieś inne miejsca jakiś kod w naszym wątku który na tym polega w tym momencie i on
musi to wiedzieć tutaj równie dobrze możemy ustawić interrupted i jeszcze wrzucić jakimś
wyjątkiem na przykład tak i teraz jeżeli mam tego sleep'a tak
to zobaczymy ładnie że to nam też zadziała mamy
tik który po prostu jest teraz między nimi tych 200 milisekund
tak jest the end of main dostajemy interrupted tutaj widzimy że został
wywołany ten catch i po tym kończy się praca ponieważ ustawiliśmy to interrupt
w tym momencie to już ten is interrupted w tym momencie
po prostu wyszło że to jest true a z całego wyrażenia
wyszło false więc while się skończył i ładnie zakończyliśmy prace naszego thread'a
okej czyli jesteśmy w stanie powiedzmy
tak to jakoś zamodelować abyśmy byli w stanie ładnie zakończyć
pracę naszego thread'a tak przypominam że jeżeli thread sam
tego nie obsługuje tak w sensie nie nie ma jakiejś takiej ścieżki jakiejś
drogi żeby go dobrze zamknąć tak w jakiś taki cywilizowany sposób z
poziomu javy tego nie da się zrobić tak to można wywołać to stop
które on średnio działa i nie powinno go się używać tak ono jest niebezpieczne
trochę no to wtedy mamy jedyne nam pozostaje to rzucić wyjątek
i zamknąć aplikację jeżeli piszemy powiedzmy jesteśmy w stanie
obsłużyć to jesteśmy w stanie zrobić coś tak zwanego gracefull stop czyli
powiedzmy jakieś takie ładne zamknięcia aplikacji czyli wszystko powiedzmy zanim aplikacja
się skończyjest to jesteśmy w stanie wyłączyć na przykład wszystkie wątki które jeszcze
sobie tam mieliłem możemy na nim nawet chwilę zaczekać jeszcze prawda okej
podsumowując to czego się nauczyliśmy jeżeli chcemy stworzyć nowego thread'a tak
odpalić jakiś kod w drugim wątku no to tworzymy albo thread'a
i podajemy mu runnable albo rozszerzamy klasę thread teraz jakoś naszą klasą i nadpisujemy
run ważne jest to żeby wywołać na końcu start a nie run oraz
jeżeli chcemy na przykład zatrzymać pracę jakiegoś thread'a no to wykorzystujemy najlepiej is
interrupted tak ten mechanizm tak tu wołamy interrupt i tutaj
powiedzmy jesteśmy w stanie zatrzymać tę pracę jeżeli tego nie zrobimy tak
to możemy jeszcze złapać się na tym że nasz kod wrzuci jakiś wyjątek i wtedy ten thread
się zakończy albo thread sam stwierdzi że okej już koniec
tej pracy później dochodzi do tego miejsca i sam się kończy inaczej nie mamy opcji
jak go inaczej zamknąć prawda kolejną rzeczą jest to że jeżeli obsługujemy
gdzieś w interrupted exeption czyli robimy sleep tak jakiegoś while'a w jakimś miejscu tak
no to później koniecznie ustawmy jeszcze interrupt tak
na tym threadzie i ostatnia rzecz którą powinniśmy pamiętać
to że jeżeli chcemy się dobrać do naszego thread'a w którym obecnie jesteśmy
nawet jeżeli jesteśmy tutaj podajemy jakąś lambdę no to wykorzystujemy po
prostu thread current thread w tym miejscu dostaniem tego thread'a okej i to
wszystko co chciałem ci pokazać w tej lekcji dzięki wielkie i do usłyszenia w kolejnej