JLS Unicode

Translacja Unicode

    Kolejność operacji:

  • Zamiana ucieczek Unicode na znaki Unicode
  • Wyszczególnienie z powyższych znaków końca linii
  • Usunięcie białych znaków i komentarzy

Powyższe kroki są wykonywane przed analizą składniową w kompilacji. Można wykorzystywać ucieczki Unicode w formacie \uxxxx gdzie xxxx to wartość znaku zapisana szesnastkowo. Ma to na celu danie możliwości zapisania w programie znaków Unicode przy wykorzystywaniu tylko znaków ASCII.

\u0070\u0075\u0062\u006C\u0069\u0063\u0020\u0063\u006C\u0061\u0073\u0073\u0020\u004F\u006A\u0061\u0063\u0069\u0065\u007B\u0070\u0075\u0062\u006C\u0069\u0063\u0020\u0073\u0074\u0061\u0074\u0069\u0063\u0020\u0076\u006F\u0069\u0064\u0020\u006D\u0061\u0069\u006E\u0028\u0053\u0074\u0072\u0069\u006E\u0067\u005B\u005D\u0020\u0061\u0072\u0067\u0073\u0029\u007B\u0053\u0079\u0073\u0074\u0065\u006D\u002E\u006F\u0075\u0074\u002E\u0070\u0072\u0069\u006E\u0074\u006C\u006E\u0028\u0022\u0048\u0065\u006A\u0068\u006F\u0022\u0029\u003B\u007D\u007D zapisane w pliku Ojacie.java można skompilować i uruchomić (raczej z konsoli).

Ucieczki Unicode muszą mieć nieparzystą ilość backslashy: \u0023 to #, \\u0023 to \u0023, \\\u0023 to \# itd …

Ucieczki mogą mieć więcej niż jedno u. Ostatnie musi poprzedzać cztery znaki szesnastkowe inaczej występuje błąd kompilacji (prekompilacji) \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu0023 jest akceptowalne.

Zamiana ucieczek na znaki odbywa się tylko raz: \u005cu0023 nie da w wyniku # (005c to kod \).

Specyfikacja dopuszcza konwersję kodu z Unicode do ASCII. Znaki niemieszczące się w ASCII są tłumaczone na ucieczki, ucieczki mają dodatkowe u np. \uu0023.


Znaki końca linii i pliku

Dopuszczalne znaki końca linii to:

  • CR (\u000d) wykorzystywany w : Commodore, Apple II, Mac OS (do wersji 9), Microware OS-9
  • LF (\u000a) wykorzystywany w: Unix, BeOS, AmigaOS, MorphOS, RISC OS, GNU/Linux, Mac OS, Multics
  • CRLF (\u000d\u000a) wykorzystywane w: DOS, OS/2, Microsoft Windows, Symbian, DEC RT-11.

CRCRLF stworzy tylko jeden znak końca linii. Kompilator może ustalać numery linii na podstawie znaków końca linii. Komentarz rozpoczynający się od // kończy się na znaku końca linii.

Jeśli plik kończy się znakiem SUB (\u001a) przeważnie wysyłanym przez control + Z, to jest on ignorowany.
Jego pełna nazwa to Substitute character. Przy wysłaniu znaku do linuksowego terminala shell wysyła do procesu sygnał SIGSTOP który pauzuje jego wykonywanie. Jest też zwyczajowo używany do oznaczania końca pliku (mimo tego że mogą po nim występować jakieś dane – przeważnie nie będą wyświetlone).

Znak jest także wykorzystywany jeśli nie ma możliwości konwersji znaku z jednej reprezentacji do innej. Pominięcie takiego znaku mogłoby skutkować lukami bezpieczeństwa.
Jeśli moduł dodawania użytkowników korzysta z innego kodowania niż moduł autoryzujący a nieznane znaki są pomijane wtedy jest możliwość utworzenia konta użytkownika o nazwie składającej się z już istniejącego użytkownika z uprawnieniami + znaki nieznane przez system kodowania. Moduł dodawania użytkowników widziałby dwóch różnych użytkowników a moduł uwierzytelniania jednego.


Komentarze i białe znaki

Zawartość komentarzy i białe znaki mogą mieć wpływ na kompilacje.
// char lf = '\u000a'; nie skompiluje się – ucieczka zostanie zmieniona na znak końca linii. Inicjalizacja zmiennej typu char mieć miejsce w jednej linii (dla Stringa nawiasy także muszą być zamknięte w tej samej linii ale można użyć znaku plusa i utworzyć nowy literał w następnej linii który będzie dodany do poprzedniego).
Nie dotyczy to komentarza utworzonego przez /* */ lub umieszczenia \n w komentarzu, które jest przetwarzane w czasie kompilacji nie przed.

