-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjava01.tex
More file actions
241 lines (202 loc) · 13.4 KB
/
java01.tex
File metadata and controls
241 lines (202 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
\documentclass[11pt]{article}
\usepackage[T1]{fontenc}
\usepackage[polish]{babel}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage{multirow}
\selectlanguage{polish}
\usepackage{graphicx}
\usepackage{listings}
\usepackage{minted}
\begin{document}
\definecolor{bg}{rgb}{0.95,0.95,0.95}
\definecolor{bgr}{rgb}{0.95,0.60,0.60}
Java jest wysokopoziomowym, kompilowanym, obiektowym językiem programowania z silną kontrolą typów. Oznacza to, że każda zmienna musi zostać zadeklarowana przed użyciem.
\section{Typy danych}
Zmienne w javie dzielą się na trzy rodzaje typów
\subsection{typy proste - prymitywy}
Typy prymitywne służą do reprezentacji danych, nie będących obiektami\footnote{istnieją również typy obiektowe reprezentujące typy proste}. Są one wbudowane w język. Ważną cechą jest, że ich wielkość (rozmiar) nie zależy od konkretnej implementacji maszyny wirtualnej. Wyróżniamy osiem typów prostych, które można dodatkowo pogrupować ze względu na \textit{charakter}: typy całkowite, zmiennoprzecinkowe, znakowe oraz logiczne.
\begin{table}[h]
\centering
\begin{tabular}{|c|c|c|}
\hline \textbf{typ wbudowany} & \textbf{rozmiar w bitach} & \textbf{zakres} \\
\hline \multicolumn{3}{|c|}{typy logiczne} \\
\hline boolean & 8 & true / false \\
\hline \multicolumn{3}{|c|}{typy całkowite} \\
\hline byte & 8 & $-128$ do $127$ \\
\hline short & 16 & $-2^{15}$ do $2^{15}-1$ \\
\hline int & 32 & $-2^{31}$ do $2^{31}-1$ \\
\hline long & 64 & $-2^{63}$ do $2^{62}-1$ \\
\hline \multicolumn{3}{|c|}{typy znakowe} \\
\hline char & 16 & $0$ do $2^{16}-1$ \\
\hline \multicolumn{3}{|c|}{typy zmiennoprzecinkowe} \\
\hline float & 32 & $1.4e^{-45}$ do $3.4028235e^{38}$ \\
\hline double & 64 & $4.9e^{-324}$ do $1.797e^{308} $ \\
\hline
\end{tabular}
\end{table}
\subsection{Konwersja typów numerycznych}
W przypadku konwersji typów należy rozpatrzeć dwa przypadki - niejawne - nie wymagające użycia operatora rzutowania oraz wymagające jawnego rzutowania.
Rzutowanie automatyczne zachodzi w przypadku przypisywania typu od mniejszej ,,dokładności'' do typu o większej (na diagramie są możliwe operacje, które obrazują czarne strzałki).
\begin{figure}
\centering
%\includegraphics[scale=0.4]{drawing.png}
\caption{Hierarchia typów}
\end{figure}
Przykładowe konwersje, które nie wymagają jawnego rzutowania, oraz nie powodują utraty danych.
\lstinputlisting{src/widening.java}
Możliwe jest też niejawne rzutowania, które powoduje utratę danych
\lstinputlisting{src/widening-loose.java}
W przykładzie powyżej kompilator nie zgłosi żadnego błędu.\\
W przypadku ,,poruszanie się'' po hierarchii typów w kierunku niezgodnym z kierunkiem strzałek, wymagane jest jawne rzutowanie. Konwersja ta wiąże się z ryzykiem utraty informacji. Przykłady:
\lstinputlisting{src/casting.java}
Aby wykonać rzutowanie, należy przez nazwą rzutowanej zmiennej postawić nazwę typu docelowego w okrągłych nawiasach.
\subsection{typy obiektowe}
\subsection{typy tablicowe}
Tablica jest rodzajem struktury danych będąca zestawieniem elementów tego samego typu. Dostęp do każdego z tych elementów można uzyskać za pomocą indeksu w postaci liczby typu int. Przykładowo, jeżeli $\textit{a}$ jest tablicą liczb całkowitych, to $\textit{a[i]}$ jest $\textit{i-tym}$ elementem tej tablicy.\\
Deklaracja zmiennej tablicowej polega na określeniu typu tablicy (czyli podaniu typu elementów i nawiasów kwadratowych $\textit{[]}$) i nazwy zmiennej
\begin{lstlisting}
int [] a;
\end{lstlisting}
Powyższa instrukcja tylko deklaruje zmienną $\textit{a}$. Nie inicjuje jej jednak tablicą (użycie tablicy, przypisanie lub pobranie jakiejś wartości, spowoduje błąd). Do utworzenia tablicy potrzebny jest operator $\textit{new}$
\begin{lstlisting}
int [] a = new int[100];
\end{lstlisting}
Powyższa instrukcja tworzy tablicę, w której można zapisać 100 elementów typy $\textit{int}$.\\
Tablice indeksowane są od zera. Oznacza to, że pierwszy element w tablicy znajduje się pod indeksem 0, natomiast ostatni po indeksem równym $\textit{długości tablicy - 1}$
\section{Przepływ sterowania}
\subsection{Instrukcje warunkowe}
Instrukcje warunkowe służą do podejmowania decyzji, czy dany kod ma być wykonany. Najprostszy warunek ma postać
\begin{lstlisting}
if (warunek_logiczny)
instrukcja
\end{lstlisting}
Warunek zawsze musi zwracać (być ewaluowany do) wartości logicznej true / false.
\lstinputlisting{src/if.java}
Po $\textit{if}$ mogą, opcjonalnie, wystąpić bloki wykonujące kod w przypadku gdy nie zostanie spełniony właściwy warunek.
\begin{lstlisting}
if (warunek_logiczny)
instrukcja1
else
instrukcja2
\end{lstlisting}
oraz
\begin{lstlisting}
if (warunek_logiczny)
instrukcja1
else if (warunek_logiczny)
instrukcja2
else
instrukcja3
\end{lstlisting}
Bloki $\textit{if()\{ \}}$, $\textit{if() \{ \} else \{ \}}$, $\textit{if() \{ \} else if() \{ \} else \{ \}}$ można dowolnie zagnieżdżać. Przykład
\lstinputlisting{src/if-example.java}
\subsection{Pętle}
Pętla $\textit{while}$ wykonuje blok instrukcji do póki zadany warunek ma wartość $\textit{true}$. Ogólna postać to
\lstinputlisting{src/while.java}
Jeżeli $\textit{warunek\_logiczny}$ będzie fałszywy przed rozpoczęciem wykonywania bloku $\textit{while}$, instrukcje wewnątrz bloku nie zostaną wykonane. Pętla $\textit{while}$ sprawdza warunek na samym początku działania. W związku z tym jej instrukcje mogą nie zostać wykonane ani razu. Aby mieć pewność, że instrukcje zostaną wykonane co najmniej raz, sprawdzanie warunku trzeba przenieść na sam koniec. Do tego służy pętla $\textit{do-while}$. Jej składnia jest następująca
\begin{lstlisting}
do {
instrukcje
} while( warunek_logiczny )
\end{lstlisting}
Najpierw wykonywany jest blok instrukcji, a następnie sprawdzany jest warunek.\\
Ostatnią przedstawioną będzie instrukcja wyboru $\textit{switch}$ (jest ona generalizacją instrukcji if). Składania jest następująca
\begin{lstlisting}
switch (warunek_wyboru) {
case wartosc1:
instrukcja1
case wartosc2:
instrukcja2
default:
instrukcja3
}
\end{lstlisting}
W instrukcji $\textit{switch}$ $\textit{warunek\_wyboru}$ nie jest wyrażeniem logiczny, które ewaluuje do wartości logicznej (true / false). $\textit{Warunek\_wyboru}$ powinien być liczą całkowitą (lub char)\footnote{lub enum}. Następnie instrukcje z $\textit{case}$, który spełnia warunek, są wykonywane kaskadowo w dół.
\lstinputlisting{src/switch.java}
W powyższym przykładzie, jeżeli wartość wejściowa będzie równa 0, zostanie wyświetlone '0'. W kolejnej linijce znajduje się instrukcja $\textit{break}$, która przerywa działanie $\textit{switch}$.
Jeżeli, wartość wejściowa będzie równa 1, zostaną wyświetlone następujące napisy: '1', '2,3'. '4' nie zostanie wyświetlone ponieważ, poprzedni blok kończy się instrukcją $break$. Jeżeli wartość wejściowa będzie równa, np. 1000, nie zostanie dopasowany żaden warunek i wykona się instrukcja z $\textit{default}$
\section{Klasy}
Program napisany w javie składa się z obiektów. Każdy z nich posiada określony stan, zachowanie, tożsamość. Stan określony przez zmienne, zachowanie definiujemy w metodach, które mogą zmieniać aktualny stan obiektu. Tożsamość pozwala odróżnić dwa obiekty, posiadające ten sam stan, od siebie. \\
Aby utworzyć obiekt, potrzebujemy szablonu, który definiuje dostępne zachowania albo pola, które będą przechowywały stan.\\
\subsection{Tworzenie klas}
Klasy przechowywane są w plikach tekstowych (zwyczajowo z rozszerzeniem 'java'). Zdefiniowanych jest szereg reguł dotyczących stworzenia klasy oraz ich struktury
\begin{itemize}
\item w pliku źródłowym może być zdefiniowana tylko jedna klasa $\textit{publiczna}$. Może nie być żadnej.
\item komentarze mogą znajdować się w dowolnym miejscu w pliku
\item nazwa pliku musi być identyczna do nazwy klasy $\textit{publicznej}$. Przykładowo klasa $public class Point { }$ musi znajdować się w pliku o nazwie $Point.java$
\item jeśli klasa umieszczona jest w pakiecie, deklaracja pakietu musi znajdować się w pierwszej linijce (nie uwzględniając komentarzy)
\item definicje importów muszą znajdować się pomiędzy deklaracją pakietu (lub początkiem pliku) oraz definicją pierwszej klasy
\item definicja pakietu oraz importów odnoszą się do całego pliku
\item w pliku źródłowym mogą znajdować się definicje $\textit{nie-publicznych}$ klas
\end{itemize}
Najprostsza definicja klasy składa się z słowa kluczowego $\textit{class}$ oraz nazwy klasy.
\begin{lstlisting}
class Point { }
\end{lstlisting}
Klasa $\textit{Point}$ zdefiniowana w powyższy sposób posiada dodatkowo niejawny modyfikator dostępu.
\subsection{Modyfikatory dostępu}
Służą do określenia 'widzialności' klasy w obrębie projektu, dzięki temu kontrolujemy dostęp do klas.
Dostępne są cztery poziomy dostępu oraz trzy modyfikatory dostępu, są to kolejno
\begin{itemize}
\item private - dane dostępne tylko w ramach danej klasy
\item protected - dane dostępne są z poziomu pakietu (klasy będące w tym samym katalogu) oraz klasy dziedziczące
\item public - dane dostępne są bez ograniczeń
\end{itemize}
Ostatni poziom dostępu nie ma powiązanego słowa kluczowego, jeśli nie zdefiniujemy inaczej, zawsze jest $\textit{nadany}$ jako domyślny. Stąd nazywany jest modyfikatorem domyślnym $\textit{default}$ lub pakietowym.
Dane z domyślnym dostępem są widoczne tylko i wyłącznie w zakresie danego pakietu. Modyfikator $\textit{default}$ zachowuje się podobnie do $\textit{protected}$ z tym, że dane nie będą widoczne w czasie dziedziczenia.\\
Przez widoczność klasy, metody lub pola rozumiemy operacje:
\begin{itemize}
\item utworzenie instancji klasy
\item dziedziczenie
\item wywołanie na obiekcie metody lub dostęp do pola
\end{itemize}
Kilka przykładów
\begin{lstlisting}
//Triangle.java
package shapes;
class Triangle { }
\end{lstlisting}
\section{Interfejs}
Tworząc interfejs definiujemy rodzaj kontraktu opisującego co dana klasa ma robić, nie wspominając o tym w jaki sposób ma tego dokonać.
Przykładowo, mamy obiekt $\textit{Calculator}$ wykonujący obliczenia na obiektach reprezentujących figury geometryczne.
\inputminted[bgcolor=bg,linenos=true]{java}{src/w05/Calculator.java}
Dla każdego typu obiektu, klasa Calculator, posiada jedną metodę wykonującą obliczenia dla niego. Zmiana zasad przeprowadzania obliczeń wymusza zmianę kodu w trzech miejscach. Np. jeśli pole powierzchni figury musi być przeliczone na inne jednostki niż zwracane przez metodę getArea(). Podobnie gdy dodamy nowy typ reprezentujący prostokąt, zmuszeni będziemy do zmiany klasy Calculator.
\inputminted[bgcolor=bg,linenos=true]{java}{src/w05/Calculatornewshape.java}
M.in. w takim przypadku stosuje się interface, który sprowadza figury geometryczne do opisu zachowania, bez definiowania konkretnej implementacji. Wprowadzając interface Shape.java
\inputminted[bgcolor=bg,linenos=true]{java}{src/w05/Shape.java}
definiujemy wspólne API dla wszystkich klas, które będą go implementowały. Po utworzeniu interface musimy 'poinformować' klasę Calculator, że metody będą pracowały na czymś co jest 'Shape' bez określenia konkretnego typu.
\inputminted[bgcolor=bg,linenos=true]{java}{src/w05/CalculatorRefactor.java}
Dzięki takie operacji możemy pozbyć się pozostałych metod calculate. Następnie, każda z figur musi implementować $\textit{typ Shape}$
\inputminted[bgcolor=bg,linenos=true]{java}{src/w05/Calculatorinterface.java}
Zalety takie podejścia:
\begin{itemize}
\item wspólne API
\item w przypadku zmiany sposobu obliczeń w klasie Calculator zmiany kodu tylko w jednymi miejscu
\item mniejsza liczba kodu testującego
\item w przypadku wprowadzenia nowego kształtu (Rectangle) nie ma potrzeby wprowadzania zmian w klasie Calculator
\end{itemize}
\subsection{Właściowości}
\begin{itemize}
\item nie można utworzyć instancji interfejsu
\item interfejs nie może posiadać konstruktora
\item interfejs nie posiada pól instancyjnych
\item klasy implementują interfejs
\item interfejsy rozszerzają inne interfejsy
\end{itemize}
\begin{minted}[bgcolor=bgr]{java}
Shape shape = new Shape();
\end{minted}
Ponieważ interfejs nie posiada ciała, ani konstruktora nie można utworzyć nowej instancji przy pomocy operatora $\textit{new}$.
Interfejs może jedynie być użyty jako typ referencyjny.\\
\begin{minted}[bgcolor=bg]{java}
Shape shape = new Rectangle();
\end{minted}
Na referencji $\textit{shape}$ można wywołać wszystkie metody, które zostały zdefiniowane w interfejsie $\textit{Shape}$.
\section{Klasa abstrakcyjna}
W przeciwieństwie do interfejsu, który jest całkowicie abstrakcyjną klasą bazową, pozbawioną implementacji, słowo kluczowe abstract pozwala na zdefiniowanie typu abstrakcyjnego, będącego czymś pomiędzy interfejsem, a klasą konkretną.\\
Klasa abstrakcyjna może posiadać metody konkretne (posiadające ciało) oraz metody abstrakcyjne wymagające implementacji. Podobnie jak w przypadku interfejsów tworzenie obiektu klasy abstrakcyjnej nie ma sensu, możemy natomiast dostarczyć konstruktor.
\inputminted[bgcolor=bg]{java}{./src/w05/Polygon.java}
Klasa abstrakcyjna może rozszerzać inne klasy (abstrakcyjne lub konkretne) lub implementować dowolny interfejs. Należy pamiętać o ograniczeniu, mówiącym, że można rozszerzać tylko dziedziczyć po jednej klasy w danym momencie.
\inputminted[bgcolor=bg]{java}{./src/w05/Polygon.ext.java}
\end{document}