2. Literały
O typy literałów troszczy się sam kompilator, stosując przy tym pewne reguły, mianowicie:
- każda liczba całkowita zapisana literalnie (np. 100) traktowana jest jako liczba typu int.
-
każda liczba rzeczywista zapisana literalnie (z kropką dziesiętną - np. 1.2,
albo w notacji naukowej np. 1e+9) traktowana jest jako liczba typu double,
- każdy literał znakowy (znak w apostrofach) jest typu char
- każdy napis true lub false jest literałem logicznym (typ boolean); słowa true i false są słowami zarezerwowanymi języka.
W niektórych przypadkach programista może zmienić interpretację literałów liczbowych, stosując specjalne modyfikatory, mianowicie:
- użycie litery L lub l jako przyrostka liczby całkowitej sprawia, że
liczba ta będzie traktowana jako typu long (np. 3L - będzie literałem typu
long)
- użycie f lub F spowoduje, że liczba rzeczywista będzie traktowana jako typu float (np. 0.3f)
Liczby całkowite mogą być zapisywane:
- w systemie dziesiętnym - w naturalny sposób (np. 3 lub 121),
- w systemie ósemkowym - poprzez poprzedzenie liczby znakiem 0 (np. 03),
-
w systemie szesnastkowym - poprzez poprzedzenie liczby znakami 0x lub 0X
(np. 0x1a, 0X11, 0xFF - zwróćmy uwagę, że cyfry szesnastkowe powyżej 9 mogą
być zapisywane dużymi lub małymi literami)
Liczby rzeczywisty mogą być zapisywane w notacji naukowej (inżynierskiej)
z wykorzystaniem litery e lub E np. 2e+9 oznacza 2 pomnożone przez 10 do potęgi 9, 3e-11
oznacza 3 pomnożone przez 10 do potęgi -11.
Po co są te wszystkie dodatkowe napisy (przyrostki, modyfikatory) ? Sprawa
jest oczywista w przypadku zmiany podstawy systemu liczbowego. Literał 10
jest traktowany jako zapis w sytemie dziesiętnym ( i ma wartość 10). Jeżeli
chcemy "dać znać", że chodzi nam o liczbę 10 zapisaną w systemie szesnastkowym
(a jej wartośc dziesiętnie jest już zupelnie inna - 16), to musimy użyć jakiejś
konwencji (w przypadku Javy przedrostka 0x lub 0X).
Ale modyfikatory L czy F - po co?
Otóż, niestety kompilator Javy przypisując typ literałom liczbowym nie kieruje
się wartością zapisanych literalnie liczb. Jak powiedziano - wszystkie literały
całkowitoliczbowe traktuje jako typu int, a rzeczywiste (czyli z kropką dziesiętną
lub zapisane w notacji naukowej) - jako double.
Zwróćmy jednak uwagę: dane typu int zajmują tylko 4 bajty w pamięci i największa
liczba, którą można zapisać w tych czterech bajtach - to 2147483647 (dlaczego
właśnie taka - wyjaśnimy sobie w jednym z następnych wykładów). Jeśli zatem
zapiszemy w programie literał 2147483649, to będzie on potraktowany jako
liczba typu int, tylko niestety nie zmieści się ona w obszarze przeznaczonym
na przechowywanie takich liczb. Kompilator rozpozna tę sytuację i zgłosi
błąd. Powinniśmy zatem zapisać literał jako 2147483649L.
Gorszy przypadek: jeśli zapiszemy operację dodawania dwóch literałów 2147483647
+ 10, to obie liczby będą typu int (i faktycznie zmieszczą się w obszarach
4 bajtowych). Kompilator nie potrafi przeprowadzić sumowania, wie tylko,
że dodawane są dwie liczby typu int i zakłada, że wynik będzie też typu int.
Zatem wydziela "pod wynik" tego wyrażenia obszar 4 bajtowy. A w 4 bajtach wynik się nie
mieści i dostaniemy nieprawidłowy, bardzo dziwny, rezultat (bez żadnego błędu
w kompilacji ani w fazie wykonania). Aby tego uniknąć znowu musimy podać
przyrostek L.
A co zrobić z wyrażeniem 10/3 - obie liczby są traktowano jako typu int,
zatem przeprowadzone będzie dzielenie całkowitoliczbowe, które da w wyniku
3 (a nie 3.33333...). Jeśli chodzi nam o operację na liczbach rzeczywistych,
to musimy albo użyć przyrostka d (wskazując, że chodzi nam o literał typu
double, albo podać explicite kropkę dziesiętne w zapisie którejś z liczb
np. 10.)
Omówione przypadki pokazuje testowy program.
public class LitLicz {
public static void main(String[] args) {
System.out.println( 10 + 0x10 );
System.out.println( 10/3 );
System.out.println( 10./3 );
System.out.println( 10d/3);
System.out.println( 2147483648L );
System.out.println( 2147483647 + 1 );
System.out.println( 2147483647L + 1 );
}
}
26
3
3.3333333333333335
3.3333333333333335
2147483648
-2147483648
2147483648
Wynik działania programu pokazuje listing obok.
Zanotujmy (do pełniejszego wyjaśnienia w przyszłości) dwie obserwacje:
- Operacje na liczbach rzeczywistych są niedokładne.
- W wyrażeniach podanych w programie wystarczyło zmodyfikować tylko jeden
z literałów biorących udział w operacji, by otrzymać pożądany wynik(nie pisaliśmy
10d/3d, wystarczyło napisac 10d/3). Intuicyjnie możemy to rozumieć w ten
sposób, że typ wyniku operacji określany jest przez "większy" (określający większe możliwe wartości) z typów biorących
w niej udział argumentów (w tym przypadku literałów).
Literały znakowe (typ char) zapisujemy jako pojedyncze znaki w apostrofach np. 'a' , '+' itp., a literały łańcuchowe (napisy) - jako ciagi znaków w cudzysłowie (np. "ala ma kota").
Literały łańcuchowe oznaczają obiekty klasy String.
Użycie lewego ukośnika (backslash) - nazywanego też symbolem ucieczki ( escape
character) - pozwala na zapisywanie w literałach znakowych i łańcuchowych
specjalnych znaków.
Uwaga: ze względu na wczesne opracowywanie literałów niedopuszczalne jest
stosowanie bezpośrednich kodów Unicode dla znaków LF (\u000a) i CR (\u000d),
gdyż spowoduje to podział kodu programu, a nie wytworzenie literałow, odpowiadających
znakom LF i CR. Zamiast tego należy stosowac znaki \n i \r.
Znaki specjalne | Zapis | Przejście do nowego wiersza (Line feed - LF) | \n | Tabulacja (Tab) | \t | Backspace (BS) | \b | Powrót karetki (carriage return - CR) | \r | Nowa strona (form feed - FF) | \f | Apostrof | \' | Cudzysłów | \" | Lewy ukośnik (backslash) | \\ | Dowolny znak o kodzie NNNN (gdzie N - cyfra szesnastkowa) w Unicodzie
|
\uNNNN
|
Należy zawsze pamiętać, że dwu- lub 6-znakowa (w tekście programu) sekwencja zapisana
w apostrofach (lub wewnątrz zestawu znaków ujętych w cudzysłowy) i zaczynająca
się od ukośnika tak naprawdę (czyli tak jak ją widzi kompilator) jest jednym
znakiem.
Uwaga:
- literał znakowy - w apostrofach - to zawsze jeden znak,
- literał łańcuchowy - w cudzysłowie - to zero lub dowolna liczba znaków;
jeśli w cudzysłowie nie podamy żadnego znaku - to mamy pusty łańcuch znakowy
Niektóre znaki specjalne są przydatne przy wyprowadzaniu informacji, a ich
dziwnie brzmiące czasem nazwy związane są z czasami gdy jedynym urządzeniem
wyjściowym była drukarka (np. powrót karetki to przesunięcie głowicy drukarki
na pierwszą pozycję w wierszu - jeśli urządzeniem wyjściowym jest konsola,
to wyprowadzenie tego znaku powoduje przejście kursora do pierwszzej pozycji
linii ekranu, w której aktualnie się znajduje).
A skąd na tej liście cudzysłów i apostrof poprzedzone ukosnikiem? Odpowiedź
jest prosta. Skoro znaki ujmujemy w apostrofy, a łańcuchy znakowe w cudzysłowy,
to jak zapisać znak apostrofu lub cudzysłów ? Do tego właśnie służy ukośnik
- piszemy więc '\'' i '\"'.
W końcu - czasem będziemy chcieli podać literalnie znak \. Trzeba go wtedy wpisać podwójnie.
Poniższy program testujący pokazuje kilka przykładów użycia literałów znakowych
i łańcuchowych. Przy okazji poznamy też inną metodę wyprowadzania danych
na konsolę: o ile System.out.println(...) wyprowadza podane jako argument
dane w bieżącym wierszu konsoli z następującym potem przejściem do nowego wiersza, to System.out.print(...)
- robi to samo, ale bez przejścia do nowego wiersza.
public class LitZn {
public static void main(String[] args) {
System.out.print('a');
System.out.print('\u0061');
System.out.print('\\');
System.out.print('\'');
System.out.print(" Ala ma kota ");
System.out.println("Pierwszy\nDrugi\nTrzeci");
System.out.println("c:\\util\\bak");
System.out.println("");
System.out.println("\"");
System.out.print("abcd\r12\n1234\rab");
}
}
aa\' Ala ma kota Pierwszy
Drugi
Trzeci
c:\util\bak
"
12cd
ab34
Wydruk działania programu przedstawiono obok. Zwróćmy uwagę:
'\u0061' oznacza (heksadecymalnie) kod znaku 'a'
znak apostrofu i lewy ukośnik w literale znakowym oznaczamy \' i \\
znaki specjalne zupełnie naturalnie wstawiamy do łańcuchów znakowych: w napise
"Pierwszy\nDrugi\nTrzeci" \n oznazca znak nowego wiersza, wobec tego napisy
Drugi i Trzeci zostanie wyprowadzony w kolejnych wierszach,
aby wyprowadzić napis zawierający ukosniki musieliśmy je w tym napisie powtarzać
aby wyprowadzic cudzysłów w łańcuchu znakowym poprzedziliśmy go znakiem \
a działanie ostatniej instrukcji programu jest następujące: wyprowadzany jest napis abcd
po czym następuje cofnięcie karetki (kursora) (znak \r) do początku wiersza,
po czym w miejsce ab wpisywany jest napis 12 i następuje przejście do nowego
wiersza (znak \n), w którym wyprowadzany jest napis 1234 po czym - po cofnięciu
kursora 12 nadpisywane jest napisem ab.
|