2.2 Klasyczne konstrukcje programowania imperatywnego w Javie

Wewnętrzne typy danych

Język Java udostępnia programiście szereg standardowych typów wartości, z których większość można spotkać także w innych językach programowania. Typy te nazywane są w oryginale 'intrinsic types', co przetłumaczyliśmy jako "typy wewnętrzne". W poniższej tabeli zawarto opis wewnętrznych typów danych, zawierający dla każdego typu jego nazwę oraz wielkość pamięci zajmowanej przez poszczególne wartości typu.

Typ danych Rozmiar

(w bitach)

boolean 8
byte 8
char 16
short 16
int 32
long 64
float 32
double 64

Tabela 2-1 Zestawienie typów wewnętrznych w Javie

Zmienne typu char są reprezentowane na 16 bitach ponieważ możemy jako wartość zmiennej typu char użyć dowolnego znaku ze zbioru znaków Unicode.

Literały

W Javie mamy do dyspozycji następujący zbiór literałów: literały całkowite (dziesiętne, ósemkowe i szesnastkowe), odpowiadające typom int i long, literały rzeczywiste (stałoprzecinkowe i zmiennoprzecinkowe), odpowiadające typom float i double, literały znakowe i łańcuchowe, związane z typem char i String oraz literały logiczne reprezentujące dwie wartości logiczne typu boolean.

Dziesiętne literały całkowite należące do typu int są ciągami cyfr dziesiętnych, ewentualnie poprzedzonymi znakiem. Umieszczenie na końcu literału całkowitego znaku 'L' lub 'l' wskazuje, że mamy do czynienia z literałem typu long. Literały o wartościach większych niż '2.147.483.647' dziesiętnie automatycznie przyjmują postać liczby typu long.

Literały rozpoczynające się znakiem '0' są liczbami ósemkowymi, natomiast literały zaczynające się sekwencją '0x' lub '0X' stanowią liczby szesnastkowe. Do zapisu cyfr szesnastkowych można używać zarówno małych jak i wielkich liter alfabetu (od A do F).

Literały rzeczywiste traktowane są jako liczby typu double. Możemy jednak zmienić to standardowe przypisanie i wymusić interpretację liczby jako wartości typu float przez dodanie na końcu literału litery 'F' (lub 'f'). Możliwe jest także jawne przypisanie literałowi typu double poprzez dodanie litery 'D' lub 'd'.

Poniżej prezentujemy literały numeryczne: całkowite i rzeczywiste:

169, 0251, 0xA9, 0XA9, 0xa9 - literały całkowite typu int i long

4.565556, 0.2, 50.0, 30.5E-11 - literały rzeczywiste typu double

4.565556f, 0.2F, 50.0f, 30.5E-11f - literały rzeczywiste typu float

4.565556d, 0.2D, 50.0d, 30.5E-11d - literały rzeczywiste typu double

Istnieją dwa literały typu boolean- są nimi stałe true i false , reprezentujące wartości "prawda" i "fałsz". Należy zauważyć, że odmiennie, niż w języku C, nie można dokonywać konwersji wartości typu boolean do wartości typu int.

Zwykłe literały znakowe zapisujemy w apostrofach. Można definiować także literały reprezentujące znaki w standardzie Unicode. W tym celu używamy notacji '\xNN', gdzie NN jest wartością Unicode znaku.

W Tabeli 2-2 zestawiono literały służące do zapisu znaków specjalnych

Opis Literał
New line (znak nowej linii) \n
Horizontal tab (tabulacja pionowa) \t
Backspace \b
Carriage return (powrót karetki) \r
From feed (znak nowej strony) \f
Single quote (apostrof) \'
Double quote (cudzysłów) \"
Backslash (lewy ukośnik) \\

Tabela 2-2 Literały znaków sterujących

Literały łańcuchowe są sekwencjami znaków ujętymi w cudzysłów. Literały tego typu w Javie nie są, jak to ma miejsce w C/C++, tablicami znaków zakończonymi znakiem pustym.

Przykłady definicji literałów znakowych:

' ' - spacja,

'\\' - lewy ukośnik,

'K' - znak K,

