JAVA- wykład 3
Transkrypt
JAVA- wykład 3
JAVA- wykład 3 Wprowadzanie danych Typ wyliczeniowy Tablice 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/ ● C. S. Horstmann, G. Cornell, Java. Podstawy, Helion, Gliwice 2008 1 Napisy Napisy (łańcuchy znakowe) są obiektami klasy String (zatem wszystko co mówiliśmy o obiektach, dotyczy również tej klasy). Dodatkowo jednak kompilator dostarcza nam pewnych udogodnień: ● ● Tworząc obiekty klasy String nie musimy używać wyrażenia new. Możemy napisać skrótowo String s = ”Ala ma kota” zamiast String s = new String(”Ala ma kota”); Możliwość użycia operatora + w znaczeniu konkatenacji (łączenia) łańcuchów String s1=”Ala ma kota”; String s2=” szarego ”; int lata=3; String s3=s1 + s2 + ”w wieku ” + lata + ” lat”; //wartość lata przekształcona w odpowiedni String 2 NAPISY ● ● ● ● Jeśli w wyrażeniu konkatenacji łańcuchów znakowych wystąpi referencja do obiektu jakiejś klasy, to dla obiektu zostanie wywołana metoda toString() i otrzymany łańcuch zostanie dołączony do łańcucha Para p = new Para(10,11); String s = ”Para = ” + p;// Para = (10,12) UWAGA 1: Operator ”+” traktowany jest jako operator konkatenacji łańcuchów znakowych, wtedy gdy jeden z jego argumentów jest klasy String: np. int a=1, b=3; String s=a+b;//BŁĄD UWAGA 2: Pamiętajmy o kolejności opracowywania wyrażeń: String s=”nr” + 1 + 2; //nr12 String s1=”nr” + (1 + 2);//nr3 ==, != porównanie referencji, equals() porównanie łańcuchów 3 Wprowadzanie danych: okna dialogowe ● Do wprowadzania danych możemy użyć skanera (klasy Scanner z pakietu java.util) dialogów wejściowych (statycznych metod showInputDialog z klasy JOptionPane z pakietu javax.swing. Zapis: String s=JOptionPane.showInputDialog(”Tekst:”); spowoduje otwarcie okna dialogowego z komunikatem ”Tekst:”, w którym będzie można wprowadzić tekst. Po kliknięciu przycisku OK wprowadzony łańcuch będzie dostępny za pomocą zmiennej s, natomiast jeśli zamkniemy dialog przyciskiem Cancel, to zmienna s będzie mieć wartość null. 4 Wprowadzanie danych: okna dialogowe ● Do pokazania wyników w okienku komunikatów możemy użyć statycznej metody showMessageDialog z klasy JOptionPane z pakietu javax.swing. Zapis: JOptionPane.showMessageDialog(null,”Komunikat”); pokaże okno z komunikatem ”Komunikat” (null mówi m.in., że okno ma być wycentrowane) Przykład. import static javax.swing.JOptionPane.*; public class Powitanie { public static void main(String[] args) { String imie = showInputDialog("Podaj swoje imię"); if (imie==null) imie=""; showMessageDialog(null, "Witaj "+ imie); } } 5 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); } } 6 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,możemy 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); } } 7 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(); System.out.print("Podaj jeszcze raz: "); } double d = scan.nextDouble(); System.out.println("Podałeś: " + d); //NumberFormat.getInstance().format(d)); } } 8 Wprowadzanie liczb - okna dialogowe ● Gdy w polu tekstowym podamy jakąś liczbę, to będzie ona dostępna jako napis String. Do przekształcania łańcuchów na liczby całkowite używamy metody: int liczba=Integer.parseInt(napis); Jeśli przekształcany napis nie reprezentuje liczby całkowitej, to zostanie wyrzucony wyjątek NumberFormatException. import static javax.swing.JOptionPane.*; public class ParseIntTest { public static void main(String[] args) { String s1 = showInputDialog("Podaj liczbę: "); if (s1!=null) { int i=Integer.parseInt(s1); showMessageDialog(null,"Kwadrat = " + (i*i)); } //if }//main }//class 9 Wprowadzanie liczb - okna dialogowe ● Podobnie, stosując metodę double Double.parseDouble(String s); możemy przekształcić znakową reprezentację liczb rzeczywistych w ich postać binarną. Metoda ta nie uwzględnia lokalizacji i zawsze za separator miejsc dziesiętnych przyjmuje ”.”. import java.lang.*; public class DoubleDemo { public static void main(String[] args) { Double d = new Double("6.35"); String str = "50"; double retval = d.parseDouble(str); // lub //double retval = Double.parseDouble(str); System.out.println("Value = " + retval); } } 10 Wprowadzanie liczb - okna dialogowe + skaner ● Skaner możemy łączyć z oknami dialogowymi, może on bowiem pobierać dane m.in.. z napisów: import java.util.*; import static javax.swing.JOptionPane.*; public class ScanNumsFromString { public static void main(String[] args) { } } String s = showInputDialog("Podaj 2 liczby "+ ”rozdzielone spacjami”); if (s!=null) { Scanner scan = new Scanner(s); int suma=scan.nextInt()+scan.nextInt(); showMessageDialog(null,"Suma = " + suma); } 11 TYP WYLICZENIOWY enum ● Definiowanie typu wyliczeniowego polega na umieszczeniu po słowie enum w nawiasach klamrowych elementów el wyliczenia, rozdzielonych przecinkami: [public] enum NazwaTypu { el1, el2,..., elN } Typ wyliczeniowy jest rodzajem typu referencyjnego pochodnego od typu Enum. ● Przykład enum Pora {ZIMA, WIOSNA, LATO, JESIEŃ} Pora-nazwa typu ZIMA, WIOSNA, LATO, JESIEŃ-stałe tego typu. Zmienna Pora p; może przyjmować wartości Pora.ZIMA, Pora.WIOSNA, Pora.LATO, Pora.JESIEŃ oraz null. Zauważmy, że wartości te to stałe statyczne typu referencyjnego Pora. 12 TYP WYLICZENIOWY enum Dla danych typów wyliczeniowych możemy stosować różne metody, np.: ● ● ● ● ● toString() - zwraca wartość zmiennej w postaci napisu, np. Pora p = Pora.ZIMA; System.out.println(p); wypisze na konsoli napis ZIMA valueOf(String s) – statyczna metoda zwracająca wartość typu wyliczeniowego, odpowiadającą argumentowi s, np. Pora.valueOf(”LATO”); zwróci wartość Pora.LATO ordinal() zwraca pozycję zmiennej w wyliczeniu (pozycje numerowane są od 0) np. Pora p=Pora.WIOSNA; to p.ordinal()zwróci 1 values() - metoda statyczna zwracająca tablicę wszystkich elementów typu wyliczeniowego (Pora.ZIMA, Pora.WIOSNA, Pora.LATO, Pora.JESIEŃ) ==, != dla porównania wartości typów wyliczeniowych 13 TYP WYLICZENIOWY enum ● ● Aby używać skróconych nazw np. ZIMA, zamiast Pora.Zima, należy statycznie zaimportować nazwy z wyliczenia (przypomnijmy, że import statyczny jest możliwy z klas umieszczonych w nazwanych pakietach) np. package enums; import static enums.Pora.*; enum Pora{ZIMA, WIOSNA, LATO, JESIEŃ}; public class PoryRoku{ public static void main(String[] args) { Pora p=ZIMA; System.out.println(p);//wyprowadzi napis ZIMA } } Wyliczenia (enum) to klasy, o pewnych określonych właściwościach. Dla enum konstruktor jest prywatny i wywołuje się go poprzez użycie nawiasów ”()” przy definiowaniu elementów wyliczenia. Nie można go wywołać spoza klasy i używać wyrażenia new do tworzenia wyliczeń. 14 TYP WYLICZENIOWY enum Przykład. Enum z konstruktorem i metodami w osobnym pliku package enumtest; public enum PoraRoku{ ZIMA("pada śnieg", 5), WIOSNA("wszystko kwitnie", 2), LATO("wakacje", 3), JESIEŃ("deszcz", 2); private String opis; private int ileMies; private PoraRoku(String opis, int ileMies){ this.opis=opis; this.ileMies=ileMies; } public String getOpis(){return opis;} public int getIleMies() {return ileMies;} } 15 TYP WYLICZENIOWY enum Wykorzystanie tak zdefiniowanego wyliczenia PoraRoku package enumtest; import static enumtest.PoraRoku.*; import static javax.swing.JOptionPane.*; public class EnumTest { public static void main(String[] args) { String nazwa = showInputDialog("Podaj porę roku kapitalikami"); PoraRoku p = valueOf(nazwa); System.out.println(p + " trwa " + p.getIleMies() + " miesiecy, " + p.getOpis()); System.out.println(JESIEŃ + " i " + ZIMA + " trwają" + (JESIEŃ.getIleMies() + ZIMA.getIleMies() + " miesięcy.")); for (PoraRoku pr : PoraRoku.values()) { System.out.println(pr + ": " + pr.getOpis()); } } } 16 TABLICE ● 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. Zatem, w szczególności elementami tablic mogą być referencje do innych tablic. Mamy wtedy do czynienia z odpowiednikiem tablic wielowymiarowych. ● 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ę. 17 TABLICE ● Przykładowa deklaracja zmiennych tablicowych: String[] s; // jest deklaracją tablicy referencji do obiektów // klasy String, s jest referencją double[][] d; // jest deklaracją dwuwymiarowej tablicy liczb // rzeczywistych ● Taka deklaracja nie alokuje pamięci dla tablicy. ● Pamięć jest alokowana dynamicznie albo w wyniku inicjacji za pomocą nawiasów klamrowych albo w wyniku użycia wyrażenia new. ● Inicjacja 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 } 18 TABLICE - TWORZENIE ● ● ● Np. int[] arr = { 1, 2, 7, 21 }; deklaruje tablicę o nazwie arr, tworzy ją i inicjuje jej elementy; kolejno: – Wydzielana jest pamięć dla zmiennej arr, która będzie przechowywać referencję do obiektu-tablicy. – Wydzielana jest pamięć (dynamicznie, na stercie) potrzebna do przechowania 4 liczb całkowitych (typu int), a następnie wartości 1,2,7,21 są zapisywane kolejno w tym obszarze pamięci. – Adres tego obszaru (referencja) jest przypisywany zmiennej arr. Tworzenie tablicy za pomocą wyrażenia new ma postać new Typ[n]; gdzie: – Typ - typ elementów tablicy – n - rozmiar tablicy (liczba elementów tablicy) Np. int[] arr = new int[4]; 19 TABLICE - INDEKSY ● 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. Tablice zawsze indeksowane są poczynając od 0. Czyli pierwszy element n-elementowej tablicy ma indeks 0, a ostatni - indeks n-1. 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. 20 TABLICE - INDEKSY ● ● ● ● Odwołanie do i-go elementu tablicy o nazwie tab ma postać: tab[i] Ta konstrukcja składniowa traktowana jest jako zmienna, stanowi nazwę zmiennej - 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, na skutek czego zostanie wyprowadzony odpowiedni komunikat i wykonanie programu zostanie przerwane (ew. taki wyjątek możemy obsłużyć). Informacje o rozmiarze (liczbie elementów) tablicy możemy uzyskać za pomocą odwołania: nazwa_tablicy.length W Javie length nie jest nazwą metody (lecz pola niejawnie stworzonej klasy, opisującej tablicę), dlatego NIE STAWIAMY po nim nawiasów okrągłych 21 TABLICE - PRZYPISANIA ● ● ● ● ● 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). 22 TABLICE JAKO ARUMENTY I WYNIKI METOD ● ● ● ● ● 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 23 TABLICE JAKO ARUMENTY I WYNIKI METOD public class Test { static int[] dblVal(int[] tab) { int[] w = new int[tab.length]; 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 i=0; i < wynik.length; i++) System.out.print(" " + wynik[i]) } } 24 TABLICE OBIEKTÓW ● Pamiętamy, że w Javie argumenty przekazywane są metodom przez wartość. Gdy argumentem jest zmienna tablicowa - przekazywana jest referencja do tablicy - i tej oczywiście w metodzie nie jesteśmy w stanie efektywnie zmienić. Nic jednak nie stoi na przeszkodzie, by zmienić elementy przekazanej tablicy. ● Elementami tablicy mogą być referencje do dowolnych obiektów, np. String[] miasta = {"Warszawa", "Poznań", "Kraków"}; ● Stwórzmy tablicę obiektów np. klasy Para Para[] tabPar = new Para[10]; Samo stworzenie tablicy nie tworzy jednak obiektów, do których referencje są elementami tablicy. Zatem elementy tablicy na początku będą miały domyślne wartości null - a dopiero po stworzeniu obiektów i przypisaniu referencji (ich adresów) elementom tablicy będziemy mogli używać elementów tablicy w operacjach na obiektach. 25 TABLICE OBIEKTÓW Np. ● ● public class TabPar { public static void main(String[] args) { Para[] tabPar = new Para[10]; for (int i=0; i < tabPar.length; i++) tabPar[i] = new Para(i+1, i+2); //for (int i=0; i < tabPar.length; i++) // tabPar[i].show(); for (Para par : tabPar) par.show(); } } Button tabB = new Button[10]; for (int i=0; i<tabB.length; i++) tabB[i] = new Button(); 26 TABLICE OBIEKTÓW ● W przypadku tablic referencji do obiektów możemy użyć inicjatorów klamrowych Para[] tabPara = {new Para(1,1),new Para(2,3), new Para(4,5) }; Button[] b = {new Button("A"),new Button("B") }; ● Przypomnijmy, że obiektowe konwersje rozszerzające pozwalają przypisywać zmiennym oznaczającym obiekty klasy bazowej referencje do obiektów klas pochodnych. Zatem elementy tablicy mogą zawierać referencje wskazujące na obiekty różnych klas, pod warunkiem, że klasy te mają tę samą klasę bazową- określającą ogólny, niejako wspólny dla wszystkich, typ elementów tablicy. Np. dla nadklasy Zwierze i podklasy Pies z poprzedniego wykładu: Zwierze[] tabz = { new Zwierze(..), new Pies(...), new Pies() }; 27 TABLICE OBIEKTÓW ● ● ● 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 } ); 28 TABLICE WIELOWYMIAROWE ● 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][]; //rozmiary 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 29 TABLICE WIELOWYMIAROWE public class MultiArr { public static void main(String[] arg) { int w[] = { 2, 3, 4 }; int n = 3; //rozmiary wierszy będą zmieniane 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); } } } 30 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"}); 31 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 32