Białe znaki mają znaczenie jeśli tokeny (najmniejsze fragmenty składniowe) które rozdzielają tworzą inny token gdy są nierozdzielone. += to operator przypisania ale + = daje błąd kompilacji.


Różne

Identyfikatory są takie same jeśli są reprezentowane za pomocą tych samych znaków Unicode. Czyli mogą być wyświetlane w ten sam sposób a dotyczyć różnych zmiennych.

Literał 2147483648 może wystąpić tylko z minusem inaczej ma miejsce błąd kompilacji. Integer w którym domyślnie są zapisywane literały całkowite ma zakres ujemny o jeden większy od zakresu dodatniego przez zastosowanie zapisu U2. Analogicznie dla longa i wartości 9223372036854775808.
Double i Float także mają minimalne (względem odległości od 0) i maksymalne wartości. Jeśli reprezentacja niezerowej wartości jest reprezentowana jako zero – występuje błąd kompilacji, tak samo jeśli wartością jest nieskończoność.
Przy operacjach w których występują bardzo duże i bardzo małe liczby zmiennoprzecinkowe ich reprezentacje muszą być „sprowadzone do wspólnego mianownika” a konkretnie do wspólnej mantysy co skutkuje utratą informacji na temat małej liczby.

Źródła:
„The Java® Language Specification Java SE 8 Edition” James Gosling, Bill Joy, Guy Steele, Gilad Bracha, Alex Buckley
Wikipedia znaki końca linii
Wikipedia Substitute character
Zalecenia Unicode dt. reprezentowania niemapowalnych znaków
Wikipedia Liczby zmiennoprzecinkowe w informatyce

OCA Wyjątki

Wyjątki

Stacktrace wyjątku jest zapisywany w momencie tworzenia wyjątku. Jeśli wyjątek jest utworzony i przekazany do innej metody która go rzuca wtedy wyświetlony stacktrace będzie odpowiadał miejscu w którym był tworzony nie rzucony.

toString() wyświetla nazwę wyjątku i jego tekst
getMessage() zwraca wiadomość
stacktrace jest wyświetlany przez printStackTrace().

Tylko ostatni z rzucanych wyjątków jest propagowany dalej. Rzucanie wyjątku w catch i kolejnego w finally skutkuje rzuceniem drugiego. W tej sytuacji jeśli to sprawdzany wyjątek to musi być zadeklarowany w sygnaturze.

throws w nazwie metody może być „na zapas” ale jeśli jest catch sprawdzanego wyjątku wyjątek musi być możliwy do rzucenia w try.
Jeśli wyjątek to Exception to powyższa zasada nie obowiązuje. Wtedy Exception jest traktowany jako RuntimeException bo może nim być.

Jeśli wywoływana jest nadpisana metoda której bazowa metoda rzuca wyjątek a nadpisująca nie wtedy referencja ma znaczenie – referencja do bazowej wymaga try catch; referencja do rozszerzającej nie.

Można łapać i deklarować RuntimeException.
Można łapać i deklarować Errory.

Źródła:
„OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808” Jeanne Boyarsky, Scott Selikoff
„JA+ V8 for Oracle Certified Associate – Java SE8 Programmer I” http://enthuware.com

OCA Projektowanie klas

Dziedziczenie

Wywołanie innego konstruktora (this lub super) musi być pierwszą instrukcją konstruktora.

Jeśli konstruktor klasy nadrzędnej nie jest wywoływany jawnie wtedy kompilator dodaje odwołanie do bezparametrowego konstruktora klasy nadrzędnej. Jeśli klasa nadrzędna nie ma konstruktora bezparametrowego wtedy jej konstruktor parametrowy musi być wywołany przez konstruktor klasy dziedziczącej.

Konstruktor klasy nadrzędnej nie ma dostępu do metod klasy rozszerzającej (nic o niej nie wie) ale jeśli wywoła swoją metodę która jest nadpisana w klasie rozszerzającej to zostanie wywołana nadpisująca metoda. Jeśli metoda korzysta z pól, pola te będą miały wartość domyślną nawet jeśli jest ona zmieniana w konstruktorze klasy rozszerzającej (konstruktor ten będzie wywołany dopiero po utworzeniu klasy nadrzędnej).

Klasy abstrakcyjne

