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 chcę ci pokazać jeszcze kilka operacji które możesz
wywołać na streamach bardziej się przyjrzymy też streamom
dla typów numerycznych dla double i int'ow oraz zobaczymy
czym się różnią streamy sekwencyjne od streamów współbieżnych pierwszą
rzeczą którą chcę ci pokazać to to jak możesz sprawdzić czy wszystkie elementy
danego streamu spełniają dany warunek moglibyśmy
to osiągnąć poprzez użycie filter tak
i następnie wpisanie tutaj warunków w jakimś predykacie później
zrobienia count na danym streamie a następnie porównanie
go z ilością elementów które powinny być w tym streamie w sensie porównaniu
ich z tą listą z długością tego arraya który tutaj podajemy istnieje
na to przygotowana operacja match all w którym podajemy po prostu predykat
który ma zostać spełniony a następnie ten match all zwraca
już boolean prawda możemy sobie to sprawdzić okej
i zwraca true jeżeli tutaj dodamy na przykład minus 1 to
zwraca false kolejną rzeczą którą chcielibyśmy sprawdzić to możesz że
nie wszystkie elementy spełniają ale że przynajmniej jeden spełnia dany warunek służy
do tego operacja any match która też dostaje predykat oraz zwraca
boolean jeżeli przynajmniej jeden z elementów spełnia ten warunek tak
tutaj sprawdzałem czy przynajmniej jeden element jest ujemny możemy też sprawdzić
czy żaden z elementów nie spełnia warunku do tego służy non match używając
tych funkcji tych operacji teraz możemy łatwo zakładać co jest w tym streamie i właściwie
reagować tak bez pisania tych wszystkich filtrów count'ów i tak dalej okej
tyle jeśli chodzi o sprawdzenie czy dany stream się matchuje kolejnymi
operacjami które mogą ci się przydać to jest skip i limit których możesz wykorzystać aby
odpowiednio przyciąć stream tak dostają bardzo dużo danych to
możemy wykorzystać limit jeżeli interesują nas na przykład tylko 5 pierwszych
elementów tak możemy też użyć skipa jeżeli wiemy że pewien zasób
danych nas nie interesuje w ogóle te operacje działają w ten sposób tak
tutaj odpowiednio mieliśmy 1 2 3 4 5 i dostaliśmy tutaj przy
użyciu skip ominęliśmy dwa pierwsze elementy a następnie używając
limita wyciągnęliśmy tylko dwa pierwsze
elementy pewną taką właściwością o której wspominałem
przy okazji w poprzednich lekcjach jest to że możemy podzielić nasze
strumienie na strumienie które zawierają obiekty tak jakieś typy generyczne ale pamiętajmy
że nie możemy utworzyć typów generycznych dla typów prymitywnych
możemy tylko utworzyć typy generyczne dla pewnych wrapperów na typy prymitywne
tak jak nie dla inta ale dla integera możemy już utworzyć na przykład stream tak
dlatego zostały przygotowane specjalne implementacje które wystawiają
właśnie taki interfejs pozwalający na operowanie na primitive'ach
jest to między innymi int stream double stream long stream
jak już utworzymy w ten sposób nasz strumień od primitive'a nic
nie stoi na przeszkodzie aby zmapować ten stream z powrotem na
stream od integerów od jakichś innych typów tutaj
możemy wykorzystać map to object i tutaj możemy już na przykład
zwrócić stringa możemy ja tu robię e to e tak więc
wracam po prostu to samo tylko ze względu na to że on tu
się spodziewa jakiegoś typu tak obiektu
wykonuje się auto boxing tak i int zamienia się na integera
dlatego tutaj mam integer już z powrotem tak tak samo int stream już
sam daje metodę boxed która sprawia
że on wraca z powrotem do stream of integer w momencie kiedy
implementowano te strumienie w środku mogłyby się znajdować tylko int prawda
więc nic nie stało na przeszkodzie żeby doimplementować kilka ciekawych takich metod
między innymi sum average min i max tak i
one działają dokładnie tak jak się tego spodziewamy min i max
oraz average zwracają option'a tak w sum
z tego co mi się wydaje zwraca int'a po prostu a te zwracają odpowiednie
optiony z tego co wiem że ten stream może nie mieć żadnego
elementu i wtedy dostaniemy pustego optiona tak
no i jeszcze dodatkowo jest taka metoda summary statistics
która tu jak sobie tutaj wyprintujemy ona zwraca taki śmieszny obiekcik
summary statistics tutaj będzie inny zależnie od tego
jaki mamy dokładnie typ tego primitive'a tak jak dla double co innego no
i on zawiera po prostu agregacji tych wszystkich metod
które tutaj wywołaliśmy tak więc wywołując jedną tą metodę
możemy dostać wszystkie te dalej później zrobić po prostu get w zależności
od tego co tutaj chcemy żeby bez względu na to że możemy zrobić
raz sumę sumie raz average i wtedy już terminujemy tego stream'a
to po prostu możemy wywołać raz summary statistics i nie musimy się powtarzać
tak to tyle jeżeli chodzi o strumieniu dla typów prymitywnych
teraz przejdźmy do strumieni sekwencyjnych oraz dla strumieni
zrównoleglonych aby zaprezentować ci różnice między
tymi strumieniami tutaj napisałem kawałek takiego kodu który pozwala
benchmarkować tak to jest oczywiście nie jest to jakaś super implementacja ale
ona zwracam nam tutaj dostajemy start time end time a pośrodku
wykonujemy jakąś operację którą chcemy zmierzyć tak jej wydajność no
i tutaj utworzyłem intstream range closed który po prostu
daje mi stream elementów od jedynki do 100 i
on już samo range closed sprawia że dostajemy tutaj stream by deafult
sekwencyjny no i co jeszcze raz wykonuje sequential
który upewnia się że ten stream jest sekwencyjny tylko po to żeby to jeszcze unaocznić
tak ponieważ przy tym będziemy manipulowani no i
tutaj w środku wywołujemy mapa i w tym mapie tutaj jest takie
takie brzydkie wyrażenie ponieważ jest thread sleep czyli usypiam
wątek tak no jest trochę takiego jest taki boilerplate
który sprawia bym mógł to bezpiecznie uruchomić ponieważ
trzeba wtedy łapać taki taki interrupted exception zapomnijmy
o tym tutaj powiedzmy w tym mapie jest jakaś wykonanie
jakichś operacji która jest po prostu kosztowna czasowa tak później po prostu
zwracam e plus jeden mógłbym po prostu e zwrócić nie ma tutaj różnicy
ważne jest że to jest po prostu kosztowne ta operacja będzie trwała
później wypisuje tak żeby ten strumień ze względu
na jego na to że strumienie nie są lazy to
żeby zmusić strumień aby on od początku do końca przetworzył każdy
z tych elementów tak bo gdybyśmy mieli tutaj mi na przykład limit 10
no to on by mi wykonał tego mapa tylko dla pierwszych 10 elementów tak no
i teraz tak odpalimy ten kodzik on
mi teraz będzie powoli bardzo leciał i każdy z tych
elementów które wpadają będzie sekwencyjnie przetwarzał dodawał tutaj jedynkę
tak i odczekiwał te 200 milisekund
okej i skończyło się tutaj dostaliśmy 20 tysięcy
milisekund czyli 20 sekund a ich teraz jeżeli zamienimy
ten stream z sequencial na parallel tak
to zobaczymy ile nam teraz to zajmie okej taki
kod wykonał się w nieco ponad 3 sekundy tak no
i właśnie na tym polega różnica między parallel a sequencial
streamami na tym że te mapy oraz inne operacje
które wewnątrz tego strumienia się wykonują one mogą być
zrównoleglone tak czyli te mapy teraz się wykonują kilka
wątków może naraz obrabiać te dane tak i też tutaj odczekiwać
tego thread sleepa tak a w przypadku sequencial wpada tutaj jedna
wpada jeden element tak on się kończy przetwarzać wpada
kolejny później znowu kolejny tak i dlatego tyle to trwa tam było 20
sekund tu mamy 3 sekundy okej teraz ze
względu na to że są parallel stream'y to one są związane
z pewną taką no tutaj mogą być jakieś takie
elementy losowe czyli na przykład jeżeli byśmy odpalili ten kod dwa
razy i tutaj robimy foreach'a na końcu no to za każdym z tych odpaleń
pewnie będziemy dostawali inny wynik tak to jest jedna z takich cech
tego power stream'u tutaj możemy próbować sobie
radzić z tym wykorzystując sorted ale to niewiele da ponieważ to i
tak sprawi że te elementy będą posortowane ale tak jakby w
ramach tych kilku wątków które operują tutaj więc
tutaj one są posortowane ale widzimy że i
tak w całości end-to-end on nie do końca jest posortowany tutaj widzimy takie
chunki a możemy sobie poradzić z tym na
przykład wywołując sequential na tym wcześniej później robiąc sorted
wtedy dostaniemy posortowaną kolejność albo możemy też zrobić ordered for each
for each ordered która
sprawia że ten konsument który w środku tutaj
podajemy do tej metody jest już wywoływany z tymi danymi
w takiej kolejności jakie one powinny być tak czyli w posortowanej tutaj
w całym zakresie tak tak
widzimy mamy dodatkowo strumień wystawię jeszcze metodę is parallel
która pozwala sprawdzić czy dany strumień jest zrównoleglony czy jest sekwencyjny co
prawda takich praktycznych zastosowań tej metody też za dużo nie jest mógłbyś
na przykład pomyśleć o takiej jakiejś generycznym jakimś helperze
który jeżeli dostaje jakiś strumień to jeszcze sprawdza że on jest parallel
i w razie potrzeby jeszcze sprawia że on jest zrównoleglony
tak powiedzmy to jest jakiś taki jakiś taki util do odpalania na przykład strumieni
do jakichś operacji na nich no i jest możesz to sobie sprawdzić czy ten
strumień jest zrównoleglony czy nie na przykład na potrzeby
jakieś debugowania tak jeżeli dostajesz jakieś dziwne wyniki i
chcesz się upewnić czy dany strumień jest sekwencyjny tak okej
to tyle na tę lekcję w kolejnej lekcji postaram się zaprezentować
takie dobre praktyki pracy ze strumieniami dzięki wielkie i do zobaczenia