Wprowadzanie danych: klasa Scanner

Podobne dokumenty
Programowanie w Javie wykład 7 Klasy c.d. (przeciążanie metod, polimorfizm) Metody i klasy abstrakcyjne Bloki inicjalizacyjne

Programowanie w Javie - wykład 4

JAVA- wykład 3 Wprowadzanie danych Typ wyliczeniowy Tablice

Aplikacje w Javie wykład 5 Klasy c.d. (przeciążanie metod, polimorfizm) Metody i klasy abstrakcyjne Interfejsy

Programowanie w Javie wykład 9 Klasy wewnętrzne, klasy anonimowe Klasy opakowujące

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Wykład 8: Obsługa Wyjątków

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

Programowanie w Javie wykład 6

Aplikacje w środowisku Java- wykład 3

Tablice i łańcuchy znakowe jako obiektowe typy danych. dr Jarosław Skaruz

Programowanie w środowisku graficznym- wykład 3

Wykład 8: klasy cz. 4

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Rozdział 4 KLASY, OBIEKTY, METODY

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Dokumentacja do API Javy.

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Dziedziczenie. Tomasz Borzyszkowski

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Definiowanie własnych klas

Programowanie w środowisku graficznym- wykład 5 Klasy c.d. (przeciążanie metod, polimorfizm) Metody i klasy abstrakcyjne Interfejsy

Wykład 4: Klasy i Metody

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Java podstawy jęyka. Wykład 2. Klasy abstrakcyjne, Interfejsy, Klasy wewnętrzne, Anonimowe klasy wewnętrzne.

Wykład 2: Podstawy Języka

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

2. Tablice. Tablice jednowymiarowe - wektory. Algorytmy i Struktury Danych

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Dawid Gierszewski Adam Hanasko

Podstawy i języki programowania

Podstawy i języki programowania

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Języki i metody programowania Java INF302W Wykład 3 (część 1)

Programowanie w Internecie. Java

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Klasy abstrakcyjne, interfejsy i polimorfizm

Interfejsy i klasy wewnętrzne

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Język JAVA podstawy. wykład 2, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Definicje klas i obiektów. Tomasz Borzyszkowski

Tablice (jedno i wielowymiarowe), łańcuchy znaków

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Wykład 5: Klasy cz. 3

Wprowadzanie danych z klawiatury. Wyjątki związane z wprowadzaniem danych, przekroczeniem rozmiaru tablicy, dzieleniem przez zero itd.

Podstawy programowania skrót z wykładów:

Wykład 6: Dziedziczenie

Niezwykłe tablice Poznane typy danych pozwalają przechowywać pojedyncze liczby. Dzięki tablicom zgromadzimy wiele wartości w jednym miejscu.

Wykład 7: Pakiety i Interfejsy

Programowanie obiektowe

Wykład 5 Okna MDI i SDI, dziedziczenie

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Obsługa błędów za pomocą wyjątków. Paweł Motofa (140746)

Wykład 3 Składnia języka C# (cz. 2)

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Podstawy Programowania C++

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Programowanie w Javie wykład 5

Aplikacje w Javie- wykład 11 Wątki-podstawy

Podstawowe części projektu w Javie

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Programowanie obiektowe

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

PARADYGMATY PROGRAMOWANIA Wykład 4

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Polimorfizm. dr Jarosław Skaruz

PHP 5 język obiektowy

TEMAT : KLASY DZIEDZICZENIE

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Programowanie obiektowe i zdarzeniowe

Programowanie obiektowe

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Informatyka I. Typy danych. Operacje arytmetyczne. Konwersje typów. Zmienne. Wczytywanie danych z klawiatury. dr hab. inż. Andrzej Czerepicki

Programowanie obiektowe

Wyjątki (exceptions)

Pakiety i interfejsy. Tomasz Borzyszkowski

.NET Klasy, obiekty. ciąg dalszy

Platformy Programistyczne Podstawy języka Java

Kompozycja i dziedziczenie klas

Klasy abstrakcyjne i interfejsy

WYJĄTKI. Jest ona jednak czasochłonna i prowadzi do duŝego zapotrzebowania na zasoby systemu.

JAVA. Platforma JSE: Środowiska programistyczne dla języka Java. Wstęp do programowania w języku obiektowym. Opracował: Andrzej Nowak

Typy sparametryzowane

1 Podstawy c++ w pigułce.

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Multimedia JAVA. Historia

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Aplikacje w środowisku Java

Aplikacje Internetowe. Najprostsza aplikacja. Komponenty Javy. Podstawy języka Java

Throwable. Wyjatek_1(int x_) { x = x_; } int podaj_x()

public - może być używana w kodzie poza klasą, jedna klasa ModyfikatorKlasy może być kombinacją wyrażeń:

Metody Metody, parametry, zwracanie wartości

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

Języki Programowania II Wykład 3. Java podstawy. Przypomnienie

Programowanie obiektowe

Transkrypt:

Aplikacje w środowisku Java - wykład 2 Tablice, Klasy String, Scanner, wyjątki, polimorfizm, klasy abstrakcyjne klasy wewnętrze, klasy anonimowe bloki inicjalizacyjne Treści prezentowane w wykładzie zostały oparte o: Barteczko, JAVA Programowanie praktyczne od podstaw, PWN, 2014 http://docs.oracle.com/javase/8/docs/ http://docs.oracle.com/javase/10/docs/ C. S. Horstmann, G. Cornell, Java. Podstawy, Helion, Gliwice 2013 Dorota Pylak C. S. Horstmann, Java, Techniki zaawansowane(wydanie10), Helion, Gliwice 2017