Klasa abstrakcyjna nie musi mieć żadnych abstrakcyjnych metod.

Metoda abstrakcyjna nie może mieć implementacji ani być prywatna.

Implementacja abstrakcyjnych metod podlega tym samym zasadom co nadpisywanie.

Nadpisywanie

Żeby nadpisanie miało miejsce muszą być spełnione warunki:

  • sygnatury metod muszą być takie same
  • metoda nadpisująca musi być co najmniej tak samo dostępna
  • metoda nadpisująca nie może rzucać ogólniejszego lub nowego sprawdzanego wyjątku (może precyzować)
  • jeśli metoda nadpisująca coś zwraca, typ zwracany musi być ten sam lub bardziej szczegółowy (kowariantnynie dotyczy typów prostych)

Prywatne metody nie są nadpisywane ale mogą być deklarowane metody o tej samej sygnaturze które nie mają związku z metodą klasy bazowej.

Przy nadpisywaniu referencja nie ma znaczenia, istotny jest tylko rzeczywisty typ obiektu (który jest sprawdzany w czasie uruchomienia).

Przesłanianie

Metody statyczne mogą być przesłaniane (poza finalnymi). Warunki dla przesłaniania są takie same jak dla nadpisywania oraz metoda przesłaniana oraz przesłaniająca muszą być statyczne (inaczej występuje błąd kompilacji).

Zmienne statyczne mogą być przesłaniane przez niestatyczne i odwrotnie.

Przesłoniętą metodę można wywołać przez super.metoda() lub rzutowanie, analogicznie dla zmiennych.

Przy przesłanianiu referencja decyduje która metoda będzie wywołana (dzieje się to w czasie kompilacji).

Przeciążanie

Nie można przeciążać po typie zwracanym.


Interfejsy

Interfejsy domyślnie mają modyfikator abstract, dozwolony dostęp publiczny lub pakietowy.

Pola domyślnie mają modyfikatory public static final.

Do pól można odwołać się przez nazwę interfejsu, nazwę klasy która go implementuje lub referencje do niej a wewnątrz klasy która go implementuje także bez żadnej referencji (przez this).

Metody domyślnie mają modyfikatory public abstract.

Metody interfejsu tak jak metody abstrakcyjny nie mogą być private, protected ani final.

Jeśli wiele implementowanych interfejsów ma metodę o tej samej sygnaturze to wystarczy ją zaimplementować raz.

Jeśli metody w dwóch interfejsach mają te same sygnatury ale różne typy zwracane – nie mogą być implementowane jednocześnie.

Metody domyślne interfejsów nie mogą być statyczne, ale można mieć metodę domyślną w interfejsie dziedziczącym o takiej samej sygnaturze jak metoda statyczna w metodzie bazowej (odwrotnie jest błąd kompilacji).

SuperInterface.super.method() wywołuje metodę domyślną z rozszerzanego interfejsu, można przejść tylko jeden poziom do góry.
Samo super się nie skompiluje ponieważ interfejs może rozszerzać kilka interfejsów.

Można nadpisywać metody domyślne (rozszerzając interfejs z metodą domyślną).

Metody domyślne umożliwiają wielokrotne dziedziczenie. Jeśli nie jest jasne która metoda powinna być wywołana występuje błąd kompilacji, chyba że klasa sama nadpisuje metodę która jest dziedziczona domyślnie z kilku interfejsów.

Interfejsy mogą mieć metody statyczne ale te metody nie są dziedziczone przez klasy implementujące interfejs (nie można się do nich odwołać też przez referencje do interfejsu). Dzięki temu nie ma wielokrotnego dziedziczenia (konieczność jawnego napisania o metodę z którego interfejsu chodzi).

Jeśli interfejs ma metodę która także jest w klasie rozszerzanej ale o mniejszym dostępie. Klasa która implementuje i rozszerza jednocześnie musi przesłonić metodę żeby zwiększyć jej poziom dostępu.

Istnienie pól lub metod o jednakowych sygnaturach w interfejsach implementowanych przez jedną klasę nie generuje błędu. Dopiero niejednoznaczne odwołanie (bez podania konkretnego interfejsu) generuje błąd kompilacji.

Źródła:
„OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808” Jeanne Boyarsky, Scott Selikoff
„JA+ V8 for Oracle Certified Associate – Java SE8 Programmer I” http://enthuware.com

OCA Metody i enkapsulacja

Struktura metody

dostęp modyfikatory typZwracany nazwa parametry
pierwsze dwa nie są obowiązkowe.

