« poprzedni punkt  następny punkt »

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.


« poprzedni punkt  następny punkt »