2 Wprowadzanie danych: klasa Scanner Standardowe wejście jest reprezentowane przez publiczną stałą z klasy System: System.in. Wygodnym sposobem czytania ze standardowego wejścia jest użycie klasy Scanner z pakietu java.util: import java.util.*; public class ScanString{ public static void main(string[] args){ Scanner scan = new Scanner(System.in); //parametr konstruktora - źródło danych System.out.println("Podaj imię:"); String name = scan.next(); //metoda next() wczytuje napis //niezawierający białych znaków System.out.println("Witaj " + name);

Wprowadzanie danych: klasa Scanner Wprowadzanie liczb z konsoli za pomocą skanera metody przekształcające postać znakową liczb w ich kod binarny: nextint()- dla liczb całkowitych, nextdouble()- dla liczb rzeczywistych. Uwaga: używamy, jako separatora miejsc dziesiętnych, ze względu na domyślnie polskie ustawienia regionalne - tzw. locale. Aby je zmieniać: Scanner scan = new Scanner(System.in).useLocale(new Locale("en")); import java.util.*; public class ScanNumbers{ public static void main(string[] args){ Scanner scan = new Scanner(System.in); System.out.print("Podaj liczbę całkowitą: "); int i = scan.nextint(); System.out.print("A teraz rzeczywistą: "); double d = scan.nextdouble(); System.out.println("Wprowadzono " + i + " " + d); 3

4 Wprowadzanie danych: klasa Scanner Metody klasy Scanner: hasnextint(), hasnextdouble(), itd. pozwalają na sprawdzenie czy w strumieniu wejściowym kolejny element jest odpowiednio liczbą całkowitą, rzeczywistą, itd. Przykład. Pętla zaporowa wymuszająca podanie przez użytkownika liczby rzeczywistej: //import java.text.numberformat; import java.util.scanner; public class ScannerTest { public static void main(string[] args) { System.out.print("Podaj liczbę rzeczywistą: "); Scanner scan = new Scanner(System.in); while (!scan.hasnextdouble()) { scan.next();//czytamy ta "nie liczbe" System.out.print("Podaj jeszcze raz: "); double d = scan.nextdouble(); System.out.println("Podałeś: " + d); //Aby sformatować liczbę zgodnie z Locale: //NumberFormat.getInstance().format(d)

Skaner - przykład Przykład Mamy dany napis zawierający w kolejnych liniach liczby całkowite oddzielone znakiem tabulatora. Policzyć sumę tych liczb. public static void main(string[] args) { String s1 = "1\t2\t3\n4\t5\t6"; Scanner scan1 = new Scanner(s1); int suma = 0; while (scan1.hasnextline()) { Scanner scan2 = new Scanner(scan1.nextLine()).useDelimiter("\\t"); while (scan2.hasnextint()) suma += scan2.nextint(); System.out.println("Suma = " + suma); 5

TABLICE 6 Tablice są zestawami elementów (wartości) tego samego typu, ułożonych na określonych pozycjach. Do elementów tablicy odwołujemy się za pomocą nazwy tablicy oraz indeksu umieszczonego w nawiasach kwadratowych, np. tab[0], tab[1] W Javie tablice są obiektami, a nazwa tablicy jest nazwą zmiennej, będącej referencją do obiektu-tablicy. Obiekt-tablica zawiera elementy tego samego typu. Może to być dowolny z typów pierwotnych lub referencyjnych. W szczególności elementami tablic mogą być referencje do innych tablic-> tablice wielowymiarowe. Deklaracja tablicy składa się z: nazwy typu elementów tablicy, pewnej liczby par nawiasów kwadratowych (liczba par określa liczbę wymiarów tablicy), nazwy zmiennej, która identyfikuje tablicę.

TABLICE Np. String[] s; // deklaracja tablicy referencji do obiektów klasy String, double[][] d; // deklaracja dwuwymiarowej tablicy liczb // rzeczywistych Taka deklaracja nie alokuje pamięci dla tablicy. Pamięć jest alokowana dynamicznie albo w wyniku inicjalizacji za pomocą nawiasów klamrowych albo w wyniku użycia wyrażenia new. Inicjalizacja tablicy za pomocą nawiasów klamrowych może wystąpić wyłącznie w wierszu deklaracji tablicy i ma postać: { element_1, element_2,... element_n Np. int[] arr = { 1, 2, 7, 21 ; Tworzenie tablicy za pomocą wyrażenia new : new Typ[n]; gdzie Typ - typ elementów tablicy, n - rozmiar tablicy (liczba elementów tablicy) Np. int[] arr = new int[4]; 7

TABLICE - INDEKSY 8 Rozmiar tablicy może być ustalony dynamicznie, w fazie wykonania programu. Np. int n; //... n uzyskuje wartość np. na skutek obliczeń //opartych na wprowadzonych przez użytkownika danych int[] tab = new int[n]; Ale po ustaleniu rozmiar nie może być zmieniony. Indeksy tablicy mogą być wyłącznie wartościami typu int. Mogą one być dowolnymi wyrażeniami, których wyliczenie daje wartość typu int. Ze względu na to, że wartości typu byte, char i short są w wyrażeniach "promowane" (przekształcane do typu int), to również wartości tych typów możemy używać przy indeksowaniu tablic. Niedopuszczalne natomiast jest użycie wartości typu long. Tablice zawsze indeksowane są poczynając od 0. Czyli pierwszy element n-elementowej tablicy ma indeks 0, a ostatni - indeks n-1.

TABLICE - INDEKSY 9 Odwołanie do i-go elementu tablicy o nazwie tab ma postać: tab[i] Ta konstrukcja składniowa traktowana jest jako zmienna, zatem możemy tej zmiennej przypisywać wartości innych wyrażeń oraz możemy używać jej wartości w innych wyrażeniach. Odwołania do elementów tablic są przez JVM sprawdzane w trakcie wykonania programu pod względem poprawności indeksów. Java nie dopuści do odwołania się do nieistniejącego elementu tablicy lub podania indeksu mniejszego od 0. Próba takiego odwołania spowoduje powstanie wyjątku ArrayIndexOutOfBoundsException Informacje o rozmiarze (liczbie elementów) tablicy możemy uzyskać za pomocą odwołania: nazwa_tablicy.length length nie jest nazwą metody (lecz pola niejawnie stworzonej klasy, opisującej tablicę), dlatego NIE STAWIAMY po nim nawiasów okrągłych

TABLICE - PRZYPISANIA 10 Pętla przebiegająca przez wszystkie elementy tablicy tab: for (int i = 0; i < tab.length; i++)... tab[i]... ; Zmiennej tablicowej typu typa[] można przypisać wartość zmiennej tablicowej typu typb[] pod warunkiem, że dopuszczalne jest przypisanie wartości typb zmiennej typa. Każdej zmiennej tablicowej - jak każdej zmiennej zawierającej referencję - można przypisać wartość null Tak samo jak w przypadku innych obiektów - nie należy mylić przypisania zmiennych tablicowych (czyli referencji) z kopiowaniem zawartości tablic Na jedną tablicę może wskazywać kilka zmiennych tablicowych. Za pomocą każdej z nich (i operacji indeksowania) możemy zmieniać wartości elementów tej jednej tablicy, a odwołania do tej tablicy poprzez inne zmienne będą - oczywiście - uwzględniać te zmiany (np. pokazywać zmienione wartości).

TABLICE JAKO ARGUMENTY I WYNIKI METOD 11 Metody mogą działać na tablicach, do których referencje otrzymują w postaci parametrów i mogą zwracać wyniki - referencje do tablic. W nagłówku metod - parametry (które mają oznaczać tablice; są referencjami do tablic) deklarujemy za pomocą T[], gdzie T - nazwa typu elementów tablicy. Oczywiście, gdy wywołujemy metodę - to na liście argumentów podajemy nazwy zmiennych tablicowych (już bez nawiasów kwadratowych). Jeśli metoda zwraca wynik - referencję do tablicy, to typem wyniku jest również odpowiedni typ tablicowy T[]. Dla uproszczenia dalej będziemy mówić, że metoda otrzymuje jako argument - tablicę i zwraca jako wynik - tablicę. Jest to skrót myślowy: pamiętajmy, że zawsze chodzi o referencje do tablic Np. metoda o nazwie dblval, która zwraca referencję do nowo utworzonej tablicy liczb całkowitych, wartości elementów której są podwojonymi wartościami elementów tablicy liczb całkowitych, do której referencja przekazana została metodzie jako argument, może być zdefiniowana i użyta następująco

TABLICE JAKO ARGUMENTY I WYNIKI METOD public class Test { static int[] dblval(int[] tab) { int[] w = new int[tab.length];//tablica for (int i=0; i < w.length; i++) w[i] = tab[i]*2; return w; public static void main(string[] args) { int[] a = {1, 2, 3, 4 ; int[] wynik = dblval(a); for(int el : wynik) System.out.print(" " + el); 12

TABLICE OBIEKTÓW 13 Elementami tablicy mogą być referencje do dowolnych obiektów, np. String[] miasta = {"Warszawa", "Poznań", "Kraków"; Para[] tabpar = new Para[10]; Samo stworzenie tablicy nie tworzy jednak obiektów, do których referencje są elementami tablicy. Mają one na początku domyślne wartości null. Dopiero po stworzeniu obiektów i przypisaniu ich referencji elementom tablicy będziemy mogli używać elementów tablicy w operacjach na obiektach. Para[] tabpar = new Para[10]; for (int i=0; i < tabpar.length; i++) tabpar[i] = new Para(i+1, i+2); lub: Para[] tabpara = {new Para(1,1),new Para(2,3), new Para(4,5) ;

TABLICE OBIEKTÓW 14 Wygodną konstrukcją składniową Javy jest wyrażenie ad hoc tworzące i inicjujące tablicę referencji do obiektów: new klasaa[] { refb, refc,... Wyrażenie to tworzy tablicę typu klasaa[] i inicjuje ją referencjami podanymi w nawiasach klamrowych, przy czym każda z tych referencji może wskazywać obiekt klasy klasaa lub dowolnej klasy pochodnej od klasy klasaa. Wynikiem wyrażenia jest referencja do zmiennej tablicowej typu klasaa[]. Najczęściej wyrażenie to ma zastosowanie na liście argumentów wywołania metody, której parametrem jest tablica. W ten sposób możemy uzyskać efekt wywołania metody ze zmienną liczbą i (do pewnego stopnia) zmiennymi typami argumentów. Np., metodę xyz, której parametrem jest tablica zwierząt, możemy wywołać ze zmienną liczbą i typami zwierząt: xyz( new Zwierze[] {new Pies(...), new Pies(...), new Zwierze(...) ); xyz( new Zwierze[] {new Pies(...), new Pies(...) ); Pies p1 = new Pies(...),p2 = new Pies(...); xyz( new Zwierze[] { p1, p2 );

TABLICE WIELOWYMIAROWE 15 Tablice wielowymiarowe w Javie realizowane są jako tablice elementów, będących referencjami do tablic. Liczba wymiarów określana jest przez liczbę nawiasów [ ]. Przykładowe sposoby deklaracji i inicjacji: inicjalizacja w nawiasach klamrowych int[][] mac1 = { { 1, 2, 3, { 4, 5, 6, 7 ; dynamicznie int[][] mac2 = new int[n][m]; tablica składa się z wektorów o różnych rozmiarach, zadawanych przez tablicę w,np. dla int w[] = { 2, 3, 4 ; int n = 3; int[][] m3 = new int[n][]; //rozmiarami wierszy będą // zmienne dynamicznie for(int i = 0; i < m3.length; i++) { m3[i] = new int[w[i]]; Rozmiar 0-go wiersza tablicy m3 wynosi 2, 1-go wiersza 3, 2-go wiersza 4

TABLICE WIELOWYMIAROWE ublic class MultiArr { public static void main(string[] arg) { int w[] = { 2, 3, 4 ; int n = 3; //rozmiary wierszy będą ustalane dynamicznie: int[][] m3 = new int[n][]; for(int i = 0; i < m3.length; i++) { m3[i] = new int[w[i]]; for (int j = 0; j < m3[i].length; j++) m3[i][j] = i + j; for (int i = 0; i < m3.length; i++) { System.out.println("Rozmiar " + i + "-go wiersza " + m3[i].length); String out = " "; for(int j = 0; j < m3[i].length; j++) out += " " + m3[i][j]; System.out.println(out); 16

varargs Możemy również korzystać z konstrukcji zwanej "varargs" do przekazania dowolnej liczby wartości (parametrów aktualnych) do metody. Używamy "varargs", gdy nie wiemy jak wiele argumentów określonego typu będziemy przekazywać do metody. Jest to szybsze niż tworzenie tablic ręcznie. Aby użyć "varargs" należy po typie, a przed nazwą ostatniego parametru napisać trzy kropki " ". Taka metoda może być wywołana z dowolną liczbą parametrów, w tym bez żadnego parametru. public static void show(string... tab) { for (int i=0; i<tab.length; i++) System.out.println(tab[i]); Przykładowe wywołania funkcji show: show(); show("hello"); show("jak", "się", "masz"); show(new String[]{"A","B"); 17

18 KLASA Arrays Klasa Arrays z pakietu java.util zawiera wiele użytecznych metod statycznych ułatwiających działania na tablicach wszelkich typów. Np. wypełnianie tablic pojedynczą wartością fill(tab, wartość) kopiowanie tablic copyof(tab, newlen) zwraca nową tablicę, przy czym jeżeli newlen==tab.length, mamy dokładną kopię tablicy, jeśli newlen>tab.length, to w nowej tablicy pojawią się na końcu dodatkowe elementy z wartościami zero ( 0, false lub null) jeśli newlen<tab.length, to zostanie skopiowane pierwsze newlen elem. tablicy tab kopiowania fragmentów tablic copyofrange(tab, fromindex, toindex) metoda tostring(tab), która wyprowadza napisową reprezentację tablicy

Klasa String 19 W klasie String znajdziemy wiele użytecznych metod przeznaczonych do operowania na łańcuchach znakowych. Kolejne znaki napisów występują na pozycjach, które są indeksowane od 0. Części napisów (łańcuchów znakowych) określa się terminem "podłańcuch" (substring). Obiekty klasy String są niemodyfikowalne (ang. immutable) - to znaczy napisów klasy String nie możemy zmieniać (np. do napisu dodać inny). W używanej przez nas konkatenacji łańcuchów wynikowy napis, powstający z dodania do łańcucha znakowego innego napisu, jest nowym obiektem i np. w takiej sekwencji: String s = "Ala"; s = s + " ma kota"; tworzony jest nowy obiekt, zawierający napis "Ala ma kota" i referencja do niego przypisywana jest zmiennej s, która poprzednio zawierała referencję do napisu "Ala". Zwróćmy też uwagę, że w klasie String nie ma żadnych metod pozwalających modyfikować istniejący obiekt-napis.

Klasa String Uwaga. Zanim napiszemy fragment wykonujący określone zadanie na napisach, sprawdźmy w dokumentacji czy w klasie String nie ma metod, które by nam to ułatwiły. Kiedy porównujemy jakieś napisy, to zwróćmy uwagę na Czy białe znaki (spacje, tabulacje) ewentualnie okalające napis są istotne w porównaniu; jeśli nie to użyjmy metody trim() Czy chcemy porównać napisy, uwzględniając wielkość liter; jeśli nie, to użyjmy metod equalsignorecase lub ujednolićmy wielkość liter w napisach metodami touppercase()lub tolowercase() Przykład. Użytkownik odpowiada w dialogu wejściowym na pytanie. Uwzględnijmy to, że może wprowadzić niepotrzebne spacje lub użyć różnej wielkości liter. String ans= JOptionPane.showInputDialog("Czy mam wykonać program?"); if (ans!= null){ ans=ans.trim().touppercase(); switch(ans){ case "TAK": case "T" : execute(); 20

Klasa String nowe metody w JDK11 W JDK11 wprowadzono nowe metody w klasie String: strip() metoda usuwa wiodące i następujące po słowie znaki białe stripleading() usuwa białe znaki przed słowem. striptrailing() usuwa białe znaki na końcu napisu var napis =" jakis tekst "; System.out.println("*"+ napis+"*"); System.out.println("*"+ napis.stripleading()+"*"); System.out.println("*"+ napis.striptrailing()+"*"); System.out.println("*"+ napis.strip()+"*"); Output: * jakis tekst * *jakis tekst * * jakis tekst* *jakis tekst* 21

Klasa String nowe metody w JDK11 22 isblank() metoda zwraca true jeśli napis jest pusty lub zawiera tylko znaki białe; w przeciwnym razie zwraca false. var value =""; System.out.println(value.isBlank()); //true repeat(n) metoda zwraca n- krotną konkatenacje napisu var tekst ="tekst"; System.out.println(tekst.repeat(5)); //tekstteksttekstteksttekst lines() metoda zwraca strumień stringów podzielonych znakami końca linii. Linie w strumieniu są w takiej kolejności jak w stringu. String t ="pierwsza linia \n druga linia \n"; System.out.println(t.lines().collect(Collectors.toList())); //[pierwsza linia, druga linia ]

Rozbiór tekstów : metoda split 23 Bardzo elastyczne możliwości analizy tekstów dają wyrażenia regularne. Do rozbioru tekstów zalecana jest metoda split z klasy String: public String[] split(string regex) Wywołanie txt.split(sep) zwraca tablicę symboli z tekstu txt rozdzielonych separatorami pasującymi do wzorca podanego przez napis- wyrażenie regularne sep. Przykład 1: String[] result = "this is a test".split("\\s"); run: for (int i=0; i<result.length; i++) System.out.println(result[i]); this is a test

Rozbiór tekstów : metoda split 24 Przykład 2:(w tekście 2 spacje i spacja z przodu, sep: >=1 spacji) run: 4 String[] s = " Ala ma kota ".split(" +");//1 lub więcej spacji System.out.println(s.length); for (int i=0; i<s.length; i++) System.out.println(s[i]); Ala ma kota Tablica zawiera cztery elementy z których pierwszy jest pustym napisem, gdyż elementy tablicy zwracanej przez metodę split są podnapisami z których każdy jest zakończony podnapisem pasującym do wyrażenia regularnego lub końcem wiersza.

WYJĄTKI Wyjątek - to sygnał o błędzie w trakcie wykonania programu Wyjątek powstaje na skutek jakiegoś nieoczekiwanego błędu. Wyjątek jest zgłaszany (lub mówiąc inaczej - sygnalizowany). Wyjątek jest (może lub musi być) obsługiwany. Prosty schemat obsługi wyjątków try { //... w bloku try ujmujemy instrukcje, //które mogą spowodować wyjątek catch(typwyjatku exc) { //w klauzuli catch umieszczamy obsługę wyjątku catch (TypWyjatku2 exc) { //obsługa wyjątków TypWyjatku2 catch (TypWyjatku3 w) { //obsługa wyjątków TypWyjatku3... 25

WYJĄTKI Wykonywane są kolejne instrukcje bloku try. Jeśli w pewnej instrukcji wystąpi błąd (na skutek czego powstanie wyjątek), wykonanie bloku try jest przerywane w miejscu wystąpienia błędu. Sterowanie przekazywane jest do pierwszej w kolejności klauzuli catch, w której podana w nawiasach okrągłych po słowie catch klasa wyjątku pasuje do typu powstałego wyjątku. Inne klauzule catch nie są wykonywane. Stąd ważny wniosek: najpierw podawać BARDZIEJ SZCZEGÓŁOWE TYPY WYJĄTKÓW np. najpierw FileNotFoundException, a później IOException, bo klasa FileNotFoundException jest pochodna od IOException Obsługująca wyjątek klauzula catch może zrobić wiele rzeczy: m.in. zmienić sekwencję sterowania (np. poprzez return lub zgłoszenie nowego wyjątku za pomocą instrukcji throw). Jeśli nie zmienia sekwencji sterowania, to wykonanie programu jest kontynuowane od następnej instrukcji po bloku try. 26

WYJĄTKI 27 Typowym przykładem obsługi wyjątków jest weryfikacja danych wprowadzanych przez użytkownika. Przykładowo żądamy od użytkownika wprowadzania liczb całkowitych, a następnie za pomocą metody parseint przekształcamy ich znakową reprezentację na binarną. Jak wiemy, jeśli przy tym wprowadzony napis nie reprezentuje liczby całkowitej, to powstaje wyjątek NumberFormatException. Możemy więc teraz zmodyfikować np. program wykonywania operacji arytmetycznych na liczbach całkowitych: try { int num1 = Integer.parseInt(snum1); //operacje na num1 catch (NumberFormatException exc) { //obsługa wyjątku

WYJĄTKI -przykład 28 public class CatchTest { public static Integer pobierzinteger() { String wynik; while((wynik = javax.swing.joptionpane.showinputdialog( "Podaj liczbę całkowitą"))!=null){ try { return Integer.parseInt(wynik);//jest liczba catch(numberformatexception exc) {//błędna wartość //nie robimy nic, powrót do while i ponowna próba pobrania return null;//anulowano public static void main(string[] args) { Integer i = pobierzinteger(); if (i == null) System.out.println("Anulowano"); else System.out.println("Podano: " + i);

WYJĄTKI Wyjątki są obiektami klas wyjątków. Zatem nazwy np. NumberFormatException są nazwami klas, a zmienna exc zawiera referencję do obiektu odpowiedniej klasy wyjątku. Wobec tej zmiennej możemy użyć licznych metod klas wyjątków np. getmessage()- zwraca napis - informację o wyjątku tostring() - zwraca napis - informację o wyjątku (zazwyczaj nazwę klasy + dodatkowe informacje uzyskane przez getmessage()) printstacktrace()- wypisuje na konsoli informacje o wyjątku oraz sekwencje wywołań metod, które doprowadziły do wyjątku (stos wywołań) getcause() - zwraca wyjątek niższego poziomu, który spowodował powstanie wyjątku lub null w przeciwnym razie 29

WYJĄTKI 30 W Javie zdefiniowanych jest wiele typowych wyjątków (najważniejsze są w pakiecie java.lang). Są one używane przez klasy ze standardowych bibliotek. Różnią się jedynie nazwą, która pozwala zorientować się co dany wyjątek oznacza. W swoich aplikacjach można korzystać z tych klas lub zdefiniować nowe, pasujące do konkretnego zastosowania. Wszystkie klasy, których egzemplarze mogą być zgłoszone jako wyjątek muszą dziedziczyć po klasie Throwable. Throwable ma dwie bezpośrednie podklasy Error i Exception. Wyjątki dziedziczące po Error reprezentują poważne problemy, których aplikacja nie będzie w stanie rozwiązać. Przykładową podklasą jest VirtualMachineError. Wystąpienie takiego wyjątku oznacza, że maszyna wirtualna nie może dalej kontynuować pracy, np. z powodu wyczerpania się zasobów systemowych. Wyjątków rozszerzających Error nie należy przechwytywać, gdyż nie ma możliwości zaradzenia sytuacjom wyjątkowym, które je spowodowały.

WYJĄTKI Klasa Exception Wyjątki dziedziczące po Exception reprezentują sytuacje, na które aplikacja powinna być przygotowana. Jej przykładowe podklasy to: IOException, która reprezentuje sytuacje wyjątkowe związane z wejściem/wyjściem, ClassNotFoundException, która wskazuje, że maszyna wirtualna nie odnalazła klasy o nazwie podanej jako napis. Ciekawą podklasą Exception jest RuntimeException. Wyjątki rozszerzające RuntimeException mogą wystąpić podczas typowych operacji, jak rzutowanie zmiennej, odwołanie się do elementu tablicy lub odwołanie się do składowej obiektu. Ich wystąpienie zazwyczaj oznacza, że programista popełnił błąd w swoim kodzie. JVM wykrywa wystąpienie takich błędów w trakcie działania programu i informuje o tym, zgłaszając odpowiedni wyjątek. Mimo, że takie wyjątki też można obsługiwać, zdecydowanie lepiej jest zadbać, żeby się nie pojawiały. Ze względu na swoją wszechobecność ich pełna obsługa praktycznie mijałaby się z celem. 31

WYJĄTKI Wyjątki pochodne od klas RuntimeException i Error są niekontrolowane nie muszą być obsługiwane. Pozostałe wyjątki są kontrolowane, co oznacza, że: metody zgłaszają wyjątki wymienione jawnie w swojej klauzuli throws (ew. podklasy tych wyjątków). Wówczas odwołania do tych metod wymagają jawnej obsługi ewentualnie zgłaszanych wyjątków poprzez konstrukcje try catch lub poprzez wymienienie wyjątku w klauzuli throws naszej metody (tej która odwołuje się do metody, która może zgłosić wyjątek) i "przesunięcie" obsługi wyjątku do miejsca wywołania naszej metody. Zatem wiele razy natkniemy się na sytuację, w której musimy obsługiwać wyjątki, które mogą powstać przy wywołaniu jakichś metod ze standardowych klas Javy. Jeśli tego nie zrobimy, kompilator wykaże błąd w programie. Sytuacja taka dotyczy, np. metod ze standardowego pakietu java.io, zawierającego klasy do operowania na strumieniach danych (m.in. plikach). Deklaracja throws TypWyj1, TypWyj2,..., TypWyjN używana w nagłówku metody, konstruktora, oznacza że dana metoda może zgłaszać wyjątki podanych typów: public void metoda() throws InterruptedException{ // ciało metody- tu może powstać wyjątek 32

multicatch 33 Wprowadzony w Javie 7 mulitcatch upraszcza obsługę wyjątków. Zamiast: try { //kod, w którym może powstać jeden z wyjątków //TypExc1, TypExc2 catch (TypExc1 exc){ func(); catch (TypExc2 exc){ func(); możemy zapisać jedną klauzulę catch dla kilku typów wyjątków try{ //kod, w którym może powstać jeden z wyjątków //TypExc1, TypExc2 catch (TypExc1 TypExc2 exc){ func(); Oczywiście, ma to sens wtedy, gdy obsługa wyjątków TypExc1, TypExc2 jest taka sama i nie da się jej wyrazić poprzez przechwycenie wyjątku wspólnej nadklasy.

WŁASNE WYJĄTKI 34 Aby stworzyć własny wyjątek należy zdefiniować odpowiednią klasę dziedziczącą po klasie Exception: class NaszWyj extends Exception { Zwykle w naszej klasie wystarczy umieścić dwa konstruktory: bezparametrowy oraz z jednym argumentem typu String (komunikat o przyczynie powstania wyjątku). W konstruktorach tych należy wywołać konstruktor nadklasy (za pomocą odwołania super(...), w drugim przypadku z argumentem String). Jeśli jakaś nasza metoda ma sygnalizować wyjątek NaszWyj - musi podać w deklaracji, że może to zrobić: void naszametoda() throws NaszWyj a następnie sprawdza warunki powstania błędu i jeśli jest błąd - tworzy wyjątek (new NaszWyj(...)) i sygnalizuje go za pomocą instrukcji throw : throw new NaszWyj(ew_param_konstruktora_z_info_o_błędzie)

WŁASNE WYJĄTKI class NaszWyjatek extends Exception { //klasa wyjątku NaszWyjatek() {super(); //konstruktory NaszWyjatek(String s) { super(s); public class NaszaKlasa { public void metoda() throws NaszWyjatek { throw new NaszWyjatek("Jakaś przyczyna"); ------------------------------------------------- public class NowyWyjatekTest { public static void main(string[] args) { NaszaKlasa obj = new NaszaKlasa(); try { obj.metoda(); catch (NaszWyjatek w) { System.out.println(w.getMessage()); //nadklasa Throwable, definiuje metodę getmessage(), //przy pomocy której można wydobyć napis z wyjątku 35

Przedefiniowanie i klauzula throws Przedefiniowanie metody z nadklasy w klasie dziedziczącej nie może poszerzać zakresu wyjatków kontrolowanych wymienionych w klauzuli throws ( wyjątki kontrolowane, to te których klasy są pochodne od klasy Exception, ale nie RuntimeException). Oznacza to, że: jeżeli metoda z klasy bazowej nie ma klauzuli throws, to metoda przedefiniowująca ją w klasie pochodnej nie może wymienić w swojej klauzuli throws żadnych wyjątków kontrolowanych, jeżeli metoda z klasy bazowej wymienia w swojej klauzuli throws jakieś wyjatki kontrolowane, to metoda przedefiniowująca ją w klasie pochodnej nie może wymienić żadnej nadklasy tych wyjątków ani żadnych dodatkowych innych klas wyjątków kontrolowanych, może natomiast wymienić dowolne wyjątki pochodzące z podklas wyjątków, zgłaszanych przez metodę z klasy bazowej, niezależnie od metody z klasy bazowej metoda przedefiniowana w klasie pochodnej może nie zgłaszać żadnych wyjatków i nie mieć klauzuli throws, metoda przedefiniowana w klasie pochodnej zawsze może zgłaszać wyjatki niekontrolowane i ewentualnie wymieniać je w swojej klauzuli throws 36

Przedefiniowywanie i przeciążanie metod 37 Przedefiniowanie (nadpisywanie) metod (overriding) należy odróżniać od przeciążania metod (overloading) Metody przeciążone mają po prostu te same nazwy, ale inną liczbę i/lub typy parametrów. Zwróćmy uwagę, że: po pierwsze, przeciążone metody mogą należeć do tej samej lub różnych klas (z których jedna pośrednio lub bezpośrednio dziedziczy inną), po drugie przeciążanie nie wyklucza przedefiniowania: jeśli np. w klasie A zdefiniowano dwie publiczne metody z tą samą nazwą (co oznacza, że są one przeciążone, bo sygnatury metod deklarowanych w jednej klasie muszą się różnić), to w klasie B dziedziczącej klasę A możemy je dodatkowo przeciążyć (czyli podać w deklaracji inny zestaw parametrów) oraz przedefiniować (pozostawiając sygnaturę bez zmian i dostarczając innej definicji kodu metody). Metoda prywatna nigdy nie może być przedefiniowana w podklasie. Deklaracja w podklasie metody o tej samej sygnaturze co metoda prywatna nadklasy oznacza praktycznie wprowadzenie "niezależnego" bytu do naszego programu (zatem możemy tu już mieć np. całkiem inny typ wyniku niż w metodzie prywatnej o tej samej sygnaturze z nadklasy).

Pokrywanie metod (hiding) 38 Pokryciem metody nazywa się dostarczenie w podklasie definicji metody statycznej o tej samej sygnaturze i tym samym lub kowariantnym typem wyniku jak metoda statyczna z nadklasy. Pokrywanie (hiding) nie dotyczy metod niestatycznych, co więcej jeśli w podklasie dostarczymy definicji metody statycznej o tej samej sygnaturze jak metoda niestatyczna nadklasy, to wystąpi błąd w kompilacji. Pokrywanie może dotyczyć również pól: oznacza ono wtedy deklarację w podklasie pola o takim samym identyfikatorze jak pole z nadklasy. Pokrycie identyfikatorów pól różni się zarówno od pokrywania identyfikatorów metod jak i przedefiniowania metod. Pole statyczne może pokryć pole niestatyczne i odwrotnie. Pole pokrywające pole nadklasy może mieć całkiem inny typ niż pokryte pole nadklasy. Odwołania do przedefiniowanych metod oraz pokrytych metod i pól nadklasy z poziomu metod podklasy realizowane są za pomocą konstrukcji: super.odwołanie_do_składowej

Metody wirtualne. Polimorfizm 40 Metody wirtualne to takie metody, dla których wiązanie odwołań z kodem programu następuje w fazie wykonania programu. Nazywa się to "dynamic binding" lub "late binding". W Javie wszystkie metody są wirtualne za wyjątkiem: metod statycznych (bo nie dotyczą obiektów, a klasy) metod deklarowanych ze specyfikatorem final, który oznacza, że metoda jest ostateczna i nie może być przedefiniowana, metod prywatnych (bo metody prywatne nie mogą zostać przedefiniowane). Mówi się, że odwołania do metod wirtualnych są polimorficzne, a słowo "polimorficzne" używane jest w tym sensie, iż konkretny efekt odwołania może przybierać różne kształty, w zależności od tego jaki jest faktyczny typ obiektu, na rzecz którego wywołano metodę wirtualną.

Metody wirtualne. Polimorfizm 41 class A { //... void fun() { System.out.println("Jestem z nadklasy"); //... class B extends A { // @Override void fun() { System.out.println("Jestem z podklasy"); // public class TestAB{ public static void main(string[] args) { A ob1 = new A(); A ob2 = new B(); ob1.fun();//jestem z nadklasy ob2.fun();//jestem z podklasy

Metody i klasy abstrakcyjne 42 Metoda abstrakcyjna to metoda, która nie ma implementacji (ciała) i jest zadeklarowana ze specyfikatorem abstract. abstract int getsomething(); //nie ma ciała {, tylko ; Klasa, w której zadeklarowano jakąkolwiek metodę abstrakcyjną jest klasą abstrakcyjną i musi być opatrzona specyfikatorem abstract. Klasa abstrakcyjna może (ale nie musi) zawierać metody abstrakcyjne. Wystarczy zadeklarować ją ze specyfikatorem abstract. abstract class SomeClass { int n; abstract int getsomething(); void say() { System.out.println("Coś tam, coś tam"); Nie można tworzyć obiektów klasy abstrakcyjnej. Klasa abstrakcyjna może być dziedziczona przez nowe klasy. Klasa pochodna powinna przedefiniować (a właściwie zdefiniować) wszystkie metody abstrakcyjne, które odziedziczyła z abstrakcyjnej klasy bazowej. W przeciwnym wypadku klasa pochodna nadal pozostanie klasą abstrakcyjną i nie będzie można tworzyć jej obiektów.

Metody i klasy abstrakcyjne Metody abstrakcyjne to takie, co do których nie wiemy jeszcze jaka może być ich konkretna implementacja, ale wiemy, że powinny wystąpić w zestawie metod każdej konkretnej klasy dziedziczącej klasę abstrakcyjną. Konkretna implementacja (definicja w klasie kodu metody) może być bardzo różna, w zależności od konkretnego rodzaju obiektów, które opisuje dana klasa. Przykład. Rozważmy klasę Figura, reprezentującą dowolną figurę geometryczną, którą można narysować i zmazać. Przyjmiemy, ze chociaż figury rysuje się różnie, to ściera się je tak samo. Dla zapewnienia wspólnego interfejsu dla wszystkich figur metody rysuj() i zmaz() powinny być zadeklarowane w klasie Figura. Ponieważ nie można narysować ogólnej figury, to metoda rysuj będzie abstrakcyjna. Poza tym, ogólne figury nie istnieją, zatem klasa Figura nie powinna w ogóle mieć instancji (każdy obiekt klasy Figura musi być jakąś konkretną figurą, obiektem jakiejś klasy pochodnej) public abstract class Figura { public abstract void rysuj(); public void zmaz() { System.out.println("Figura zmazana"); 43

Metody i klasy abstrakcyjne W definicji klasy pochodnej po klasie Figura musimy przedefiniować metodę rysuj(), zaś metodę zmaz() możemy, ale nie musimy, gdyż nie jest ona abstrakcyjna. class Trojkat extends Figura { private int wysokosc; public Trójkat(int wysokosc) { this.wysokosc = wysokosc; @Override public void rysuj() { for (int i = 0; i < wysokosc; i++) { for (int j = 0; j < wysokosc - i; j++) System.out.print( ); for (int j = 0; j < i * 2-1; j++) System.out.print( ^ ); System.out.println(); 44

Metody i klasy abstrakcyjne 45 Wówczas możemy używać tej klasy jak poniżej: Trojkat t = new Trojkat(7); Figura f = new Trojkat(3); //! Figura g = new Figura(); // nie można tworzyć instancji t.rysuj(); t.zmaz(); f.rysuj(); Oczywiście należy pamiętać, że referencje do obiektu typu Trojkat możemy przechowywać w zmiennej typu Figura, co wcale nie oznacza, że obiekt f z powyższego listingu jest klasy Figura jest on klasy Trojkat.

Klasy wewnętrzne Klasa wewnętrzna to klasa zdefiniowana wewnątrz innej klasy. class A {... class B {...... Klasa B jest klasą wewnętrzną w klasie A. Klasa A jest klasą otaczającą klasy B. Klasy wewnętrzne (ang. Nested Classes) dzielą się na dwie kategorie: statyczne (static) i niestatyczne (non-static) class OuterClass {... static class StaticNestedClass {... class InnerClass {... 46

Klasy wewnętrzne 47 Klasa wewnętrzna może: być zadeklarowana ze specyfikatorem private (normalne klasy nie!), uniemożliwiając wszelki dostęp spoza klasy otaczającej, odwoływać się do niestatycznych składowych klasy otaczającej (jeśli nie jest zadeklarowana ze specyfikatorem static), być zadeklarowana ze specyfikatorem static (normalne klasy nie!), co powoduje, że z poziomu tej klasy nie można odwoływać się do składowych niestatycznych klasy otaczającej (takie klasy nazywają się zagnieżdżonymi, ich rola sprowadza się wyłącznie do porządkowania przestrzeni nazw i ew. lepszej strukturyzacji kodu) mieć nazwę (klasa nazwana) lub nie mieć nazwy (wewnętrzna klasa anonimowa), być lokalna zdefiniowana w bloku (metodzie lub innym bloku np. w bloku po instrukcji if) odwoływać się do zmiennych lokalnych, jeśli jest lokalna, a wartości zmiennych lokalnych, do których się odwołujemy nie mogą się zmieniać (do Javy 7 wymagane było by były ze specyfikatorem final).

Klasy wewnętrzne 48 Do statycznej klasy wewnętrznej mamy dostęp poprzez nazwę klasy otaczającej OuterClass.StaticNestedClass Np. aby stworzyć obiekt statycznej klasy wewnętrznej należy OuterClass.StaticNestedClass nestedobject = new OuterClass.StaticNestedClass(); Między obiektami statycznej klasy wewnętrznej a obiektami klasy otaczającej nie zachodzą żadne związki. Rozważmy klasy class OuterClass {... class InnerClass {... Dla nieprywatnych klas wewnętrznych możliwe jest odwoływanie się do nich spoza kontekstu klasy otaczającej: OuterClass.InnerClass

Klasy wewnętrzne Zawarcie klasy wewnętrznej w klasie otaczającej nie oznacza, że obiekty klasy otaczającej zawierają elementy (pola) obiektów klasy wewnętrznej. Obiekt niestatycznej klasy wewnętrznej zawiera referencję do obiektu klasy otaczającej, co umożliwia odwoływanie się do jej wszystkich składowych. Tworzenie obiektu niestatycznej klasy wewnętrznej wymaga zawsze istnienia obiektu klasy otaczającej. Mówi się, że obiekt klasy wewnętrznej opiera się na obiekcie klasy otaczającej. Dlatego tworzymy najpierw obiekt klasy otaczającej OuterClass outerobject = new OuterClass(); OuterClass.InnerClass innerobject = outerobject.new InnerClass(); lub jeśli nie potrzebujemy obiektu klasy zewnętrznej OuterClass.InnerClass innerobject = new OuterClass().new InnerClass(); Wewnątrz klasy otaczającej (OuterClass) tam, gdzie dostępne jest this, można po prostu napisać: InnerClass innerobject = new InnerClass(); 49

Anonimowe klasy wewnętrzne 50 Szczególną rolę odgrywają anonimowe klasy wewnętrzne. Klasy te nie mają nazwy. Najczęściej tworzymy je po to, by przedefiniować jakieś metody klasy bazowej przez klasę wewnętrzną bądź zdefiniować metody implementowanego przez nią interfejsu na użytek jednego obiektu. Referencję do tego obiektu chcemy traktować jako typu klasy bazowej lub typu implementowanego interfejsu. Nazwa takiej klasy wewnętrznej jest więc nam niepotrzebna i nie chcemy jej wymyślać. Wtedy stosujemy anonimowe klasy wewnętrzne. Definicja anonimowej klasy wewnętrznej : new NazwaTypu( parametry ) { // pola i metody klasy wewnętrznej gdzie: NazwaTypu nazwa nadklasy (klasy dziedziczonej w klasie wewnętrznej) lub implementowanego przez klasę wewnętrzną interfejsu, parametry argumenty przekazywane konstruktorowi nadklasy; w przypadku, gdy Typ jest nazwą interfejsu lista parametrów jest oczywiście pusta (bo chodzi o implementację interfejsu).

Anonimowe klasy wewnętrzne Uwagi: anonimowe klasy wewnętrzne nie mogą mieć konstruktorów (bo nie mają nazwy), za pomocą anonimowej klasy wewnętrznej można stworzyć tylko jeden obiekt, bo definicja klasy podana jest w wyrażeniu new czyli przy tworzeniu obiektu, a nie mając nazwy klasy nie możemy potem tworzyć innych obiektów; jeśli jednak to wyrażenie new umieścimy np. w pętli to oczywiście stworzone zostanie tyle obiektów ile razy wykona się pętla, definiowanie klas wewnętrznych implementujących interfejsy stanowi jedyny dopuszczalny przypadek użycia nazwy interfejsu w wyrażeniu new anonimowe klasy wewnętrzne są kompilowane do plików.class o nazwach automatycznie nadawanych przez kompilator (nazwa składa się z nazwy klasy otaczającej i jakiegoś automatycznie nadawanego identyfikatora) Klasy wewnętrzne (nazwane i anonimowe) mogą być definiowane w blokach lokalnych (np. w ciele metody). Będziemy je krótko nazywać klasami lokalnymi. Wewnętrzne klasy lokalne są doskonale odseparowane (nie ma do nich żadnego dostępu spoza bloku, w którym są zdefiniowane), a mogą odwoływać się do składowych klasy otaczającej oraz zmiennych lokalnych zadeklarowanych w bloku (pod warunkiem, że są one niezmienne). 51

Anonimowe klasy wewnętrzne - przykład Przykład: Listowanie katalogu z filtrowaniem nazw (pliki.java) Obiekty plikowe (pliki i katalogi) są obiektami klasy File z pakietu java.io. Wobec katalogu można użyć metody list z klasy File, która zwraca tablicę nazw plików (i podkatalogów) w nim zawartych. Używając metody list z argumentem typu FilenameFilter możemy określić kryteria filtrowania wyniku wg nazw (np. otrzymać tylko listę plików o rozszerzeniu.java): public String[] list(filenamefilter filter) FilenameFilter jest interfejsem, w którym zawarto jedną metodę boolean accept(file dir, String filename). Musimy zatem mieć obiekt klasy implementującej FilenameFilter, w której to klasie zdefiniujemy metodę accept i podać referencję do tego obiektu jako argument metody list. Metoda accept będzie wtedy wywoływana dla każdego obiektu plikowego, zawartego w katalogu z argumentami katalog, nazwa pliku lub podkatalogu. Powinniśmy ją zdefiniować w taki sposób, by zwracała true tylko wtedy, gdy nazwa spełnia wymagane przez nas kryteria, a w przeciwnym razie false. Naturalnym sposobem oprogramowania jest tu umieszczenie definicji anonimowej klasy wewnętrznej implementującej FilenameFilter w wyrażeniu new podanym jako argument metody list. A ponieważ listowanie umieszczamy w jakiejś metodzie, to ta anonimowa klasa będzie lokalną klasą wewnętrzną. 52

53 Anonimowe klasy wewnętrzne - przykład void listjavafiles(string dirname) { // argument - nazwa katalogu File dir = new File(dirName); // katalog - obiekt typu File // listowanie z filtrowaniem nazw; kryteria wyboru nazw // podajemy za pomocą implementacji metody accept // w lokalnej anonimowej klasie wewnętrznej String[] fnames = dir.list( new FilenameFilter() { public boolean accept(file dir, String name) { return name.endswith(".java"); ); for (int i=0; i < fnames.length; i++) { // lista -> stdout System.out.println(fnames[i]);

Bloki inicjacyjne (ang. initialization blocks) 54 W Javie zasadniczo nie można używać instrukcji sterujących poza ciałami metod i konstruktorów. Od tej zasady istnieją dwa wyjątki, a mianowicie użycie: niestatycznego bloku inicjacyjnego (inicjującego lub inicjalizacyjnego) statycznego bloku inicjacyjnego (inicjującego lub inicjalizacyjnego) Niestatyczny blok inicjacyjny wprowadzamy ujmując kod wykonywalny w nawiasy klamrowe i umieszczając taką konstrukcję w definicji klasy poza ciałem jakiejkolwiek metody (czy konstruktora). Kod bloku zostanie wykonany na etapie inicjacji obiektu, czyli przy tworzeniu obiektu, przed wywołaniem konstruktora. Taka możliwość może okazać się przydatna, gdy mamy kilka konstruktorów i chcemy wyróżnić pewien kod, który będzie inicjował obiekt niezależnie od użytego konstruktora i przed użyciem jakiegokolwiek z nich. Jeśli blok inicjujący zgłasza wyjątek, to musi on być zadeklarowany w klauzuli throws każdego konstruktora. Bloki inicjujące mogą zawierać dowolne instrukcje (np. pętle). Można zdefiniować wiele bloków inicjujących (ale nie należy tego robić, ze względu na czytelność).

Bloki inicjacyjne (inicjujące) -przykład class A { final static int n = 10; int tab[] = new int[n]; { //Blok inicjujący tablicę, nie musimy tej inicjacji //wpisywać do poszczególnych konstruktorów. //Można to osiągnąć przy pomocy odpowiedniej metody. for(int i=0; i<n; i++) tab[i] = i; System.out.println("Inicjalizacja"); //Wywołanie metod też jest dozwolone //konstruktory A() { //... A(int i) { //... 55

Bloki inicjacyjne 56 Bloki inicjujące są przydatne: w klasach anonimowych, bo tam nie da się zdefiniować konstruktora, instrukcje w bloku inicjującym mogą inicjować zmienne final, czego nie można zrobić w treści metody. Statyczne (klasowe) bloki inicjacyjne. Można też definiować bloki inicjujące wykonujące się przy tworzeniu klasy (a nie obiektu). Czyli dokonujące inicjacji na rzecz całej klasy. Ich deklaracja wygląda tak samo jak w przypadku zwykłych (egzemplarzowych) bloków inicjujących, tyle że cały blok poprzedza słowo static. Oczywiście nie ma w nim dostępu do zmiennych egzemplarzowych. W tych blokach nie można także zgłaszać sprawdzalnych wyjątków (bo nie ma gdzie ich przechwytywać). Korzystamy z nich, gdy pojawia się potrzeba wykonania jakiegoś kodu jeden raz, przy pierwszym odwołaniu do klasy (np. użyciu metody statycznej lub stworzeniu pierwszego obiektu). Przy inicjacji pól statycznych możemy skorzystać z dowolnych wyrażeń, składających się ze zmiennych i stałych statycznych oraz z wywołań statycznych metod, ale - oczywiście - nie sposób użyć instrukcji wykonywalnych (np. sterujących).

REGUŁY INICJACJI Inicjacja klasy powoduje jednokrotne zainicjowanie elementów statycznych tzn. najpierw wszystkie pola statyczne uzyskują wartości domyślne, a następnie wykonywane są inicjatory statyczne (tzn. inicjatory pól statycznych oraz statyczne bloki inicjacyjne) w kolejności ich występowania w klasie. Inicjacja klasy następuje w wyniku jej załadowania przez JVM, co może się zdarzyć przy uruchomieniu głównej klasy programu lub pierwszym odwołaniu z programu do innej klasy na skutek odwołania do składowej statycznej Tworzenie każdego obiektu (new) powoduje nadanie niestatycznym polom klasy wartości domyślnych (0, false dla typu boolean, null dla referencji), następnie wykonanie inicjatorów niestatycznych w kolejności ich występowania w klasie, po czym wykonywany jest konstruktor. W momencie tworzenia jakiegokolwiek obiektu wszystkie pola statyczne są już zainicjowane i zostały już wykonane wszystkie inicjatory statyczne. W inicjatorach statycznych można odwoływać się do wszystkich statycznych metod klasy, ale tylko do tych statycznych pól, których deklaracje poprzedzają inicjator. W inicjatorach niestatycznych można odwoływać się do wszystkich metod klasy, do wszystkich pól statycznych (niezależnie od miejsca ich występowania), ale tylko do tych pól niestatycznych, których deklaracje poprzedzają inicjator. W konstruktorze można odwoływać się do wszystkich metod i pól klasy (są już zainicjowane). 57

public class InitOrder { REGUŁY INICJACJI private static int s = 100; private static final int C; private int a = 1; InitOrder() { report("konstruktor: s, C, a, b mają wartości :", s, C, a, b); private int b = 2; { report("blok inicjacyjny: s, C, a =", s, C, a); static { report("statyczny blok inicjacyjny, zmienna s = ", s); C = 101; // opóźniona inicjacja stałej! private static void report(string msg, int... args ) { System.out.print(msg + " "); for (int i : args) { System.out.print(i + " "); System.out.println(); public static void main(string[] args) { report("wywołanie metody main"); new InitOrder();new InitOrder(); 58

REGUŁY INICJACJI 59 W wyniku zostanie wyprowadzony kod: Statyczny blok inicjacyjny, zmienna s = 100 Wywołanie metody main Blok inicjacyjny: s, C, a = 100 101 1 Konstruktor: s, C, a, b mają wartości : 100 101 1 2 Blok inicjacyjny: s, C, a = 100 101 1 Konstruktor: s, C, a, b mają wartości : 100 101 1 2 Podsumowując: najpierw - i tylko raz - inicjowane są kolejno pola statyczne, a przy każdym tworzeniu obiektu - kolejno - pola niestatyczne.