Opcjonalne modyfikatory:
static, abstract, final, synchronized, native, strictfp mogą występować w dowolnej kolejności, konstruktor nie może mieć żadnego z nich.

static oraz final są dozwolone dla pól i metod
transient oraz volatile – tylko dla pól
synchronized, abstract oraz native – tylko dla metod
final – dozwolone dla zmiennych lokalnych.

Klasy finalne nie mogą być rozszerzane a metody nadpisywane (ale mogą być przeciążane).
Zmiennie finalne nie mogą zmienić wartości ale mogą być przesłonięte.

Statyczna metoda finalna nie może być przesłaniana.

Może istnieć metoda i pole o tej samej nazwie w tym samym zasięgu.


Wywołanie metody

Przy dopasowaniu metody do wywołania java używa metody z najdokładniejszymi typami parametrów, nigdy typy zmiennych nie są zawężane (w przeciwieństwie do przypisywania zmiennych całkowitych). Dotyczy także konstruktorów.

Priorytety przy dopasowaniach:

  • dokładne dopasowanie
  • bardziej pojemny typ
  • automatyczne opakowywanie
  • varargs

Przeprowadzana jest tylko jedna konwersja.
Nie będzie konwersji na bardziej pojemny typ prosty a następnie jego odpowiednik obiektowy.

Wywołanie metody protected na rzecz obiektu z innego pakietu jest możliwe tylko jeśli posługujemy się referencją podklasy.
W przypadku posługiwania się referencją w innym pakiecie to musi być referencja typu dziedziczącego.

Modyfikator private dotyczy wszystkich obiektów tego typu a nie tylko jednego obiektu (patrz equals()).

Do metod i pól statycznych można odwoływać się przez referencję instancji (nawet jeśli wskazuje na null).

Przy wywoływaniu metody z nullem wywoływana jest najbardziej szczegółowa implementacja (klasa która jest najniżej w hierarchii dziedziczenia).

Odwoływanie do pól lub metod statycznych klasy nadrzędnej jest możliwe przez referencje klasy dziedziczącej (ale klasa dziedzicząca nie jest wtedy ładowana).

Metoda statyczna nie ma dostępu do metod i pól instancyjnych.

Finalna zmienna musi być zainicjalizowany w konstruktorze lub w inicjalizacji instancyjnej, ewentualnie w sekcji statycznej jeśli jest statyczny.

Finalne zmienne typów prostych są konwertowane do literałów (są znane w czasie kompilacji).

Typ podawany w lamdzie musi być identyczny z typem w sygnaturze interfejsu funkcyjnego. Jeśli w interfejsie jest List wtedy ArrayList w lambdzie się nie skompiluje.

Źródła:
„OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808” Jeanne Boyarsky, Scott Selikoff
„JA+ V8 for Oracle Certified Associate – Java SE8 Programmer I” http://enthuware.com

OCA Java core API

String

Operator + jest przeciążony: dla argumentów numerycznych to dodawanie, jeśli chociaż jeden z argumentów jest Stringiem jest to konkatentacja.

Przez niezmienność Stringów operacje na nich wykonane zwracają nowy obiekt.

Stringi utworzone przez dodawanie stałych są, w czasie kompilacji, zamieniane na stałe.

Stringi które powstały z wykorzystaniem niefinalnej zmiennej powstają w czasie uruchomienia więc nie są umieszczane w puli. Można je tam umieścić przez wywołanie intern(), metoda zwraca referencje do łańcucha o tej samej zawartości ale umieszczonego w puli.

"0123".substring(4) da w wyniku pusty String ale "0123".charAt(4) rzuci wyjątek, tak samo jak "0123".substring(5).

Operator ++ nie działa dla Stringa, mimo że "napis"+1 da w wyniku napis1.