'\x20' - spacja.

Napis:

 "To literał"

jest przykładem literału łańcuchowego.

Wyrażenia

Do tworzenia wyrażeń, oprócz zmiennych i wywołań funkcji, służą operatory, z których większość zaczerpnięto z języków C i C++. Jednakże, w Javie wprowadzono szereg zmian dotyczących operatorów - omówimy je kolejno.

Po pierwsze, udostępniono nowy operator '>>>', oznaczający przesunięcie bitowe w prawo, bez znaku (postaci: op1>>>op2, gdzie op2 liczbą określającą o ile pozycji nastąpi przesunięcie). Przyjęto także zasadę, że operatory nie mogą być przeciążane. Ponadto, jeśli argumentami operatorów: '&', '|' oraz '^' są liczby, to traktuje się je jako operatory bitowe, odpowiednio: bitowej sumy, iloczynu i "wyłączającego ALBO", natomiast dla argumentów typu boolean, wymienione operatory są operatorami: sumy logicznej, iloczynu logicznego i wyłączającego ALBO logicznego (ang. "exclusive or" XOR).

Zastosowanie operatorów logicznych '&' i ' |' różni się od użycia dostępnych także operatorów '&&' i '||' tym, że podczas ich wartościowania nie jest stosowana zasada "short-circuit evaluation", polegająca, w ogólności, na obliczaniu wartości składowych wyrażenia logicznego tylko wtedy, gdy wpływają one na wartość całego wyrażenia, na przykład w wyrażeniu:

 (6 > 2) | true

po obliczeniu wartości '6>2' znana jest wartość całego wyrażenia i nie ma potrzeby dalszego wartościowania. Gdy jednak chcemy, aby drugi argument operatora '|' nie był wyliczany, stosujemy operator '||'.

Poniższa tabela zawiera zestawienie operatorów języka Java.

Priorytet Operator Priorytet Operator
1 . [] () 9 ^
2 ++ -- ! ~ instanceof 10 |
3 * / % 11 &&
4 + - 12 ||
5 << >> >>> 13 ?:
6 < > <= >= 14 = op=
7 == != 15 ,
8 &    

Tabela 2-3 Zestawienie operatorów Javy w porządku wzrastających priorytetów

Wyrażenie 'op=', gdzie 'op' jest jednym z operatorów: ( '+', '-', '*', '/', '%', '&', '|', '^', '<<', '>>', '>>>') , stanowi definicję zbioru operatorów złożonych, łączących operator binarny 'op' i operator przypisania '=', w następujący sposób:

a op= b; jest równoważne a = a op b;

Wartościami wyrażeń zawierających operatory relacyjne: '>', '<', '>=', '<=', '== 'i '!=' są stałe typu boolean, które nie mogą być przekształcane, jak to ma miejsce w języku C, do wartości typu całkowitego.

Dla operatorów binarnych, w większości przypadków, występuje w Javie zjawisko automatycznej konwersji zmiennych, zadeklarowanych jako: byte, char i short do typu int, co często prowadzi do wystąpienia błędu w instrukcji przypisania.

Dla zmiennych a, b, c typu byte, zadeklarowanych np. tak:

byte a;
byte b=2;
byte c=3;

w poniższej instrukcji występuje błąd przypisania, spowodowany automatyczną konwersją typu zmiennych.

a=b*c; // błąd przypisania wyniku typu int do zmiennej typu byte! 

Przyczyną wystąpienia błędu w instrukcji przypisania jest dokonanie, przed wykonaniem operacji mnożenia, automatycznej konwersji typu zmiennych b i c do typu int. Aby uniknąć tego błędu, musimy zastosować konwersję wyniku operacji (typu int) do typu byte:

a=(byte)(b*c); //to wyrażenie jest poprawne.

Zmienne

Wszystkie zmienne w Javie muszą być zadeklarowane, zanim zostaną użyte. Deklaracja zmiennej może znajdować się w dowolnym miejscu definiowanej metody (pojęcie metody jest równoważne identycznemu pojęciu w języku C++).

