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.
Kolejną rzeczą za którą się weźmiemy to jest import
i eksport do pliku tak jak w przypadku repozytorium
najlepiej zacznijmy od tworzenia interfejsów które zdefiniują jakie metody będziemy
chcieli na naszych klasach wywoływać zauważ że to może być
eksport do pliku który może być csv który może być jsonem czy jakimś
innym formatem pliku z tego względu warto by było
sobie zrobić właśnie interfejs który będzie miał ileś implementacji tak my
oczywiście będziemy mieli jedną ale warto to sobie wyeksportować
do jakiegoś interfejsu stworzyłem interfejs do eksportowania
oraz loadowania danych z plików outcome'ów odpowiednio outcome
exporter i outcome loader oraz domyślne dla nich implementacje
ten interfejsy po prostu w przypadku eksportu dostają listę
naszych outcome'ów w przypadku loadu zwracają listę
outcome'ów już to tak jakby dane które są potrzebne
tym powiedzmy co to jest za plik do którego
pliku on ma wrzucić z jakiego pliku ma odczytać to jest taki zamysł żeby
te dane przekazywać przez konstruktor dzięki
temu będzie to niezależny tak jakby nie będzie musiał tych danych wrzucać w naszym interfejsie
oczywiście moglibyśmy tutaj przekazywać jakiś kontekst czy coś takiego jakąś taką generyczne
jakiś interfejs który może wytrzymać takie dane okej też
zauważ że przeniosłem to wszystko do pakietu dump tak
tak jakby dobrą praktyką jest przeniesienie tak
jakby rozdzielanie w pakietach jeżeli mamy tutaj taki
trochę odrębny feature tak tutaj mamy repozytorium oraz jego inkrementację a
tutaj mamy rzeczy związane właśnie z dumpem z czytaniem z tego
dumpu oraz implementację przeniosłem do pakietu
csv z tego względu że to są rzeczy które implementują odpowiednio
czytanie i zapisywanie do csv właśnie okej przy
implementacji eksportu i loadu również będziemy wykorzystywali testy więc
wejdźmy sobie na przykład do csv outcome exporter oraz stwórzmy
sobie test załóżmy że w jednej klasie testowej będziemy chcieli testować
export i load na raz z tego względu nazwijmy naszą
klasę testową csv export load test
na początek dodajmy sobie trochę outcome'ów abyśmy mieli na czym testować stwórzmy
sobie następnie metodę testową która będzie sprawdzała eksport
a następnie czy to co wyeksportowaliśmy da się też zaloadować do naszego
programu po napisaniu naszego testu może on wyglądać w ten sposób
tworzymy sobie tak jak w przypadku naszej bazy danych tymczasowy
plik oraz tworzymy sobie listę naszych outcome'ów
a następnie po stworzeniu exportera oraz loadera
wywołujemy export a następnie load oraz porównujemy
po prostu te listy na koniec tak zakładamy też tutaj że jeżeli
to zostało wyeksportowane w jakiejś kolejności to również będzie zaloadowane w takiej
samej kolejności tutaj też dodaliśmy konstruktorach nasz temp file
tak file path do pod jakim ma być powiedzmy robiony export
i import okej przejdźmy teraz do implementacji okej
zacznijmy od implementacji metodą export w naszym eksporterze pierwsza rzecz
którą musimy zrobić to jest przemapowanie naszych outcome'ów na wiersze w csv
to przemapowanie wydzieliny do metody map to csv rows wewnątrz tej metody
przejedziemy po wszystkich outcome'ach i wywołamy metodę mapującą je na
stringi oraz zapiszemy do listy i wrócimy tutaj w metodzie tworzącej
jeden wiersz musimy pamiętać o tym że to jak zapiszemy tutaj
powiedzmy te wartości po przecinkach w ten sam sposób będziemy
musieli je odczytać później tak czyli jeżeli robimy to stringa na dacie
to później będziemy musieli zrobić parse już ostatnia
rzecz która nam pozostaje wtedy to zapisanie po prostu tych wierszy do pliku na
przykład wykorzystując metodę write na files jeszcze jedną
rzeczą którą warto też zauważyć to jest w sposób w jaki ja tutaj zapisuje
te dane powiedzmy ja to wyrzucam po prostu string prawda dla
każdego z tych elementów i na przykład w komentarzu może się
zdarzyć że na przykład zostanie umieszczony przecinek załóżmy
na razie że on tam się nie może pojawić ale chcesz żeby na przykład w twojej implementacji
był prawidłowo obsłużony to powinniśmy tutaj escape'ować
powiedzmy takie rzeczy to znaczy w standardzie csv jest
dokładnie pokazane w jaki sposób powiedzmy zapisywać
takie dane tak się wtedy powiedzmy otacza double coatami czyli
takimi znakami oraz każde wystąpienie na przykład
double quote wewnątrz takiego powiedzmy fieldu też
musi być escape'owane drugim takim double coatem tak możesz
na przykład jeżeli chcesz to rozszerzyć tak powiedzmy pozwolić na wykorzystywanie przecinków
czy później double coatów w twoim powiedzmy właśnie komentarzu
no to wtedy możesz wykorzystać na przykład istniejący jakiś parser
i encoder powiedzmy właśnie do csv no
albo masz spróbować samodzielnie to zaimplementować wykorzystując po prostu regexy okej
teraz zajmijmy się mechanizmem wczytywania danych z pliku tutaj flow
będzie bardzo prosty odczytujemy wszystkie dane z naszego pliku linia po linii tak z takiego
pliku o takim pathie zamieniamy to na strumień później możemy na
każdym elemencie tego strumienia wywołać magiczną metodę map to outcome taką
która zaraz powiedzmy będzie nam mapowała te stringi stąd na
outcome'y później zamieniamy to na listę i zwracamy oczywiście też dbamy
o złapanie wyjątków tutaj nam się mogą pojawić a mianowicie tutaj wywołanie
tego read all my to gdzie map to outcome pierwszą rzecz którą robimy to jest
podzielenie tego wejściowego wiersza po przecinkach następnie
warto sprawdzić czy liczba otrzymanych w ten sposób powiedzmy
właśnie stringów tak jest równa oczekiwanej tak czyli
w tym przypadku powinniśmy dostać trzy elementy w tej tablicy tak w razie
czego rzucamy wyjątkiem wykorzystując otrzymaną tablice możemy uzupełnić
też outcome tak tutaj ważne jest aby odpowiednio sparsować
te elemonty które otrzymujemy wykorzystujemy tutaj metodę parse w przypadku
daty oraz konstruktor w decimalu warto zauważyć
że metoda parse też może nam rzucić wyjątki tak oraz tutaj
w przypadku decimala też możemy dostać wyjątek warto więc też złapać
te wszelkie wyjątki które tutaj dostaniemy oraz przemapować go na jakiś nasz wyjątek
może to być na przykład runtimemowy exception tak będzie to wygodne
możemy też stworzyć naszą własną klasę wyjątku na razie nam runtime
exeption wystarczy ważne żeby po prostu dać tą informację co tu się wydarzyło prawda
okej w ten sposób zaimplementowaliśmy nasz loader zobaczmy teraz
czy test będzie przechodził jak widzimy nasz test nie przeszedł tutaj wszystko
zostało dobrze zaimplementowane tylko tutaj właśnie nadzialiśmy
się na jedną rzecz związaną z tym że metoda assert equals wykorzystuje
equalsy w tym momencie my porównujemy dwie listy pod spodem trochę prawdopodobnie
są array listami oraz te listy też przy
ich porównywaniu sprawdzają czy ich elementy w kolejności i powiedzmy swoimi
equalsami sobie odpowiadają no i tutaj właśnie nadziewamy się na to że masz outcome nie
ma metody equals i hash code przez nas zdefiniowany
więc wykorzystuje tą domyślną implementację czyli porównuje po prostu referencje
a to siłą rzeczy powiedzmy tutaj one nie mogą być takie same
ponieważ to są dwa razy utworzone obiekty raz tworzymy jeden outcome
zanim chcemy go zserializować a następnie po jego deserializacji
tworzymy drugi taki sam ale to już nie jest ten sam obiekt prawda w
tym celu jeżeli chcemy powiedzmy wykorzystywać właśnie equalsa tutaj jak przy tych asercjach
możemy sobie zaimplementować naszą wersję equalsa i hash code prawda jeżeli
implementujemy equalsa to hash code z automatu też powinniśmy z
tego względu że id może być nadawany dopiero
po dodaniu do bazy tak albo może być w zależności
od implementacji tak jak u nas w repozytorium nie jest nadawany do
tego obiektu po dodaniu do bazy ale dopiero jeżeli wyciągamy z bazy jest nadawany
ten id w tym celu możemy sobie na razie zrobić żeby ten id
nie był brany pod uwagę w equals i hash code okej i teraz zobaczmy czy nasz
test zacznie przychodzić jak widzimy test przeszedł warto teraz
dodać sobie jeszcze jeden corner case czyli powiedzmy taki przypadek brzegowy
dodajemy sobie test który sprawdza jeszcze czy load i export
działa w momencie kiedy eksportujemy pustą listę i
później ją wczytujemy tak czyli z pustej csv jeszcze okej
ten test będzie wyglądał analogicznie tylko ta lista wyjściowa jest po prostu pusta jak
widzimy on też będzie przechodził prawda w ten sposób zaimplementowaliśmy
nasze klasy odpowiadające za eksport oraz load które
będziemy mogli wykorzystać później przy spinaniu tych wszystkich rzeczy wykorzystując
okej dzięki wielkie za tę lekcję i do usłyszenia