toString() z Object zwraca String klasaZeScieżką;@hashCodeObiektu, jeśli klasa jest poprzedzona [L – referencja wskazuje tablicę obiektów tego typu [Ljava.langString;@120bc123.

indexOf przyjmuje Stringa lub znak jako int.

Metody które rzucają StringIndexOutOfBoundsExcepion delete, deleteCharAt, replace, insert, substring
Metody które rzucają bardziej ogólny IndexOutOfBoundsExcepion append, insert, setLength, charAt, codePointAt, codePointBefore, codePointCount, offsetByCodePoints, getChars, setCharAt, subSequence. W praktyce wszystkie te metody rzucają szczegółowy wyjątek, ale to może się zmienić i dalej będą zgodne z dokumentacją.


StringBuilder

Obiekt o zmiennej zawartości, domyślna pojemność to 16 znaków.

equals porównuje referencje a nie zawartość.

setLength może obciąć łańcuch lub dopełnić go za pomocą null character ’\u0000′.

indexOf przyjmuje Stringa.

append przyjmuje wszystko poza short i byte.


Tablice

W momencie dodawania elementu do tablicy sprawdzany jest typ obiektu i może zostać rzucony ArrayStoreException. Jest to spowodowane kowariancyjnością tablic.

Tablica Stringów jest podtypem tablicy Obiektów.

Tablica dwuwymiarowa to tablica kolumn (które też są tablicami) dlatego nie muszą być prostokątne.

Tablice wielowymiarowe podczas deklaracji wymagają podania tylko pierwszego wymiaru.

Tablica może być zadeklarowana przez String [] tab = {„a”, „b”} tylko w momencie inicjalizacji, w innych miejscach będzie to błąd kompilacji i trzeba to zrobić przez tab = new String[] {„a”, „b”}.

Puste tablice dwuwymiarowe można zainicjalizować przez jedną parę nawiasów.

java.util.Arrays zawiera narzędzia do sortowania, przeszukiwania i wyswietlania tablic. Arrays.binarySech zwraca pozycję elementu lub indeks miejsca w którym zostałby dodany -1.

Tablice nie przeciążają equals za to domyślnie implementują Serializable i Cloneable.


ArrayList

add() zawsze zwraca true.

ArrayList rozszerza AbstractList – szkieletową implementacje listy z dostępem do dowolnego elementu która umożliwia sekwencyjne dodawanie elementów, czyszczenie całej listy, łączenie list (addAll()), Iterator, subList(), equals(), hashCode()
Odpowiednik dla dostępu szeregowego to AbstractSequentialList.

remove(Object object) mówi czy usunięcie miało miejsce
remove(int index) zwraca usuwany element lub rzuca IndexOutOfBoundsException.

set(int index, E newElement) nadpisuje i zwraca zastąpiony element.


Typy opakowujące

Wszystkie typy są niezmienne. Wszystkie mają cache który przechowuje wartości od -128 do 127 (węższy zakres mają Character i Boolean, Integer ma konfigurowalny górny zakres). Cache jest wykorzystywany przez valueOf() który jest wywoływany przy tworzeniu literałów i podczas autoboxingu.

Integer.parseInt(String string) zwraca typ prosty (analogicznie dla innych typów opakowujących).

Autoboxing ma niższy priorytet w dopasowaniu parametrów niż bezpośrednie dopasowanie
listaIntegerow.remove(1) usunie pierwszy element
listaIntegerow.remove(new Integer(1)) usunie obiekt o wartości 1.


java.time

LocalDate przechowuje datę, LocalTimeprzechowuje czas, LocalDateTime przechowuje oba.
Wszystkie te obiekty są niezmienne, konstruowane przez statyczne metody, implementują TemporalAccessor którego zaleca się nie stosować.

LocalDate.of(int rok, int miesiąc, int dzień) miesiące są numerowane od 1 i mogą być zastąpione przez enuma Month.

LocalDateTime.of(rok, miesiąc, dzień, godzina, minuta, sekunda) lub LocalDateTime.of(data, czas).

Utworzenie daty przez podanie błędnych parametrów jest wykrywane dopiero w czasie uruchomienia (DateTimeException).

Wszystkie obiekty mają fluent API do dodawania lub odejmowania wartości (zwracają nowy obiekt).

LocalDate nie ma metod do modyfikowania czasu.

Period

Period.ofYears, Period.ofMonths, Period.ofWeeks, Period.ofDays tworzą obiekty okresu które można dodawać/odejmować od daty.

Okresy nie mają fluent API to tworzenia (tak samo jak Local*Time) of() to statyczne metody zwracające nowy obiekt. Przy próbie użycia of() jak fluent API tylko ostatnia instrukcja ma znaczenie.

Obiekt Period bierze pod uwagę zmiany strefy czasowej lub czasu na letni/zimowy. Analogiczny obiekt przechowujący bezwzględny okres to Duration.

DateTimeFormatter to obiekt opisujący formatowanie dla czasu. Formatowaniu można przekazać datę lub odwrotnie (dawne SimpleDateFormat).

Źródła:
„OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808” Jeanne Boyarsky, Scott Selikoff
„JA+ V8 for Oracle Certified Associate – Java SE8 Programmer I” http://enthuware.com