Deklaracja zmiennej składa się z nazwy typu zmiennej, za którą znajduje się nazwa zmiennej oraz, opcjonalnie, wyrażenie nadające wartość początkową zmiennej. Cały napis zakończony jest średnikiem. Deklarację tę możemy przedstawić schematycznie w następujący sposób:

NazwaTypu NazwaZmiennej [ = WartośćPoczątkowa ];

gdzie napis w nawiasach '[' i ']' oznacza część opcjonalną deklaracji.

Nazwa musi zaczynać się literą alfabetu, znakiem podkreślenia ' _ ' lub znakiem dolara '$'. Dalej, identyfikator może zawierać cyfry lub litery alfabetu. W skład alfabetu wchodzą znaki:

Oznacza to zatem, że napisy 'Mąka' i 'Tëmço' są poprawnymi identyfikatorami Javy.

W Javie zastosowano mechanizm nadawania zmiennym wewnętrznym domyślnych wartości początkowych - w Tabeli 2-4 zestawiono te wartości.

Typ danych Wartość domyślna
boolean false
byte 0
char 'x0'
short 0
int 0
long 0
float 0.0F
double 0.0D

Tabela 2-4 Zestawienie wartości domyślnych dla typów wewnętrznych

Przykładowe deklaracje zmiennych:

//deklaracja zmiennej typu całkowitego z nadaniem jej
// wartości początkowej '5'
int nLiczbaPowrotow = 5; 
// deklaracja zmiennej typu boolean z nadaniem jej 'true'
boolean bMozna = true;
// deklaracja zmiennej typu boolean bez nadania wartości 
// początkowej, zostanie więc przypisana domyślna wartość 'false'
boolean bKoniec; 

W rozdziale tym omówiono zmienne typów wewnętrznych, deklaracja zmiennych typów obiektowych wygląda podobnie z tym, że w opcjonalnej części deklaracji zmiennej (wyrażeniu nadającym wartość początkową), może zostać utworzony nowy obiekt, odpowiedniego typu obiektowego. Deklarowanie zmiennych obiektowych omówione zostało w punkcie 2.3.6.

Instrukcje

Wprowadzenie

Zbiór instrukcji języka Java, wzorowany na zbiorze instrukcji języka C, zawiera:

W kolejnych podpunktach, omówimy postaci składniowe i semantykę wymienionych instrukcji.

W Javie do dyspozycji mamy następujące instrukcje sterujące: if, for, while, switch.

Instrukcje warunkowe

Instrukcja warunkowa if przyjmuje postać:

if (wyrażenie_logiczne) instrukcja  else instrukcja ;    

lub

if (wyrażenie_logiczne) instrukcja

Wykonanie instrukcji polega na obliczeniu wartości wyrażenia_logicznego i, jeśli jest ona równa true, to wykonaniu podlega pierwsza instrukcja, w przeciwnym przypadku (dla wartości równej false), wykonywana jest instrukcja umieszczona po słowie kluczowym else (jeśli występuje) lub następna instrukcja programu.

W Javie istnieje także możliwość łatwego programowania warunków, przyjmujących wartości ze zbioru większego, niż dwuelementowy (jak to ma miejsce w instrukcji if). Stosujemy wtedy instrukcję wyboru switch o postaci:

switch(wyrażenie_warunkowe)
{
	case stała1: instrukcja	
	case stała2: instrukcja
	...
	case stałaN: instrukcja
	[default: instrukcja]
}   

W pierwszym kroku wykonania instrukcji obliczana jest wartość wyrażenia_warunkowego (które przyjmuje wartości całkowite), a następnie porównywana jest ona kolejno z wartościami stałych umieszczonych po słowach case. Sterowanie jest przekazywane do tej instrukcji, w której wartość stałej jest równa wartości wyrażenia_warunkowego. Jeśli żadna stała nie ma wartości identycznej z wartością wyrażenia, to wykonywana jest instrukcja umieszczona po słowie default (jeśli jest zdefiniowane) lub następna instrukcja w programie.

Instrukcje iteracyjne

W Javie mamy do dyspozycji klasyczny zestaw instrukcji iteracyjnych:

Instrukcja while przyjmuje postać:

while (wyrażenie_logiczne) instrukcja

