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 dodamy interfejs konsolowy
do stworzonych przez nas building bloków zacznijmy od tego że mamy nasze
repozytorium mamy nasze klasy do eksportu i importu teraz warto
dodać oddzielny pakiet nazwijmy go w cli jak command
line interface którym po prostu będziemy implementowali nasze rzeczy związane
z samym interfejsem zauważ pozostałe rzeczy są
zupełnie niezwiązane one mogą być wykorzystywane przez zupełnie inne
interfejsy na przykład czy to jakiś webowy restowy jakiś powiedzmy
jakieś inne interfejsy tak a tutaj będą
tylko rzeczy związane właśnie z naszym cli okej w pakiecie
cli stwórzmy sobie klasę app w tej klasie stwórzmy
sobie main'a który będzie wywoływany poprzez uruchomienie naszego jar'a
okej wewnątrz naszego main'a po prostu wywołamy
parser picocli który zajmie się naszymi opcjami oraz
komendami które wywołujemy punktem wejściowym z wykorzystaniem
picocli jest klasa command line tworząc ją podajemy taką
rootową komendę czym są komendy komedy to są pewnego rodzaju powiedzmy
elementy które mogą mieć opcję parametry
i które są powiedzmy takimi podstawowymi building blokami
w picocli powiedzmy pierwszą taką komendą jest
nasza rootowa komenda jest do komenda która jest wywoływana w momencie
kiedy wywołujemy samą naszą aplikację tak czyli powiedzmy wywołujemy
sobie na przykład naszego naszego jara tak i
nie podajemy mu na przykład co co ma zrobić tak
czyli na przykład nie podajemy mu dodaj albo nie mówimy mu wylistuj
to jest nasza taka rootowa komenda okej i to jest tak
jakby tą główną komendą będzie właśnie app tutaj jak się dowiemy jak uruchomimy
na przykład ten kawałek to dowiemy się że ta
komenda musi być opisana jak command adnotacja tak okej jak
dodamy naszą adnotacje to jeszcze dowiemy się że nasza klasa komendy
powinna jeszcze implementować na przykład runnable a co za tym idzie też
powinna mieć run i teraz ten run to jest właśnie kawałek
kodu który jest uruchamiany w momencie wywołania komendy jak teraz sobie
to odpalimy no to nic się nie wydarzyło specjalnie okej co
teraz powinniśmy dodać pierwsza rzecz to to że jeżeli wywołujemy
nasz program bez żadnej komendy typu add albo list no
to wtedy powinniśmy pokazać helpa czyli powiedzieć jakie są możliwe
komendy tak zrobimy to właśnie w tym runie możemy
to zrobić poprzez wywołanie usage na command line i tutaj podajemy jako this
jako command nasz właśnie this czyli powiedzmy naszą komendę
oraz wskazujemy strumień gdzie ma zostać wyświetlony ten usage tak
teraz jak to uruchomimy to pojawi się niewiele
więcej a mianowicie usage main class tak wynika to z tego że
nie mamy za dużo jakichś opisów nie mam parametrów w tym w tej naszej komendzie możemy
na przykład dodać między innymi nazwę naszej komendy oraz włączyć taką specjalną
opcję mix in standard help options która sprawi że do każdej
komendy oraz do tej naszej komendy zostaną dodane
możliwe opcje czyli te powiedzmy takie flagi które włączamy poprzez
dodanie na przykład minus h albo minus minus help tak to
właśnie takie flagi pokażą się tutaj oraz one właśnie będą
pokazywały mniej więcej to samo co usage okej teraz jak uruchomimy
naszą komendę to widzimy że już się pokazało kilka rzeczy między
innymi jest na przykład opcja version
oraz opcja help okej no i dobra powiedzmy na taką
główną komendę to jest może wszystko teraz zajmiemy się
tym co jest faktycznie jakoś wartościowe biznesowo tutaj zanim
zaczniemy dodawać kolejne subkomendy tak czyli właśnie add
least i tak dalej to stwórzmy sobie na nie oddzielny pakiet nazwijmy
go subcommand okej i zacznijmy od implementowania
subkomendy add stwórzmy sobie po prostu klasę
add w tym pakiecie okej i tutaj implementacja będzie dość analogiczna
pierwsze dwie rzeczy które należy zrobić to raz dodanie tej adnotacji
command tutaj bardzo ważne żeby wyspecyfikować name
ponieważ po tym name będziemy wywoływali naszą subkomendę oraz
daliśmy runnable oraz co za tym idzie musieliśmy zaimplementować run
teraz dodajemy parametry i opcje do naszej komendy okej
dodaliśmy między innymi m od date oraz command to
są po prostu elementy które mają wylądować w naszym outcome'ie poprzez wywołanie
add tutaj te dwie rzeczy te option są by
default są niewymagane tak powiedzmy dostaniemy tutaj nulla jeżeli dana
flaga się nie pojawi z wartością tak w przypadku parameter on by
default jest wymagany i tutaj też czym się
różni parameters od option parameters to jest coś co się pojawia
powiedzmy jest to związane tak jakby z kolejnością
option możemy podać w każdym miejscu tak po prostu dajemy minus minus date i piszemy
jaka jest data a parametr po prostu specyfikujemy w danym miejscu
i on ci nie ma tak jakby żadnego prefiksu żadnej jakiejś takiej
abrewiacji okej dobra to są nasze powiedzmy
parametry i opcje tutaj jeszcze warto dodać opcję path do naszej
bazy danych na przykład w ten sposób tutaj i dodałem po prostu
opcję która którą wywołujemy poprzez wywołanie path albo
minus p oraz co ważne ma ona domyślną wartość również
dodałem descriptions które również możemy dodać w pozostałych opcjach okej i
zajmijmy się implementacją metody run zacznijmy od wyłuskania i sparsowania
date pamiętaj że date jest opcjonalne więc możemy tutaj dostać nulla
więc musimy go odpowiednie obsłużyć tutaj stworzyłem jeszcze
metodę parse optional date która zwróci nam optionala i on będzie
pusty jeżeli dostaliśmy nulla oraz będzie w nim sparsowana data
jeżeli tego nulla tam nie było i było coś tam sensownego okej i
tutaj po prostu robimy parse zauważ też jedną rzecz że wykorzystuje
local datetime zakładam że tak jakby integracja z zewnątrz
z użytkownikiem wykorzystuje czas lokalny ja go później sobie przekładam
na offset datetime i posługuję się offset datetime dalej
wszędzie będzie w aplikacji w eksporcie i tak dalej ale zakładam że powiedzmy
gadanie z użytkownikiem wykorzystuje właśnie czas lokalny tutaj
również jeżeli nie ma tej daty to wstawiam po prostu now kolejną
rzeczą jest stworzenie naszego wydatku po prostu przez wywołanie
konstruktora następnie z tak utworzonym wydatkiem
możemy wywołać metode add na otwartym obiekcie repository tutaj
repozytorium otwieram powiedzmy w try with resources od
razu je zamykam tak ponieważ tak jakby wywołanie tej subkomendy
no jest tylko w momencie kiedy odpalamy
daną komendę i już później to repository nie ma szansy być wykorzystane jeszcze raz
ponieważ cała nasza aplikacja umiera w tym momencie razem z jvm więc
nie ma sensu go dalej trzymać wywołujemy add po prostu zamykamy repozytorium zauważ
że ten fragment kodu będzie często używany prawda
w innych komendach z tego względu możemy wywalić ten fragment jako
po prostu jakiś consumer tak a całą resztę wyekstraktować
do oddzielnej metody na przykład w ten sposób tutaj jeszcze wyekstraktowałem
metodę outcome żeby na koniec wyświetlić komunikat użytkownikowi można
to zrealizować w ten sposób tutaj jeszcze dodałem metodę add outcome która dostaje
repozytorium oraz outcome dodaje ten outcome do repozytorium oraz
wyświetla w przypadku sukcesu tak czy jeżeli tutaj nie zostanie rzucony wyjątek po prostu
komunikat użytkownikowi komunikat możemy wyświetlić po prostu używając print line'a
na tym miejscu okej tyle jeżeli chodzi o implementacja tej komendy add
jeżeli chcemy teraz implementować kolejne subkomendy
to warto sobie przygotować trochę pole i tutaj możemy na
przykład te metody jak do in repository oraz parse optional date
możemy je przenieść na przykład do jakiegoś jakiejś klasy nadrzędnej możemy
sobie stworzyć na przykład klasę nadrzędną abstrakcyjną base
subcommand i do niej wrzucić te wszystkie metody które
mogą być przydatne w kolejnych subkomendach okej
tutaj stworzyłem właśnie klasę abstrakcyjna bese subcommand oraz przyniosłem
do niej te metody które powiedzmy były też vat dodatkowo jeszcze stworzyłem
metodę compute with repository która jest dokładnie do in repository z
tą różnicą że jeszcze zwraca wynik więc dostajemy tutaj function zamiast konsumenta
dodatkowo jeszcze tutaj zaimplementowałem
run już na poziomie base sub command z tego względu że
chciałem też dodać logowanie w tym miejscu chce złapać wszystkie wyjątki które
były wrzucone w miejscu run command i run command to jest powiedzmy
metoda którą nadpisują wszystkie subkomendy które będą pod
pod spodem tak czyli które będą implementowały tę klasę atrakcyjną tak
ją rozszerzały i tutaj jeżeli zostanie rzucony
jakiś wyjątek no to po pierwsze printuje ten message
z tego wyjątku a jeżeli jest włączony verbose vervose to jest taka
opcja którą możemy dodać na przykład minus v tak domyślnie
jest wyłączona to jeszcze dodatkowo printuje stack trace tak
to jest taka opcja powiedzmy do zdebugowania co się działo i metoda run command
jest implementowana na poziomie add tak już nie implementujemy samego
run tylko tak jakby jeden fragment który jest
wewnątrz run'a wykorzystywany tak w ten sposób zaimplementowaliśmy
też pewien design pattern który nazywa się template method ale
to wykracza poza ten kurs jeżeli chcesz możesz coś znaleźć w internecie co to jest
w każdym razie w ten sposób zaimplementowaliśmy liczby pierwszą subkomendę
oraz mamy taki jakby powiedzmy przygotowany grunt na implementacje kolejnych
w dość łatwy sposób w momencie kiedy mamy zainteresowany tą subkomendę
add trzeba ją jeszcze zarejestrować na poziomie app tutaj w tej
głównej komendzie tak tutaj mamy w tej adnotacji
command mamy coś to się nazywa subcommands takie pole i tutaj mamy
array class które są subkomendami tej komendy tak
czyli dajemy po prostu at class w ten sposób każdą dodatkową
subkomendę którą zaimplementujemy w tej w tym pakiecie musimy jeszcze
zarejestrować w tym miejscu tak nie zapominaj o tym teraz proponuję abyś
samodzielnie spróbował zaimplementować pozostałe subkomendy tak jak powiedzmy
wydaje ci się że one powinny być rozłożone możesz podzielić powiedzmy niektóre
rzeczy mogą być zaimplementowane jedną subkomendą oraz
mogą mieć kilka opcji tak a niektóre możesz rozdzielić na przykład na
dwie subkomendy na przykład delete możesz mieć delete one i delete all
albo możesz mieć na przykład delete który dostaje opcje minus a na przykład
prawda jak zaimplementujesz już samodzielnie wtedy odtwórz
dalej ten materiał i porównać z tym jak ja to zaimplementowałem okej
to teraz pauza i do usłyszenia za chwilkę okej
mam nadzieję że udało ci się zaimplementować pozostałe subkomendy
to zobaczmy jak ja to zrobiłem po rozdzieleniu tych
wszystkich rzeczy wymagań stworzyłem takie subkomendy jak add
delete export import list sum oraz update tak jak
mówiłem wcześniej delete'a rozbiłem na dwie części tak
tutaj na minus a który usuwa wszystkie oraz na
zwykłego delete który usuwa po samych id ok ale
przejdźmy po ich po kolei ale już widzieliśmy teraz idąc
dalej list będzie po prostu wyświetlał wszystkie rzeczy które wyciągamy
repository dalej w streamie będziemy wszystko
printowali tak tutaj wykorzystując jakiś format żeby to było w miarę ładne tutaj
też wykorzystałem number format aby wyświetlić nasze currency też
to nam daje taki efekt że widzimy wtedy złotówki tak tutaj wstawiłem
też taba aby to w miarę ładnie wyglądało ok i na końcu printuje
ile wszystkiego było okej to było dość proste
okej później jeszcze mamy delete tutaj w delete
było trochę kodu związanego z tym aby wykminić o co chodziło użytkownikowi tak
więc było trzeba wykminić na przykład czy chodzi mi o usunięcie wszystkiego
i tutaj to zależało od tego co jest w tej opcji minus
a tak delete all opt no i wtedy usuwałem
wszystkie jeżeli coś takiego było a jeżeli tam nie było
tak no to jeszcze sprawdzałem czy id są i jeżeli id nie
było to wtedy trzeba było już rzucić wyjątkiem tak no bo coś się nie zgadzało no
i tutaj wywoływałem odpowiednie metody pod spodem one były dość proste jeszcze
zliczałem dodatkowo w przypadku usuwania które elementy zostały usunięte
tak jeżeli usuwałem po id to jeszcze zliczyłem jedynki
zera zrobimy na końcu sumę jeszcze tak no i dzięki
temu wiedziałem ile tak faktycznie zostało usunięte komenda update
była również dość prosta było praktycznie tyle samo opcji co
w przypadku add tak tylko był jeszcze parametr który
specyfikował jaki id jest update'owanego elementu tak tutaj
rankomat jest dość prosty w metodzie match
i w parameters obsłużyłem to że powiedzmy jeżeli te elementy
były nullami no to wtedy po prostu nie podmieniałem
tego co jest obecnie w danych parametrach prawda bo tutaj
wyciągnąłem najpierw używając get one z repozytorium
ten nasz outcome jeżeli go nie było no to wtedy tutaj jeszcze printowałem
że nie ma takiego gościa tak a no i później w
zależności od tego czy te parametry były nullami czy nie to updatetowałem poszczególne
pola to jest dość ważne z tego względu żeby nie ustawiać tutaj null no
bo jeżeli ustawimy nulla to tak jakbyśmy usunęli te kolumny tak usunęli wartość
tej kolumny w repozytorium okej tutaj eksport i import były
dość proste analogicznie się jej implementowało w eksporcie po prostu
dostawałem path on miał też domyślną nazwę tak
wykorzystywałem get all razem z compute with repository
a następnie wywoływałem po prostu w naszym eksporterze export w imporcie jest
identycznie tylko z tą różnicą że na początek tworzy loader'a
używając go robię load i następnie wykorzystując
add all dodaje te wszystkie outcome'y do repozytorium ostatnia komenda
to sum miała dwie opcje które były po prostu tymi datami
w postaci stringów tak i tutaj wywoływałem po prostu compute
sum z tego względu że nasz compute sum w naszym repozytorium obsłuży
sytuację że tutaj mamy nulla i po prostu nie zawęzi od tej strony naszego przydziału
mogłem bezpośrednio przekazywać po prostu te elementy jeszcze z tą
różnicą że musiałem tutaj wepchnąć null z tego względu że parse optional date
zwraca optional okej tak ja zaimplementowałem te
komendy ciekawi nie czy zaimplementowałeś je podobnie czy trochę inaczej w
każdym razie w kolejnej lekcji spróbujemy jeszcze spakować te wszystkie rzeczy z
zależnościami do jara i wtedy będziemy mieli gotowe już narzędzie do wykorzystania dzięki
wielkie i do usłyszenia