Najpierw obliczana jest wartość wyrażenia_logicznego. Jeśli jest ona równa true, to wykonywana jest instrukcja, gdy zaś wartość ta równa jest false - sterowanie przekazywane jest do następnej instrukcji programu. Po wykonaniu instrukcji, ponownie sprawdzana jest wartość wyrażenia_logicznego i procedura powtarza się.

Instrukcję do...while można zapisać:

do instrukcja
while (wyrażenie_logiczne);

Wykonanie tej instrukcji różni się od wykonania poprzedniej tym, że warunek wyrażenie_logiczne jest sprawdzany po każdym wykonaniu instrukcji, a nie przed, jak to ma miejsce w przypadku instrukcji while.

Należy zauważyć, że jest możliwa zmiana opisanego wyżej trybu wykonania instrukcji iteracyjnych poprzez zastosowanie instrukcji sterujących break i continue, które omówimy później .

Na koniec, zajmiemy się instrukcją for o następującej składni:

for (wyrażenie_1; wyrażenie_logiczne; wyrażenie_3) instrukcja

Wykonanie instrukcji for przebiega w następujący sposób:

  1. Obliczana jest wartość wyrażenia_1
    1. Obliczana jest wartość wyrażenia_logicznego. Jeśli wartość obliczona jest równa true, to wykonywane jest instrukcja, w przeciwnym przypadku, sterowanie przekazywane jest do następnej instrukcji programu
    2. Obliczana jest wartość wyrażenia_3
  2. Powrót do kroku 2.

Zmienne zadeklarowane w wyrażeniu_1 są widoczne tylko w ciele instrukcji for - po wykonaniu pętli, nie mogą być one ponownie użyte. Ta cecha deklaracji zmiennych wykazuje istotną różnicę z analogiczną deklaracją w języku C++, gdzie zmienne zadeklarowane w instrukcji for są widoczne są poza nią.

Instrukcje sterujące

Instrukcje break i continue pozwalają na wcześniejsze opuszczenie pętli. Jeśli break występuje bez etykiety (ang. label) to sterowanie programem przekazywane jest poza instrukcję while lub for. Jeśli instrukcja break posiada etykietę to sterowanie przekazywane jest poza blok instrukcji oznaczonych etykietą.

Przykład 2.1 Użycie instrukcji break z etykietą
Przykład ten ilustruje zastosowanie instrukcji break z etykietą do sterowania wykonaniem programu w Javie:

int i=0;
int k=10;
NaszBlok:
	{	
		while(i++<10)
		{	
			if (--k==0)
				break NaszBlok;
			System.out.println(i/k); 
			// wyświetlenie na ekranie wyniku dzielenia
		}
		//Tu zostałoby przekazane sterowanie gdyby instrukcja 	
		//break nie miała etykiety NaszBlok.
	} //koniec bloku instrukcji NaszBlok 
	//Tu zostaje przekazane sterowanie programem po wykonaniu 	
	//instrukcji break NaszBlok

Użycie instrukcji break z etykietą pozwala na natychmiastowe opuszczenie wielu zagnieżdżonych instrukcji while.

Instrukcja continue nie powoduje opuszczenia pętli ale natychmiastowe przejście do następnego kroku iteracji. Jeśli użyjemy instrukcji continue z etykietą sterowanie przekazywane jest do nawiasu otwierającego blok instrukcji oznaczonych etykietą.

Blok instrukcji

Instrukcja złożona to ciąg instrukcji pomiędzy nawiasami { i }, nazywana blokiem instrukcji.

Jeśli zadeklarujemy w bloku zmienną to będzie ona widoczna tylko w tym bloku.

Przykład 2.2 Widoczność zmiennych w bloku instrukcji
Zmienna ta została zadeklarowana tu dwukrotnie, dodatkowo jeszcze jako zmienna różnych typów:

{
	{
		int i = 10;
	}
	{
		long i = 5; // możemy tu ponowanie zadeklarować zmienną 
			// nazwie 'i', ponieważ nie jest tu widoczna zadeklarowana 
			// we wcześniejszym bloku zmienna o tej samej nazwie
	}
}