SlideShare a Scribd company logo
Java.
Techniki zaawansowane.
Wydanie VIII
Autor: Cay S. Horstmann, Gary Cornell
T³umaczenie: Jaromir Senczyk
ISBN: 978-83-246-1483-7
Tytu³ orygina³u: Core Java(tm),
Volume II--Advanced Features:
Eighth Edition
Format: 172x245, stron: 1064


    • Jak wykorzystaæ strumienie?
    • Jak stworzyæ efektowny interfejs u¿ytkownika?
    • Jak zapewniæ bezpieczeñstwo w tworzonych aplikacjach?
Co spowodowa³o, ¿e jêzyk programowania Java zyska³ tak wielk¹ popularnoœæ?
Przyczyn jest kilka: mo¿liwoœæ przenoszenia kodu miêdzy programami, wydajnoœæ i to,
co programiœci lubi¹ najbardziej – mechanizm automatycznego oczyszczania pamiêci.
Nie bez znaczenia jest równie¿ to, ¿e Java jest jêzykiem zorientowanym obiektowo,
udostêpnia obs³ugê programowania rozproszonego oraz œwietn¹ dokumentacjê.
Ponadto liczne publikacje oraz pomocna spo³ecznoœæ sprawiaj¹, ¿e Java zajmuje
poczesne miejsce wœród innych jêzyków programowania.
Kolejne wydanie ksi¹¿ki „Java. Techniki zaawansowane. Wydanie VIII” zosta³o
zaktualizowane o wszystkie te elementy, które pojawi³y siê w wersji szóstej platformy
Java Standard Edition. Dziêki tej ksi¹¿ce dowiesz siê, w jaki sposób wykorzystaæ
strumienie, jak parsowaæ dokumenty XML czy te¿ w jaki sposób tworzyæ aplikacje
sieciowe. Poznasz interfejs JDBC, sposób wykorzystania transakcji oraz wykonywania
zapytañ SQL. Autorzy w szczegó³owy sposób poka¿¹ Ci, jak tworzyæ aplikacje
z wykorzystaniem biblioteki Swing. Dodatkowo przedstawi¹, w jaki sposób zapewniæ
bezpieczeñstwo w tworzonych przez Ciebie aplikacjach. Wszystkie te – oraz wiele
innych – zagadnienia zostan¹ przedstawione w przystêpny i sprawdzony sposób!
    • Wykorzystanie strumieni
    • Dokumenty XML i ich wykorzystanie w jêzyku Java
    • Programowanie aplikacji sieciowych
    • Wykorzystanie interfejsu JDBC
    • Tworzenie aplikacji wielojêzycznych
    • Mo¿liwoœci pakietu Swing
    • Wykorzystanie biblioteki AWT
    • Bezpieczeñstwo w aplikacjach
    • Zastosowanie podpisu cyfrowego
    • Sposoby wykorzystania obiektów rozproszonych (RMI)
        Wykorzystaj zaawansowane mo¿liwoœci jêzyka Java w swoich projektach!
Spis treÊci
Przedmowa ...............................................................................................................................................11

PodziÂkowania .........................................................................................................................................15

RozdziaÄ 1. Strumienie i pliki ....................................................................................................................17
     Strumienie ................................................................................................................... 17
         Odczyt i zapis bajtów ............................................................................................... 18
         Zoo peäne strumieni ................................................................................................ 20
         ãñczenie filtrów strumieni ........................................................................................ 24
     Strumienie tekstowe ..................................................................................................... 27
         Zapisywanie tekstu ................................................................................................. 28
         Wczytywanie tekstu ................................................................................................. 31
         Zapis obiektów w formacie tekstowym ...................................................................... 31
         Zbiory znaków ......................................................................................................... 35
     Odczyt i zapis danych binarnych ..................................................................................... 40
         Strumienie plików o swobodnym dostöpie ................................................................. 43
     Strumienie plików ZIP ................................................................................................... 48
     Strumienie obiektów i serializacja .................................................................................. 55
         Format pliku serializacji obiektów ............................................................................. 61
         Modyfikowanie domy lnego mechanizmu serializacji .................................................. 67
         Serializacja singletonów i wyliczeþ ............................................................................ 70
         Wersje ................................................................................................................... 71
         Serializacja w roli klonowania ................................................................................... 73
     Zarzñdzanie plikami ...................................................................................................... 75
     Ulepszona obsäuga wej cia i wyj cia .............................................................................. 82
         Mapowanie plików w pamiöci ................................................................................... 82
         Struktura bufora danych .......................................................................................... 89
         Blokowanie plików ................................................................................................... 91
     Wyra enia regularne ..................................................................................................... 93

RozdziaÄ 2. JÂzyk XML ...........................................................................................................................103
     Wprowadzenie do jözyka XML ...................................................................................... 104
        Struktura dokumentu XML ..................................................................................... 106
     Parsowanie dokumentów XML ..................................................................................... 109
4       Java. Techniki zaawansowane

        Kontrola poprawno ci dokumentów XML ...................................................................... 120
            Definicje typów dokumentów .................................................................................. 122
            XML Schema ........................................................................................................ 129
            Praktyczny przykäad ............................................................................................... 131
        Wyszukiwanie informacji i XPath .................................................................................. 145
        Przestrzenie nazw ....................................................................................................... 151
        Parsery strumieniowe ................................................................................................. 154
            Wykorzystanie parsera SAX .................................................................................... 154
            Wykorzystanie parsera StAX ................................................................................... 159
        Tworzenie dokumentów XML ....................................................................................... 163
            Tworzenie dokumentu XML za pomocñ parsera StAX ................................................ 167
        Przeksztaäcenia XSL ................................................................................................... 174

    RozdziaÄ 3. Programowanie aplikacji sieciowych ...............................................................................185
        Poäñczenia z serwerem ............................................................................................... 185
           Limity czasu gniazd ............................................................................................... 190
           Adresy internetowe ............................................................................................... 191
        Implementacja serwerów ............................................................................................ 193
           Obsäuga wielu klientów .......................................................................................... 196
           Poäñczenia czö ciowo zamkniöte ............................................................................ 200
           Przerywanie dziaäania gniazd sieciowych .................................................................. 201
        Wysyäanie poczty elektronicznej ................................................................................... 207
        Poäñczenia wykorzystujñce URL .................................................................................... 212
           URL i URI ............................................................................................................. 212
           Zastosowanie klasy URLConnection do pobierania informacji ................................... 214
           Wysyäanie danych do formularzy ............................................................................. 224

    RozdziaÄ 4. Poľczenia do baz danych: JDBC .......................................................................................233
        Architektura JDBC ...................................................................................................... 234
            Typy sterowników JDBC ......................................................................................... 235
            Typowe zastosowania JDBC ................................................................................... 236
        Jözyk SQL .................................................................................................................. 237
        Instalacja JDBC .......................................................................................................... 243
            Adresy URL baz danych ......................................................................................... 243
            Pliki JAR zawierajñce sterownik .............................................................................. 244
            Uruchamianie bazy danych ..................................................................................... 244
            Rejestracja klasy sterownika .................................................................................. 245
            Nawiñzywanie poäñczenia z bazñ danych ................................................................. 246
            Wykonywanie poleceþ jözyka SQL ........................................................................... 248
            Zarzñdzanie poäñczeniami, poleceniami i zbiorami wyników ....................................... 251
            Analiza wyjñtków SQL ............................................................................................ 252
            Wypeänianie bazy danych ....................................................................................... 255
        Wykonywanie zapytaþ ................................................................................................. 258
            Polecenia przygotowane ........................................................................................ 259
            Odczyt i zapis du ych obiektów ............................................................................... 267
            Sekwencje sterujñce ............................................................................................. 269
            Zapytania o wielu zbiorach wyników ........................................................................ 270
            Pobieranie warto ci kluczy wygenerowanych automatycznie ...................................... 271
        Przewijalne i aktualizowalne zbiory wyników zapytaþ ...................................................... 272
            Przewijalne zbiory wyników ..................................................................................... 272
            Aktualizowalne zbiory rekordów .............................................................................. 274
Spis treÊci              5

     Zbiory rekordów .......................................................................................................... 279
        Buforowane zbiory rekordów ................................................................................... 279
     Metadane .................................................................................................................. 282
     Transakcje ................................................................................................................. 292
        Punkty kontrolne ................................................................................................... 293
        Aktualizacje wsadowe ............................................................................................ 293
        Zaawansowane typy jözyka SQL ............................................................................. 295
     Zaawansowane zarzñdzanie poäñczeniami .................................................................... 297
     Wprowadzenie do LDAP .............................................................................................. 298
        Konfiguracja serwera LDAP .................................................................................... 299
        Dostöp do informacji katalogu LDAP ....................................................................... 303

RozdziaÄ 5. Internacjonalizacja .............................................................................................................315
     Lokalizatory ............................................................................................................... 316
     Formaty liczb .............................................................................................................. 321
         Waluty .................................................................................................................. 326
     Data i czas ................................................................................................................ 328
     Porzñdek alfabetyczny ................................................................................................. 335
         Moc uporzñdkowania ............................................................................................. 337
         Rozkäad ................................................................................................................ 337
     Formatowanie komunikatów ........................................................................................ 343
         Formatowanie z wariantami .................................................................................... 345
     Pliki tekstowe i zbiory znaków ...................................................................................... 347
         Internacjonalizacja a pliki ródäowe programów ........................................................ 347
     Komplety zasobów ..................................................................................................... 348
         Lokalizacja zasobów .............................................................................................. 349
         Pliki wäa ciwo ci ................................................................................................... 350
         Klasy kompletów zasobów ..................................................................................... 351
     Kompletny przykäad .................................................................................................... 353

RozdziaÄ 6. Zaawansowane moÑliwoÊci pakietu Swing ......................................................................367
     Listy .......................................................................................................................... 367
         Komponent JList ................................................................................................... 368
         Modele list ........................................................................................................... 374
         Wstawianie i usuwanie .......................................................................................... 379
         Odrysowywanie zawarto ci listy .............................................................................. 381
     Tabele ....................................................................................................................... 386
         Najprostsze tabele ................................................................................................ 386
         Modele tabel ........................................................................................................ 390
         Wiersze i kolumny ................................................................................................. 394
     Drzewa ...................................................................................................................... 421
         Najprostsze drzewa ............................................................................................... 422
         Przeglñdanie wözäów .............................................................................................. 438
         Rysowanie wözäów ................................................................................................. 440
         Nasäuchiwanie zdarzeþ w drzewach ........................................................................ 443
         Wäasne modele drzew ........................................................................................... 450
     Komponenty tekstowe ................................................................................................ 458
           ledzenie zmian zawarto ci komponentów tekstowych ............................................. 459
         Sformatowane pola wej ciowe ............................................................................... 463
         Komponent JSpinner ............................................................................................. 479
         Prezentacja HTML za pomocñ JEditorPane .............................................................. 487
6       Java. Techniki zaawansowane

        Wska niki postöpu ..................................................................................................... 494
           Paski postöpu ...................................................................................................... 494
           Monitory postöpu .................................................................................................. 498
           Monitorowanie postöpu strumieni wej cia ............................................................... 501
        Organizatory komponentów ......................................................................................... 507
           Panele dzielone .................................................................................................... 507
           Panele z zakäadkami .............................................................................................. 511
           Panele pulpitu i ramki wewnötrzne .......................................................................... 518
           Rozmieszczenie kaskadowe i sñsiadujñce ............................................................... 521
           Zgäaszanie weta do zmiany wäa ciwo ci .................................................................. 529

    RozdziaÄ 7. Zaawansowane moÑliwoÊci biblioteki AWT ......................................................................537
        Potokowe tworzenie grafiki .......................................................................................... 538
        Figury ........................................................................................................................ 540
            Wykorzystanie klas obiektów graficznych ................................................................. 542
        Pola .......................................................................................................................... 555
          lad pödzla ................................................................................................................ 556
        Wypeänienia ............................................................................................................... 564
        Przeksztaäcenia ukäadu wspóärzödnych ......................................................................... 566
        Przycinanie ................................................................................................................ 571
        Przezroczysto è i skäadanie obrazów ............................................................................ 573
        Wskazówki operacji graficznych ................................................................................... 581
        Czytanie i zapisywanie plików graficznych ..................................................................... 587
            Wykorzystanie obiektów zapisu i odczytu plików graficznych ...................................... 588
            Odczyt i zapis plików zawierajñcych sekwencje obrazów ............................................ 592
        Operacje na obrazach ................................................................................................. 598
            Dostöp do danych obrazu ...................................................................................... 598
            Filtrowanie obrazów ............................................................................................... 604
        Drukowanie ............................................................................................................... 613
            Drukowanie grafiki ................................................................................................ 614
            Drukowanie wielu stron ......................................................................................... 623
            Podglñd wydruku ................................................................................................... 624
            Usäugi drukowania ................................................................................................. 633
            Usäugi drukowania za po rednictwem strumieni ....................................................... 637
            Atrybuty drukowania .............................................................................................. 638
        Schowek ................................................................................................................... 644
            Klasy i interfejsy umo liwiajñce przekazywanie danych ............................................. 645
            Przekazywanie tekstu ............................................................................................ 646
            Interfejs Transferable i formaty danych ................................................................... 650
            Przekazywanie obrazów za pomocñ schowka ........................................................... 652
            Wykorzystanie lokalnego schowka do przekazywania referencji obiektów ................... 657
            Wykorzystanie schowka systemowego do przekazywania obiektów Java ..................... 657
            Zastosowanie lokalnego schowka do przekazywania referencji obiektów .................... 661
        Mechanizm „przeciñgnij i upu è” ................................................................................. 662
            Przekazywanie danych pomiödzy komponentami Swing ............................................. 664
              ródäa przeciñganych danych .................................................................................. 667
            Cele upuszczanych danych ..................................................................................... 670
        Integracja z macierzystñ platformñ ............................................................................... 678
            Ekran powitalny .................................................................................................... 678
            Uruchamianie macierzystych aplikacji pulpitu .......................................................... 683
            Zasobnik systemowy ............................................................................................. 688
Spis treÊci              7


RozdziaÄ 8. JavaBeans ..........................................................................................................................693
     Dlaczego ziarnka? ...................................................................................................... 694
     Proces tworzenia ziarnek JavaBeans ............................................................................ 696
     Wykorzystanie ziarnek do tworzenia aplikacji ................................................................ 698
         Umieszczanie ziarnek w plikach JAR ....................................................................... 699
         Korzystanie z ziarnek ............................................................................................. 700
     Wzorce nazw wäa ciwo ci ziarnek i zdarzeþ ................................................................... 705
     Typy wäa ciwo ci ziarnek ............................................................................................. 709
         Wäa ciwo ci proste ............................................................................................... 709
         Wäa ciwo ci indeksowane ...................................................................................... 710
         Wäa ciwo ci powiñzane ......................................................................................... 710
         Wäa ciwo ci ograniczone ....................................................................................... 712
     Klasa informacyjna ziarnka .......................................................................................... 719
     Edytory wäa ciwo ci .................................................................................................... 722
         Implementacja edytora wäa ciwo ci ........................................................................ 726
     Indywidualizacja ziarnka .............................................................................................. 733
         Implementacja klasy indywidualizacji ...................................................................... 735
     Trwaäo è ziarnek JavaBeans ........................................................................................ 742
         Zastosowanie mechanizmu trwaäo ci JavaBeans dla dowolnych danych ..................... 746
         Kompletny przykäad zastosowania trwaäo ci JavaBeans ............................................ 752

RozdziaÄ 9. BezpieczeÆstwo .................................................................................................................763
     ãadowanie klas .......................................................................................................... 764
        Hierarchia klas äadowania ...................................................................................... 766
        Zastosowanie procedur äadujñcych w roli przestrzeni nazw ........................................ 768
        Implementacja wäasnej procedury äadujñcej ............................................................. 769
     Weryfikacja kodu maszyny wirtualnej ............................................................................ 774
     Mened ery bezpieczeþstwa i pozwolenia ...................................................................... 779
        Bezpieczeþstwo na platformie Java ........................................................................ 781
        Pliki polityki bezpieczeþstwa .................................................................................. 784
        Tworzenie wäasnych klas pozwoleþ ......................................................................... 790
        Implementacja klasy pozwoleþ ............................................................................... 792
        Uwierzytelnianie u ytkowników ............................................................................... 798
        Moduäy JAAS ......................................................................................................... 804
     Podpis cyfrowy ........................................................................................................... 813
        Skróty wiadomo ci ................................................................................................ 814
        Podpisywanie wiadomo ci ..................................................................................... 820
        Certyfikaty X.509 .................................................................................................. 822
        Weryfikacja podpisu .............................................................................................. 823
        Problem uwierzytelniania ....................................................................................... 825
        Podpisywanie certyfikatów ..................................................................................... 827
          ñdania certyfikatu ............................................................................................... 829
     Podpisywanie kodu ..................................................................................................... 830
        Podpisywanie plików JAR ....................................................................................... 830
        Certyfikaty twórców oprogramowania ...................................................................... 835
     Szyfrowanie ............................................................................................................... 837
        Szyfrowanie symetryczne ....................................................................................... 837
        Generowanie klucza .............................................................................................. 839
        Strumienie szyfrujñce ............................................................................................ 843
        Szyfrowanie kluczem publicznym ............................................................................ 844
8        Java. Techniki zaawansowane


    RozdziaÄ 10. Obiekty rozproszone .........................................................................................................851
         Role klienta i serwera ................................................................................................. 852
         Wywoäania zdalnych metod .......................................................................................... 854
            Namiastka i szeregowanie parametrów ................................................................... 854
         Model programowania RMI .......................................................................................... 856
            Interfejsy i implementacje ...................................................................................... 856
            Rejestr RMI .......................................................................................................... 858
            Przygotowanie wdro enia ....................................................................................... 861
            Rejestrowanie aktywno ci RMI ............................................................................... 864
         Parametry zdalnych metod i warto ci zwracane ............................................................. 866
            Przekazywanie obiektów zdalnych ........................................................................... 866
            Przekazywanie obiektów, które nie sñ zdalne ........................................................... 866
            Dynamiczne äadowanie klas ................................................................................... 868
            Zdalne referencje obiektów o wielu interfejsach ....................................................... 873
            Zdalne obiekty i metody equals, hashCode oraz clone ............................................. 874
         Aktywacja zdalnych obiektów ....................................................................................... 874
         Usäugi sieciowe i JAX-WS ............................................................................................. 880
            Stosowanie JAX-WS ............................................................................................... 881
            Klient usäugi Web .................................................................................................. 884
            Usäuga Amazon ..................................................................................................... 886

    RozdziaÄ 11. Skrypty, kompilacja i adnotacje .......................................................................................893
         Skrypty na platformie Java .......................................................................................... 893
             Wybór silnika skryptów .......................................................................................... 894
             Przekierowanie wej cia i wyj cia ............................................................................. 897
             Wywoäywanie funkcji i metod skryptów .................................................................... 898
             Kompilacja skryptu ................................................................................................ 900
             Przykäad: skrypty i graficzny interfejs u ytkownika ..................................................... 901
         Interfejs kompilatora .................................................................................................. 905
             Kompilacja w najprostszy sposób ........................................................................... 906
             Stosowanie zadaþ kompilacji ................................................................................. 906
             Przykäad: dynamiczne tworzenie kodu w jözyku Java ................................................. 911
         Stosowanie adnotacji ................................................................................................. 916
         Przykäad — adnotacje obsäugi zdarzeþ .......................................................................... 918
         Skäadnia adnotacji ...................................................................................................... 922
         Adnotacje standardowe .............................................................................................. 926
             Adnotacje kompilacji ............................................................................................. 927
             Adnotacje zarzñdzania zasobami ............................................................................ 928
             Metaadnotacje ...................................................................................................... 928
         Przetwarzanie adnotacji w kodzie ródäowym ................................................................. 931
         In ynieria kodu bajtowego ........................................................................................... 937
             Modyfikacja kodu bajtowego podczas äadowania ...................................................... 943

    RozdziaÄ 12. Metody macierzyste .........................................................................................................947
         Wywoäania funkcji jözyka C z programów w jözyku Java .................................................. 948
         Numeryczne parametry metod i warto ci zwracane ........................................................ 954
            Wykorzystanie funkcji printf do formatowania liczb ................................................... 955
         ãaþcuchy znaków jako parametry ................................................................................. 956
         Dostöp do skäadowych obiektu .................................................................................... 961
            Dostöp do pól instancji .......................................................................................... 962
            Dostöp do pól statycznych ..................................................................................... 965
Spis treÊci               9

     Sygnatury .................................................................................................................. 966
     Wywoäywanie metod jözyka Java .................................................................................. 967
         Wywoäywanie metod obiektów ................................................................................ 968
         Wywoäywanie metod statycznych ............................................................................. 972
         Konstruktory ......................................................................................................... 973
         Alternatywne sposoby wywoäywania metod .............................................................. 973
     Tablice ...................................................................................................................... 975
     Obsäuga bäödów ......................................................................................................... 978
     Interfejs programowy wywoäaþ jözyka Java .................................................................... 983
     Kompletny przykäad: dostöp do rejestru systemu Windows ............................................. 988
         Rejestr systemu Windows ...................................................................................... 988
         Interfejs dostöpu do rejestru na platformie Java ...................................................... 990
         Implementacja dostöpu do rejestru za pomocñ metod macierzystych ........................ 990

Skorowidz ............................................................................................................................................1005
1
                       Strumienie i pliki
   W tym rozdziale:
     Q   strumienie,
     Q   strumienie tekstowe,
     Q   odczyt i zapis danych binarnych,
     Q   strumienie plików ZIP,
     Q   strumienie obiektów i serializacja,
     Q   zarz dzanie plikami,
     Q   ulepszona obsáuga wej cia i wyj cia,
     Q   wyra enia regularne.

   W tym rozdziale omówimy metody obsáugi plików i katalogów, a tak e metody zapisywa-
   nia do i wczytywania informacji z plików w formacie tekstowym i binarnym. W rozdziale
   przedstawiony jest równie mechanizm serializacji obiektów, który umo liwia przechowy-
   wanie obiektów z tak áatwo ci , z jak przechowujesz tekst i dane numeryczne. Nast pnie
   omówimy szereg ulepsze , które do obsáugi wej cia i wyj cia wprowadziá pakiet java.nio
   udost pniony w wersji Java SE 1.4. Rozdziaá zako czymy przedstawieniem problematyki
   wyra e regularnych, mimo e nie jest ona bezpo rednio zwi zana ze strumieniami i pli-
   kami. Nie potrafili my jednak znale ü dla niej lepszego miejsca w ksi ce. W naszym wybo-
   rze nie byli my zreszt osamotnieni, poniewa zespóá Javy doá czyá specyfikacj interfejsów
   programowych zwi zanych z przetwarzaniem wyra e regularnych do specyfikacji ulepszo-
   nej obsáugi wej cia i wyj cia w Java SE 1.4.



Strumienie
   W j zyku Java obiekt, z którego mo emy odczytaü sekwencj bajtów, nazywamy strumieniem
   wej cia. Obiekt, do którego mo emy zapisaü sekwencj bajtów, nazywamy strumieniem
   wyj cia. ródáem b d celem tych sekwencji bajtów mog byü, i cz sto wáa nie s , pliki,
18   Java. Techniki zaawansowane


     ale tak e i poá czenia sieciowe, a nawet bloki pami ci. Klasy abstrakcyjne InputStream i Out
     ´putStream stanowi baz hierarchii klas opisuj cych wej cie i wyj cie programów Java.

     Poniewa strumienie binarne nie s zbyt wygodne do manipulacji danymi przechowywanymi
     w standardzie Unicode (przypomnijmy tutaj, e Unicode opisuje ka dy znak za pomoc dwóch
     bajtów), stworzono osobn hierarchi klas operuj cych na znakach Unicode i dziedzicz cych
     po klasach abstrakcyjnych Reader i Writer. Klasy te s przystosowane do wykonywania ope-
     racji odczytu i zapisu, opartych na dwubajtowych znakach Unicode, nie przydaj si nato-
     miast do znaków jednobajtowych.


Odczyt i zapis bajtów
     Klasa InputStream posiada metod abstrakcyjn :
        abstract int read()

     Metoda ta wczytuje jeden bajt i zwraca jego warto ü lub –1, je eli natrafi na koniec ródáa
     danych. Projektanci konkretnych klas strumieni wej cia przeáadowuj t metod , dostarcza-
     j c w ten sposób u ytecznej funkcjonalno ci. Dla przykáadu, w klasie FileInputStream metoda
     read czyta jeden bajt z pliku. System.in to predefiniowany obiekt klasy pochodnej od Input
     ´Stream, pozwalaj cy pobieraü informacje z klawiatury.

     Klasa InputStream posiada równie nieabstrakcyjn metod pozwalaj c pobraü lub zigno-
     rowaü tablic bajtów. Metody te wywoáuj abstrakcyjna metod read, tak wi c podklasy
     musz przeáadowaü tylko t jedn metod .

     Analogicznie, klasa OutputStream definiuje metod abstrakcyjn
        abstract void write(int b)

     która wysyáa jeden bajt do aktualnego wyj cia.

     Metody read i write potrafi zablokowaü w tek, dopóki dany bajt nie zostanie wczytany lub
     zapisany. Oznacza to, e je eli strumie nie mo e natychmiastowo wczytaü lub zapisaü danego
     bajta (zazwyczaj z powodu powolnego poá czenia sieciowego), Java zawiesza w tek doko-
     nuj cy wywoáania. Dzi ki temu inne w tki mog wykorzystaü czas procesora, w którym
     wywoáana metoda czeka na udost pnienie strumienia.

     Metoda available pozwala sprawdziü liczb bajtów, które w danym momencie odczytaü.
     Oznacza to, e poni szy kod prawdopodobnie nigdy nie zostanie zablokowany:
        int bytesAvaible = System.in.available();
        if (bytesAvaible > 0)
        {
          byte[] dane = new byte[bytesAvaible];
          System.in.read(data);
        }

     Gdy sko czymy odczytywaü albo zapisywaü dane do strumienia, zamykamy go, wywoáuj c
     metod close. Metoda ta uwalnia zasoby systemu operacyjnego, do tej pory udost pnione
     w tkowi. Je eli aplikacja otworzy zbyt wiele strumieni, nie zamykaj c ich, zasoby systemu
RozdziaÄ 1.   Q   Strumienie i pliki   19


mog zostaü naruszone. Co wi cej, zamkni cie strumienia wyj cia powoduje opró nienie bufora
u ywanego przez ten strumie — wszystkie znaki, przechowywane tymczasowo w buforze,
aby mogáy zostaü zapisane w jednym wi kszym pakiecie, zostan natychmiast wysáane. Je eli
nie zamkniemy strumienia, ostatni pakiet bajtów mo e nigdy nie dotrzeü do odbiorcy. Bufor
mo emy równie opró niü wáasnor cznie, przy u yciu metody flush.

Mimo i klasy strumieni udost pniaj konkretne metody wykorzystuj ce funkcje read i write,
programi ci Javy rzadko z nich korzystaj , poniewa niecz sto si zdarza, eby programy
musiaáy czytaü i zapisywaü sekwencje bajtów. Dane, którymi jeste my zwykle bardziej zain-
teresowani, to liczby, áa cuchy znaków i obiekty.

Java udost pnia wiele klas strumieni pochodz cych od podstawowych klas InputStream
i OutputStream, które pozwalaj operowaü na danych w sposób bardziej dogodny ani eli
w przypadku pracy na poziomie pojedynczych bajtów.

   java.io.InputStream 1.0

  Q   abstract int read()
      pobiera jeden bajt i zwraca jego warto ü. Metoda read zwraca –1, gdy natrafi
      na koniec strumienia.
  Q   int read(byte[] b)
      wczytuje dane do tablicy i zwraca liczb wczytanych bajtów, a je eli natrafi
      na koniec strumienia, zwraca –1. Metoda read czyta co najwy ej b.length bajtów.
  Q   int read(byte[] b, int off, int len)
      wczytuje dane do tablicy bajtów. Zwraca liczb wczytanych bajtów, a je eli natrafi
      na koniec strumienia, zwraca –1.
      Parametry: b                 tablica, w której zapisywane s dane.
                    off            indeks tablicy b, pod którym powinien zostaü
                                   umieszczony pierwszy wczytany bajt.
                    len            maksymalna liczba wczytywanych bajtów.
  Q   long skip(long n)
      ignoruje n bajtów w strumieniu wej cia. Zwraca faktyczn liczb zignorowanych
      bajtów (która mo e byü mniejsza ni n, je eli natrafimy na koniec strumienia).
  Q   int available()
      zwraca liczb bajtów dost pnych bez konieczno ci zablokowania w tku (pami tajmy,
       e zablokowanie oznacza, e wykonanie aktualnego w tku zostaje wstrzymane).
  Q   void close()
      zamyka strumie wej cia.
  Q   void mark(int readlimit)
      ustawia znacznik na aktualnej pozycji strumienia wej cia (nie wszystkie strumienie
      obsáuguj t mo liwo ü). Je eli ze strumienia zostaáo pobranych wi cej ni readlimit
      bajtów, strumie ma prawo usun ü znacznik.
20   Java. Techniki zaawansowane


       Q   void reset()
           wraca do ostatniego znacznika. Pó niejsze wywoáania read b d powtórnie czytaü
           pobrane ju bajty. Je eli znacznik nie istnieje, strumie nie zostanie zresetowany.
       Q   boolean markSupported()
           zwraca true, je eli strumie obsáuguje znaczniki.

        java.io.OutputStream 1.0

       Q   abstract void write(int n)
           zapisuje jeden bajt.
       Q   void write(byte[] b)
       Q   void write(byte[] b, int off, int len)
           zapisuj wszystkie bajty tablicy b lub pewien ich zakres.
           Parametry: b                 tablica, z której pobierane s dane.
                        off             indeks tablicy b, spod którego powinien zostaü
                                        pobrany pierwszy zapisywany bajt.
                        len             liczba zapisywanych bajtów.
       Q   void close()
           opró nia i zamyka strumie wyj cia.
       Q   void flush()
           opró nia strumie wyj cia, czyli wysyáa do odbiorcy wszystkie dane znajduj ce
           si w buforze.


Zoo peÄne strumieni
     W przeciwie stwie do j zyka C, który w zupeáno ci zadowala si jednym typem FILE*,
     Java posiada istne zoo ponad 60 (!) ró nych typów strumieni (patrz rysunki 1.1 i 1.2).

     Podzielmy gatunki nale ce do zoo klas strumieni zale nie od ich przeznaczenia. Istniej
     osobne hierarchie klas przetwarzaj cych bajty i znaki.

     Jak ju o tym wspomnieli my, klasy InputStream i OutputStream pozwalaj pobieraü i wysy-
     áaü jedynie pojedyncze bajty oraz tablice bajtów. Klasy te stanowi baz hierarchii pokazanej
     na rysunku 1.1. Do odczytu i zapisu liczb i áa cuchów znakowych u ywamy ich podklas.
     Na przykáad, DataInputStream i DataOutputStream pozwalaj wczytywaü i zapisywaü wszyst-
     kie podstawowe typy Javy w postaci binarnej. Istnieje wiele po ytecznych klas strumieni,
     na przykáad ZipInputStream i ZipOutputStream pozwalaj ce odczytywaü i zapisywaü dane
     w plikach skompresowanych w formacie ZIP.

     Z drugiej strony, o czym ju wspominali my, do obsáugi tekstu Unicode u ywamy klas pocho-
     dz cych od klas abstrakcyjnych Reader i Writer (patrz rysunek 1.2) Podstawowe metody
     klas Reader i Writer s podobne do tych nale cych do InputStream i OutputStream.
RozdziaÄ 1.   Q   Strumienie i pliki   21




Rysunek 1.1. Hierarchia strumieni wej cia i wyj cia

           abstract int read()
           abstract void write(int b)

       Metoda read zwraca albo kod znaku Unicode (jako liczb z przedziaáu od 0 do 65535), albo –1,
       je eli natrafi na koniec pliku. Metoda write jest wywoáywana dla podanego kodu znaku
       Unicode (wi cej informacji na temat kodów Unicode znjadziesz w rozdziale 3. ksi ki
       Java 2. Podstawy).
22     Java. Techniki zaawansowane




Rysunek 1.2. Hierarchia klas Reader i Writer

       Pocz wszy od Java SE 5.0, wprowadzono cztery dodatkowe interfejsy: Closeable, Flushable,
       Readable i Appendable (patrz rysunek 1.3). Pierwsze dwa z nich s wyj tkowo proste i zawie-
       raj odpowiednio metody:
           void close() throws IOException

       i
           void flush()

       Klasy InputStream, OutputStream, Reader i Writer implementuj interfejs Closeable. Klasy
       OutputStream i Writer implementuj interfejs Flushable.

       Interfejs Readable ma tylko jedn metod
           int read(CharBuffer cb)

       Klasa CharBuffer ma metody do sekwencyjnego oraz swobodnego odczytu i zapisu. Repre-
       zentuje ona bufor w pami ci lub map pliku w pami ci. (Patrz punkt „Struktura bufora da-
       nych” na stronie 89).

       Interfejs Appendable ma dwie metody umo lwiaj ce dopisywanie pojedynczego znaku b d
       sekwencji znaków:
           Appendable append(char c)
           Appendable append(CharSequence s)
RozdziaÄ 1.   Q   Strumienie i pliki   23




Rysunek 1.3. Interfejsy Closeable, Flushable, Readable i Appendable

       Interfejs CharSequence opisuje podstawowe wáa ciwo ci sekwencji warto ci typu char. Inter-
       fejs ten implementuj klasy String, CharBuffer, StringBuilder i StringBuffer.

       Spo ród klas strumieni jedynie klasa Writer implementuje interfejs Appendable.

           java.io.Closeable 5.0

          Q   void close()
              zamyka obiekt implemetuj cy interfejs Closeable. Mo e wyrzuciü wyj tek
              IOException.


           java.io.Flushable 5.0

          Q   void flush()
              opró nia bufor danych zwi zany z obiektem implementuj cym interfejs Flushable.

           java.lang.Readable 5.0

          Q   int read(CharBuffer cb)
              próbuje wczytaü tyle warto ci typu char, ile mo e pomie ciü cb. Zwraca liczb
              wczytanych warto ci lub -1, je li obiekt Readable nie ma ju warto ci do pobrania.

           java.lang.Appendable 5.0

          Q   Appendable append(char c)
24   Java. Techniki zaawansowane


       Q   Appendable append(CharSequence cs)
           dopisuje podany kod znaku lub wszystkie kody podanej sekwencji do obiektu
           Appendable; zwraca this.


        java.lang.CharSequence 1.4

       Q   char charAt(int index)
           zwraca kod o podanym indeksie.
       Q   int length()
           zwraca liczb kodów w sekwencji.
       Q   CharSequence subSequence(int startIndex, int endIndex)
           zwraca sekwencj CharSequence záo on z kodów od startIndex do endIndex - 1.
       Q   String toString()
           zwraca áa cuch znaków skáadaj cy si z kodów danej sekwencji.


þczenie filtrów strumieni
     Klasy FileInputStream i FileOutputStream obsáuguj strumienie wej cia i wyj cia przypo-
     rz dkowane okre lonemu plikowi na dysku. W konstruktorze tych klas podajemy nazw pliku
     lub peán cie k dost pu do niego. Na przykáad
        FileInputStream fin = new FileInputStream("employee.dat");

     spróbuje odszukaü w aktualnym katalogu plik o nazwie employee.dat.

              Poniewa wszystkie klasy w java.io uznajñ relatywne cie ki dostöpu za rozpoczy-
              najñce siö od aktualnego katalogu roboczego, powiniene wiedzieè, co to za kata-
        log. Mo esz pobraè tö informacjö poleceniem System.getProperty("user.dir").

     Tak jak klasy abstrakcyjne InputStream i OutputStream, powy sze klasy obsáuguj odczyt
     i zapis plików na poziomie pojedynczego bajta. Oznacza to, e z obiektu fin mo emy czytaü
     wyá cznie pojedyncze bajty oraz tablice bajtów.
        byte b = (byte)fin.read();

     W nast pnym podrozdziale przekonamy si , e korzystaj c z DataInputStream, mogliby my
     wczytywaü typy liczbowe:
        DataInputStream din = . . .;
        double p = din.readDouble();

     Ale tak jak FileInputStream nie posiada metod czytaj cych typy liczbowe, tak DataInput
     ´Stream nie posiada metody pozwalaj cej czytaü dane z pliku.

     Java korzysta ze sprytnego mechanizmu rozdzielaj cego te dwa rodzaje funkcjonalno ci.
     Niektóre strumienie (takie jak FileInputStream i strumie wej cia zwracany przez metod
     openStream klasy URL) mog udost pniaü bajty z plików i innych, bardziej egzotycznych loka-
RozdziaÄ 1.   Q   Strumienie i pliki   25


lizacji. Inne strumienie (takie jak DataInputStream i PrintWriter) potrafi tworzyü z bajtów
reprezentacj bardziej u ytecznych typów danych. Programista Javy musi poá czyü te dwa
mechanizmy w jeden. Dla przykáadu, aby wczytywaü liczby z pliku, powinien utworzyü
obiekt typu FileInputStream, a nast pnie przekazaü go konstruktorowi DataInputStream.
   FileInputStream fin = new FileInputStream("employee.dat");
   DataInputStream din = new DataInputStream(fin);
   double s = din.readDouble();

Wróümy do rysunku 1.1, gdzie przedstawione s klasy FilterInputStream i FilterOutput
´Stream. Ich podklasy mo emy wykorzystaü do rozbudowy obsáugi strumieni zwykáych
bajtów.

Ró ne funkcjonalno ci mo emy dodawaü poprzez zagnie d anie filtrów. Na przykáad —
domy lnie strumienie nie s buforowane. Wobec tego ka de wywoáanie metody read oznacza
odwoáanie si do usáug systemu operacyjnego, który odczytuje kolejny bajt. Du o efektyw-
niej b dzie daü od systemu operacyjnego caáych bloków danych i umieszczaü je w buforze.
Je li chcemy uzyskaü buforowany dost p do pliku, musimy skorzystaü z poni szej, mon-
strualnej sekwencji konstruktorów:
   DataInputStream din = new DataInputStream
    (new BufferedInputStream
     (new FileInputStream("employee.dat")));

Zwróümy uwag , e DataInputStream znalazá si na ostatnim miejscu w áa cuchu konstrukto-
rów, poniewa chcemy u ywaü metod klasy DataInputStream i chcemy, aby korzystaáy one
z buforowanej metody read.

Czasami b dziemy zmuszeni utrzymywaü á czno ü ze strumieniami znajduj cymi si po rodku
áa cucha. Dla przykáadu, czytaj c dane, musimy cz sto podejrzeü nast pny bajt, aby spraw-
dziü, czy jego warto ü zgadza si z naszymi oczekiwaniami. W tym celu Java dostarcza klas
PushbackInputStream.
   PushbackInputStream pbin = new PushbackInputStream
    (new BufferedInputStream
     (new FileInputStream("employee.dat")));

Teraz mo emy odczytaü warto ü nast pnego bajta:
   int b = pbin.read();

i umie ciü go z powrotem w strumieniu, je eli jego warto ü nie odpowiada naszym oczeki-
waniom.
   if (b != '<') pbin.unread(b);

Ale wczytywanie i powtórne wstawianie to jedyne metody obsáugiwane przez klas Push
´back-InputStream. Je eli chcemy podejrzeü kolejne bajty, a tak e wczytywaü liczby, potrze-
bujemy referencji zarówno do PushbackInputStream, jak i do DataInputStream.
   DataInputStream din = DataInputStream
    (pbin = new PushbackInputStream
     (new BufferedInputStream
      (new FileInputStream("employee.dat"))));
26     Java. Techniki zaawansowane


       Oczywi cie, w bibliotekach strumieni innych j zyków programowania takie udogodnienia jak
       buforowanie i kontrolowanie kolejnych bajtów s wykonywane automatycznie, wi c koniecz-
       no ü tworzenia ich kombinacji w j zyku Java wydaje si niepotrzebnym zawracaniem gáowy.
       Jednak mo liwo ü á czenia klas filtrów i tworzenia w ten sposób naprawd u ytecznych
       sekwencji strumieni daje nam niespotykan elastyczno ü. Na przykáad, korzystaj c z poni -
       szej sekwencji strumieni, mo emy wczytywaü liczby ze skompresowanego pliku ZIP
       (patrz rysunek 1.4).
          ZipInputStream zin
           = new ZipInputStream(new FileInputStream("employee.zip"));
          DataInputStream din = new DataInputStream(zin);

Rysunek 1.4.
Sekwencja
filtrowanych
strumieni




       Aby dowiedzieü si wi cej o obsáudze formatu ZIP, zajrzyj do podrozdziaáu po wi conego
       strumieniom plików ZIP na stronie 48.

           java.io.FileInputStream 1.0

          Q    FileInputStream(String name)
               tworzy nowy obiekt typu FileInputStream, u ywaj c pliku, którego cie ka dost pu
               znajduje si w áa cuchu nazwa.
          Q    FileInputStream(File file)
               tworzy nowy obiekt typu FileInputStream, u ywaj c pliku, którego cie k dost pu
               zawiera parametr name, lub u ywaj c informacji zawartych w obiekcie file (klasa
               File zostanie omówiona pod koniec tego rozdziaáu). cie ki dost pu s podawane
               wzgl dem katalogu roboczego skonfigurowanego podczas uruchamiania maszyny
               wirtualnej Java.

           java.io.FileOutputStream 1.0

          Q    FileOutputStream(String name)
          Q    FileOutputStream(String name, boolean append)
          Q    FileOutputStream(File file)
               tworzy nowy obiekt typu
RozdziaÄ 1.   Q   Strumienie i pliki   27


     Q   FileOutputStream(File file, boolean append)
         tworzy nowy strumie wyj ciowy pliku okre lonego za pomoc áa cucha file
         lub obiektu file (klasa File zostanie omówiona pod koniec tego rozdziaáu). Je eli
         parametr append ma warto ü true, dane doá czane s na ko cu pliku, a istniej cy
         plik o tej samej nazwie nie zostanie skasowany. W przeciwnym razie istniej cy
         plik o tej samej nazwie zostanie skasowany.

      java.io.BufferedInputStream 1.0

     Q   BufferedInputStream(InputStream in)
         tworzy nowy obiekt typu BufferedInputStream, o domy lnym rozmiarze bufora.
         Strumie buforowany wczytuje znaki ze strumienia danych, nie wymuszaj c
         za ka dym razem dost pu do urz dzenia. Gdy bufor zostanie opró niony, system
         prze le do niego nowy blok danych.

      java.io.BufferedOutputStream 1.0

     Q   BufferedOutputStream(OutputStream out)
         tworzy nowy obiekt typu Buffered-OutputStream, o domy lnym rozmiarze bufora.
         Strumie umieszcza w buforze znaki, które powinny zostaü zapisane, nie wymuszaj c
         za ka dym razem dost pu do urz dzenia. Gdy bufor zapeáni si lub gdy strumie
         zostanie opró niony, dane s przesyáane odbiorcy.

      java.io.PushbackInputStream 1.0

     Q   PushbackInputStream(InputStream in)
         tworzy strumie sprawdzaj cy warto ü nast pnego w kolejce bajta.
     Q   PushbackInputStream(InputStream we, int size)
         tworz strumie umo liwiaj cy podgl d kolejnego bajta wraz z buforem o podanym
         rozmiarze.
     Q   void unread(int b)
         wstawia bajt z powrotem do strumienia, dzi ki czemu przy nast pnym wywoáaniu
         read zostanie on ponownie odczytany.
         Parametry: b               zwracany bajt



Strumienie tekstowe
   Zapisuj c dane, mo emy wybieraü pomi dzy formatem binarnym i tekstowym. Dla przykáadu:
   je eli liczba caákowita 1234 zostanie zapisana w postaci binarnej, w pliku pojawi si sekwencja
   bajtów 00 00 04 D2 (w notacji szesnastkowej). W formacie tekstowym liczba ta zostanie
   zapisana jako áa cuch "1234". Mimo i zapis danych w postaci binarnej jest szybki i efektywny,
   to uzyskany wynik jest kompletnie nieczytelny dla ludzi. W poni szym podrozdziale skoncen-
   trujemy si na tekstowym wej ciu-wyj ciu.
28   Java. Techniki zaawansowane


     Zapisuj c áa cuchy znakowe, musimy uwzgl dniü sposób kodowania znaków. W przypad-
     ku kodowania UTF-16 áa cuch "1234" zostanie zakodowany jako 00 31 00 32 00 33 00 34
     (w notacji szesnastkowej). Jednak e obecnie wi kszo ü rodowisk, w których uruchamiamy
     programy w j zyku Java, u ywa swojego wáasnego formatu tekstu. W kodzie ISO 8859-1,
     najcz ciej stosowanym w USA i Europie Zachodniej, nasz przykáadowy áa cuch zostanie
     zapisany jako 31 32 33 34, bez bajtów o warto ci zero.

     Klasa OutputStreamWriter zamienia strumie znaków Unicode na strumie bajtów, stosuj c
     odpowiednie kodowanie znaków. Natomiast klasa InputStreamReader zamienia strumie
     wej cia, zawieraj cy bajty (reprezentuj ce znaki za pomoc okre lonego kodowania), na obiekt
     udost pniaj cy znaki Unicode.

     Poni ej przedstawiamy sposób utworzenia obiektu wej cia, wczytuj cego znaki z konsoli
     i automatycznie konwertuj cego je na Unicode.
        InputStreamReader in = new InputStreamReader(System.in);

     Obiekt wej cia korzysta z domy lnego kodowania lokalnego systemu, na przykáad ISO 8859-1
     Mo emy wybraü inny sposób kodowania, podaj c jego nazw w konstruktorze InputStream
     ´Reader, na przykáad:
        InputStreamReader in = new InputStreamReader(new FileInputStream("kremlin.dat"),
        ´"ISO8859_5");

     Wi cej informacji na temat kodowania znaków znajdziesz w punkcie „Zbiory znaków” na
     stronie 35.

     Poniewa obiekty tekstowego wej cia i wyj cia s tak cz sto doá czane do plików, Java dostar-
     cza w tym celu dwie wygodne klasy: FileReader i FileWriter. Na przykáad instrukcja
        FileWriter out = new FileWriter("output.txt");

     jest równoznaczna z
        FileWriter out = new FileWriter(new FileOutputStream("output.txt");



Zapisywanie tekstu
     W celu zapisania tekstu korzystamy z klasy PrintWriter. Dysponuje ona metodami umo -
     liwiaj cymi zapis áa cuchów i liczb w formacie tekstowym. Dla wygody programistów ma
     ona konstruktor umo liwiaj cy poá czenie obiektu klasy PrintWriter z FileWriter. Zatem
     instrukcja
        PrintWriter out = new PrintWriter("employee.txt");

     stanowi odpowiednik instrukcji
        PrintWriter out = new PrintWriter(new FileWriter("employee.txt"));

     Do zapisywania danych za pomoc obiektu klasy PrintWriter u ywamy tych samych metod
     print i println, których u ywali my dot d z obiektem System.out. Mo emy wykorzystywaü
     je do zapisu liczb (int, short, long, float, double), znaków, warto ci logicznych, áa cuchów
     znakowych i obiektów.
RozdziaÄ 1.   Q   Strumienie i pliki   29


Spójrzmy na poni szy kod:
   String name = "Harry Hacker";
   double salary = 75000;
   out.print(name);
   out.print(' ');
   out.println(salary);

Rezultatem jego wykonania b dzie wysáanie napisu
   Harry Hacker 75000.0

do strumienia out. Nast pnie znaki zostan skonwertowane na bajty i zapisane w pliku
employee.txt.

Metoda println automatycznie dodaje znak ko ca wiersza, odpowiedni dla danego systemu
operacyjnego ("rn" w systemie Windows, "n" w Unix). Znak ko ca wiersza mo emy
pobraü, stosuj c wywoáanie System.getProperty("line.separator").

Je eli obiekt zapisu znajduje si w trybie automatycznego opró niania, w chwili wywoáania
metody println wszystkie znaki w buforze zostan wysáane do odbiorcy (obiekty PrintWriter
zawsze s buforowane). Domy lnie automatyczne opró nianie jest wyá czone. Automaty-
czne opró nianie mo emy wá czaü i wyá czaü przy u yciu konstruktora PrintWriter(Writer
out, boolean autoFlush:
   PrintWriter out = new PrintWriter(new FileWriter("employee.txt", true);
   // automatyczne opró nianie

Metody print nie wyrzucaj wyj tków. Aby sprawdziü, czy ze strumieniem jest wszystko
w porz dku, wywoáujemy metod checkError.

         Weterani Javy prawdopodobnie zastanawiajñ siö, co siö staäo z klasñ PrintStream
         i obiektem System.out. W jözyku Java 1.0 klasa PrintStream obcinaäa znaki Unicode
   do znaków ASCII, po prostu opuszczajñc górny bajt. Takie rozwiñzanie nie pozwalaäo na
   przenoszenie kodu na inne platformy i w jözyku Java 1.1 zostaäo zastñpione przez kon-
   cepcjö obiektów odczytu i zapisu. Ze wzglödu na konieczno è zachowania zgodno ci
   System.in, System.out i System.err wciñ sñ strumieniami, nie obiektami odczytu
   i zapisu. Ale obecna klasa PrintStream konwertuje znaki Unicode na schemat kodowa-
   nia lokalnego systemu w ten sam sposób, co klasa PrintWriter. Gdy u ywamy metod
   print i println, obiekty PrintStream dziaäajñ tak samo jak obiekty PrintWriter, ale
   w przeciwieþstwie do PrintWriter pozwalajñ wysyäaè bajty za pomocñ metod write(int)
   i write(byte[]).


   java.io.PrintWriter 1.1

  Q   PrintWriter(Writer out)
  Q   PrintWriter(Writer out, boolean autoFlush)
      tworzy nowy obiekt klasy PrintWriter.
      Parametry: out            obiekt zapisu tekstu.
                   autoFlush true oznacza, e metody println b d opró niaü bufor
                                (domy lnie: false).
30   Java. Techniki zaawansowane


       Q   PrintWriter(OutputStream out)
       Q   PrintWriter(OutputStream out, boolean autoFlush)
           tworzy nowy obiekt klasy PrintWriter na podstawie istniej cego obiektu typu
           OutputStream, poprzez utworzenie po rednicz cego obiektu klasy
           OutputStreamWriter.
       Q   PrintWriter(String filename)
       Q   PrintWriter(File file)
           tworzy nowy obiekt klasy PrintWriter zapisuj cy dane do pliku poprzez
           utworzenie po rednicz cego obiektu klasy FileWriter.
       Q   void print(Object obj)
           drukuje áa cuch zwracany przez metod toString danego obiektu.
           Parametry: obj             drukowany obiekt.
       Q   void print(String p)
           drukuje áa cuch Unicode.
       Q   void println(String p)
           drukuje áa cuch zako czony znakiem ko ca wiersza. Je eli automatyczne
           opró nianie jest wá czone, opró nia bufor strumienia.
       Q   void print(char[] p)
           drukuje tablic znaków Unicode.
       Q   void print(char c)
           drukuje znak Unicode.
       Q   void print(int i)
       Q   void print(long l)
       Q   void print(float f)
       Q   void print(double d)
       Q   void print(boolean b)
           drukuje podan warto ü w formacie tekstowym.
       Q   void printf(String format, Object... args)
           drukuje podane warto ci wedáug áa cucha formatuj cego. Specyfikacj áa cucha
           formatuj cego znajdziesz w rozdziale 3. ksi ki Java 2. Podstawy.
       Q   boolean checkError()
           zwraca true, je eli wyst piá bá d formatowania lub zapisu. Je eli w strumieniu
           danych wyst pi bá d, strumie zostanie uznany za niepewny (ang. tainted) i wszystkie
           nast pne wywoáania metody checkError b d zwracaü true.
RozdziaÄ 1.   Q   Strumienie i pliki   31


Wczytywanie tekstu
    Wiemy ju , e:
      Q   aby zapisaü dane w formacie binarnym, u ywamy klasy DataOutputStream;
      Q   aby zapisaü dane w formacie tekstowym, u ywamy klasy PrintWriter.

    Na tej podstawie mo na si domy laü, e istnieje równie klasa analogiczna do DataInput
    ´Stream, która pozwoli nam czytaü dane w formacie tekstowym. Najbli szym odpowiedni-
    kiem jest w tym przypadku klasa Scanner, któr wykorzystywali my intensywnie w ksi ce
    Java 2. Podstawy. Niestety, przed wprowadzeniem Java SE 5.0 mo na byáo u yü w tym celu
    jedynie klasy BufferedReader. Ma ona metod readLine pozwalaj c pobraü wiersz tekstu.
    Aby j wykorzystaü, musimy najpierw poá czyü obiekt typu BufferedReader ze ródáem
    wej cia.
       BufferedReader in = new BufferedReader(new FileReader("employee.txt"));

    Je eli dalsze wczytywanie nie jest mo liwe, metoda readLine zwraca null. Typowa p tla
    pobierania danych wygl da wi c nast puj co:
       String line;
       while ((line = in.readLine()) != null)
       {
         operacje na danych line
       }

    Jednak klasa BufferedReader nie udost pnia metod odczytu danych liczbowych. Dlatego do
    odczytu danych sugerujemy zastosowanie klasy Scanner.


Zapis obiektów w formacie tekstowym
    W tym podrozdziale przeanalizujemy dziaáanie przykáadowego programu, który b dzie zapi-
    sywaü tablic obiektów typu Employee w pliku tekstowym. Dane ka dego obiektu zostan
    zapisane w osobnym wierszu. Warto ci pól skáadowych zostan oddzielone od siebie sepa-
    ratorami. Jako separatora u ywamy pionowej kreski (|) (innym popularnym separatorem jest
    dwukropek (:), zabawa polega na tym, e ka dy programista u ywa innego separatora).
    Naturalnie, taki wybór stawia przed nami pytanie, co b dzie, je li znak | znajdzie si w jednym
    z zapisywanych przez nas áa cuchów?

    Oto przykáadowy zbiór danych obiektów:
       Harry Hacker|35500|1989|10|1
       Carl Cracker|75000|1987|12|15
       Tony Tester|38000|1990|3|15

    Zapis tych rekordów jest prosty. Poniewa korzystamy z pliku tekstowego, u ywamy klasy
    PrintWriter. Po prostu zapisujemy wszystkie pola skáadowe, za ka dym z nich stawiaj c |,
    albo te , po ostatnim polu, n. Operacje te wykona poni sza metoda writeData, któr dodamy
    do klasy Employee.
32   Java. Techniki zaawansowane

        public void writeData(PrintWriter out) throws IOException
        {
          GregorianCalendar calendar = new GregorianCalendar();
          kalendarz.setTime(hireDay);
          out.println(name + "|"
           + salary + "|"
           + calendar.get(Calendar.YEAR) + "|"
           + (calendar.get(Calendar.MONTH) + 1) + "|"
           + calendar.get(Calendar.DAY_OF_MONTH));
        }

     Aby odczytaü te dane, wczytujemy po jednym wierszu tekstu i rozdzielamy pola skáadowe.
     Do wczytania wierszy u yjemy obiektu klasy Scanner, a metoda String.split pozwoli nam
     wyodr bniü poszczególne tokeny.
        public void readData(Scanner in)
        {
           String line = in.nextLine();
           String[] tokens = line.split("|");
           name = tokens[0];
           salary = Double.parseDouble(tokens[1]);
           int y = Integer.parseInt(tokens[2]);
           int m = Integer.parseInt(tokens[3]);
           int d = Integer.parseInt(tokens[4]);
           GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d);
           hireDay = calendar.getTime();
        }

     Parametrem metody split jest wyra enie regularne opisuj ce separator. Wyra enie regularne
     omówimy bardziej szczegóáowo pod koniec bie cego rozdziaáu. Poniewa pionowa kreska
     ma specjalne znaczenie w wyra eniach regularnych, to musimy poprzedziü j znakiem .
     Ten z kolei musimy poprzedziü jeszcze jednym znakiem  — w efekcie uzyskuj c wyra enie
     postaci "|".

     Kompletny program zostaá przedstawiony na listingu 1.1. Metoda statyczna
        void writeData(Employee[] e, PrintWriter out)

     najpierw zapisuje rozmiar tablicy, a nast pnie ka dy z rekordów. Metoda statyczna
        Employee[] readData(BufferedReader in)

     najpierw wczytuje rozmiar tablicy, a nast pnie ka dy z rekordów. Wymaga to zastosowania
     pewnej sztuczki:
        int n = in.nextInt();
        in.nextLine(); // konsumuje znak nowego wiersza
        Employee[] employees = new Employee[n];
        for (int i = 0; i < n; i++)
        {
           employees[i] = new Employee();
           employees[i].readData(in);
        }

     Wywoáanie metody nextInt wczytuje rozmiar tablicy, ale nie nast puj cy po nim znak nowego
     wiersza. Musimy zatem go pobraü (wywoáuj c metod nextLine), aby metoda readData mogáa
     uzyskaü kolejny wiersz.
RozdziaÄ 1.   Q   Strumienie i pliki   33


Listing 1.1. TextFileTest.java
            import java.io.*;
            import java.util.*;

            /**
              * @version 1.12 2007-06-22
              * @author Cay Horstmann
              */
            public class TextFileTest
            {
                 public static void main(String[] args)
                 {
                    Employee[] staff = new Employee[3];

                    staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
                    staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
                    staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);

                    try
                    {
                          // zapisuje wszystkie rekordy pracowników w pliku employee.dat
                          PrintWriter out = new PrintWriter("employee.dat");
                          writeData(staff, out);
                          out.close();

                          // wczytuje wszystkie rekordy do nowej tablicy
                          Scanner in = new Scanner(new FileReader("employee.dat"));
                          Employee[] newStaff = readData(in);
                          in.close();

                          // wy wietla wszystkie wczytane rekordy
                          for (Employee e : newStaff)
                             System.out.println(e);
                    }
                    catch (IOException exception)
                    {
                       exception.printStackTrace();
                    }
                }

                /**
                 * Zapisuje dane wszystkich obiektów klasy Employee
                 * umieszczonych w tablicy
                 * do obiektu klasy PrintWriter
                 * @param employees tablica obiektów klasy Employee
                 * @param out obiekt klasy PrintWriter
                 */
                private static void writeData(Employee[] employees, PrintWriter out) throws
                ´IOException
                {
                    // zapisuje liczbú obiektów
                    out.println(employees.length);

                    for (Employee e : employees)
                       e.writeData(out);
                }
34   Java. Techniki zaawansowane

            /**
             * Wczytuje tablicú obiektów klasy Employee
             * @param in obiekt klasy Scanner
             * @return tablica obiektów klasy Employee
             */
            private static Employee[] readData(Scanner in)
            {
                // pobiera rozmiar tablicy
                int n = in.nextInt();
                in.nextLine(); // pobiera znak nowego wiersza

                Employee[] employees = new Employee[n];
                for (int i = 0; i < n; i++)
                {
                   employees[i] = new Employee();
                   employees[i].readData(in);
                }
                return employees;
            }
        }

        class Employee
        {
           public Employee()
           {
           }

            public Employee(String n, double s, int year, int month, int day)
            {
               name = n;
               salary = s;
               GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
               hireDay = calendar.getTime();
            }

            public String getName()
            {
               return name;
            }

            public double getSalary()
            {
               return salary;
            }

            public Date getHireDay()
            {
               return hireDay;
            }

            public void raiseSalary(double byPercent)
            {
               double raise = salary * byPercent / 100;
               salary += raise;
            }

            public String toString()
RozdziaÄ 1.   Q   Strumienie i pliki   35

           {
               return getClass().getName() + "[name=" + name + ",salary=" + salary +
               ´",hireDay=" + hireDay
                     + "]";
           }

           /**
            * Zapisuje dane obiektu klasy Employee
            * do obiektu klasy PrintWriter
            * @param out obiekt klasy PrintWriter
            */
           public void writeData(PrintWriter out)
           {
               GregorianCalendar calendar = new GregorianCalendar();
               calendar.setTime(hireDay);
               out.println(name + "|" + salary + "|" + calendar.get(Calendar.YEAR) + "|"
                     + (calendar.get(Calendar.MONTH) + 1) + "|" +
                     ´calendar.get(Calendar.DAY_OF_MONTH));
           }

           /**
            * Wczytuje dane obiektu klasy Employee
            * @param in obiekt klasy Scanner
            */
           public void readData(Scanner in)
           {
               String line = in.nextLine();
               String[] tokens = line.split("|");
               name = tokens[0];
               salary = Double.parseDouble(tokens[1]);
               int y = Integer.parseInt(tokens[2]);
               int m = Integer.parseInt(tokens[3]);
               int d = Integer.parseInt(tokens[4]);
               GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d);
               hireDay = calendar.getTime();
           }

           private String name;
           private double salary;
           private Date hireDay;
       }




Zbiory znaków
    We wcze niejszych edycjach platformy Java problem znaków narodowych obsáugiwany byá
    w maáo systematyczny sposób. Sytuacja zmieniáa si z wprowadzeniem pakietu java.nio
    w Java SE 1.4, który unifikuje konwersje zbiorów znaków, udost pniaj c klas Charset
    (zwróümy uwag na maá liter s w nazwie klasy).

    Zbiór znaków stanowi odwzorowanie pomi dzy dwubajtowymi kodami Unicode i sekwen-
    cjami bajtów stosowanymi w lokalnych kodowaniach znaków. Jednym z najpopularniejszych
    kodowa znaków jest ISO-8859-1, które koduje za pomoc jednego bajta pierwszych 256
    znaków z zestawu Unicode. Coraz wi ksze znaczenie zyskuje równie ISO08859-15, w którym
36   Java. Techniki zaawansowane


     zast piono cz ü mniej przydatnych znaków kodu ISO-8859-1 akcentowanymi znakami j zyka
     francuskiego i fi skiego, a przede wszystkim zamiast znaku waluty mi dzynarodowej ¤
     umieszczono symbol euro (€) o kodzie 0xA4. Innymi przykáadami kodowa znaków s ko-
     dowania o zmiennej liczbie bajtów stosowane dla j zyków japo skiego i chi skiego.

     Klasa Charset u ywa nazw zbiorów znaków zgodnie ze standardem okre lonym przez IANA
     Character Set Registry (http://www.iana.org/assignments/character-sets). Nazwy te ró ni si
     nieco od nazw stosowanych w poprzednich wersjach. Na przykáad „oficjaln ” nazw ISO-8859-1
     jest teraz "ISO-8859-1" zamiast "ISO8859_1" preferowan w Java SE 1.3 i wcze niejszych
     wersjach.

             Opis kodowania ISO 8859 znajdziesz na stronie http://aspell.net/charsets/
             iso8859.html.

     Obiekt klasy Charset uzyskujemy, wywoáuj c metod statyczn forName, której podajemy
     oficjaln nazw zbioru znaków lub jeden z jej synonimów:
        Charset cset = Charset.forName("ISO-8859-1");

     Du e i maáe litery nie s rozró niane w nazwach zbiorów znaków.

     Ze wgl du na konieczno ü zachowania zgodno ci z innymi konwencjami nazw nazwa ka dego
     zbioru znaków mo e mieü wiele synonimów. Na przykáad dla ISO-8859-1 istniej nast -
     puj ce synonimy:
        ISO8859-1
        ISO_8859_1
        ISO8859_1
        ISO_8859-1
        ISO_8859-1:1987
        8859_1
        latin1
        l1
        csISOLatin1
        iso-ir-100
        cp819
        IBM819
        IBM-819
        819

     Metoda aliases zwraca obiekt klasy Set zawieraj cy synonimy. Poni ej przedstawiamy kod
     umo liwiaj cy przegl danie synonimów:
        Set<String> aliases = cset.aliases();
        for (String alias : aliases)
           System.out.println(alias);

     Aby dowiedzieü si , które zbiory znaków s dost pne dla konkretnej implementacji, wywo-
     áujemy metod statyczn availableCharsets. Poni szy kod pozwala poznaü nazwy wszyst-
     kich dost pnych zbiorów znaków:
        Map<String, Charset> charsets = Charset.availableCharsets();
        for (String name : charsets.keySet())
           System.out.println(name);
RozdziaÄ 1.   Q   Strumienie i pliki   37


      W tabeli 1.1 zostaáy przedstawione wszystkie kodowania znaków, które musi obsáugiwaü
      ka da implementacja platformy Java. W tabeli 1.2 wymienione zostaáy schematy kodowania
      instalowane domy lnie przez pakiet JDK. Zbiory znaków przedstawione w tabeli 1.3 s insta-
      lowane tylko w przypadku systemów operacyjnych u ywaj cych j zyków innych ni euro-
      pejskie.

Tabela 1.1. Kodowania znaków wymagane na platformie Java

 Standardowa nazwa
                     Nazwa tradycyjna         Opis
 obiektu Charset
 US-ASCII             ASCII                   American Standard Code for Information Exchange
 ISO-8859-1           ISO8859_1               ISO 8859-1, alfabet Latin 1
 UTF-8                UTF8                    8-bitowy Unicode Transformation Format
 UTF-16               UTF-16                  16-bitowy Unicode Transformation Format, porz dek
                                              bajtów okre lony przez opcjonalny znacznik
 UTF-16BE             UnicodeBigUnmarked      16-bitowy Unicode Transformation Format, porz dek
                                              bajtów od najstarszego
 UTF-16LE             UnicodeLittleUnmarked   16-bitowy Unicode Transformation Format, porz dek
                                              bajtów od najmáodszego

Tabela 1.2. Podstawowe kodowania znaków

 Standardowa nazwa
                     Nazwa tradycyjna         Opis
 obiektu Charset
 ISO8859-2            ISO8859_2               ISO 8859-2, alfabet Latin 2
 ISO8859-4            ISO8859_4               ISO 8859-4, alfabet Latin 4
 ISO8859-5            ISO8859_5               ISO 8859-5, alfabet Latin/Cyrillic
 ISO8859-7            ISO8859_7               ISO 8859-7, alfabet Latin/Greek
 ISO8859-9            ISO8859_9               ISO 8859-9, alfabet Latin 5
 ISO8859-13           ISO8859_13              ISO 8859-13, alfabet Latin 7
 ISO8859-15           ISO8859_15              ISO 8859-15, alfabet Latin 9
 windows-1250         Cp1250                  Windows, wschodnioeuropejski
 windows-1251        Cp1251                   Windows Cyrillic
 windows-1252         Cp1252                  Windows, Latin 1
 windows-1253         Cp1253                  Windows, grecki
 windows-1254         Cp1254                  Windows, turecki
 windows-1257        Cp1257                   Windows, baátycki
38      Java. Techniki zaawansowane


Tabela 1.3. Rozszerzone kodowania znaków

 Standardowa nazwa
                      Nazwa tradycyjna       Opis
 obiektu Charset
 Big5                 Big5                   Big5, tradycyjny chi ski
 Big5-HKSCS           Big5_HKSCS             Big5, tradycyjny chi ski z rozszerzeniami Hongkong
 EUC-JP               EUC_JP                 JIS X 0201, 0208, 0212, kodowanie EUC, japo ski
 EUC-KR               EUC_KR                 KS C 5601, kodowanie EUC, korea ski
 GB18030              GB18030                uproszczony chi ski, standard PRC
 GBK                  GBK                    GBK, uproszczony chi ski
 ISCII91              ISCII91                ISCII91, indu
 ISO-2022-JP          ISO2022JP              JIS X 0201, 0208 w postaci ISO 2022, japo ski
 ISO-2022-KR          ISO2022KR              ISO 2022 KR, korea ski
 ISO8859-3            ISO8859_3              ISO 8859-3, Latin 3
 ISO8859-6            ISO8859_6              ISO 8859-6, alfabet áaci ski/arabski
 ISO8859-8            ISO8859_8              ISO 8859-8, alfabet áaci ski/hebrajski
 Shift_JIS            SJIS                   Shift_JIS, japo ski
 TIS-620              TIS620                 TIS620, tajski
 windows-1255         Cp1255                 Windows, hebrajski
 windows-1256         Cp1256                 Windows, arabski
 windows-1258         Cp1258                 Windows, wietnamski
 windows-3lj          MS392                  Windows, japo ski
 x-EUC-CN             EUC_CN                 GB2313, kodowanie EUC, uproszczony chi ski
 x-EUC-JP-LINUX       EUC_JP_LINUX           JIS X 0201, 0208, kodowanie EUC, japo ski
 x-EUC-TW             EUC_TW                 CNS11643 (Plane 1-3), kodowanie EUC, tradycyjny
                                             chi ski
 x-MS950-HKSCS        MS950_HKSCS            Windows, tradycyjny chi ski z rozszerzeniami
                                             Hongkong
 x-mswin-936          MS936                  Windows, uproszczony chi ski
 x-windows-949        MS949                  Windows, korea ski
 x-windows-950        MS950                  Windows, tradycyjny chi ski

        Lokalne schematy kodowania nie mog oczywi cie reprezentowaü wszystkich znaków Unicode.
        Je li znak nie jest reprezentowany, to zostaje przeksztaácony na znak ?.

        Dysponuj c zbiorem znaków, mo emy u yü go do konwersji áa cuchów Unicode i sekwencji
        bajtów. Oto przykáad kodowania áa cucha Unicode:
RozdziaÄ 1.   Q   Strumienie i pliki   39

   String str = . . .;
   ByteBuffer buffer = cset.encode(str);
   byte[] bytes = buffer.array();

Natomiast aby dokonaü konwersji w kiedunku przeciwnym, potrzebny b dzie bufor. Wykorzy-
stamy metod statyczn wrap tablicy ByteBuffer, aby przeksztaáciü tablic bajtów w bufor.
W wyniku dziaáania metody decode otrzymujemy obiekt klasy CharBuffer. Wystarczy wywo-
áaü jego metod toString, aby uzyskaü áa cuch znaków.
   byte[] bytes = . . .;
   ByteBuffer bbuf = ByteBuffer.wrap(bytes, offset, length);
   CharBuffer cbuf = cset.decode(bbuf);
   String str = cbuf.toString();


   java.nio.charset.Charset 1.4

  Q   static SortedMap availableCharsets()
      pobiera wszystkie zbiory znaków dost pne dla maszyny wirtualnej. Zwraca map ,
      której kluczami s nazwy zbiorów znaków, a warto ciami same zbiory.
  Q   static Charset forName(String name)
      zwraca zbiór znaków o podanej nazwie.
  Q   Set aliases()
      zwraca zbiór synonimów nazwy danego zbioru znaków.
  Q   ByteBuffer encode(String str)
      dokonuje konwersji podanego áa cucha na sekwencj bajtów.
  Q   CharBuffer decode(ByteBuffer buffer)
      dokonuje konwersji sekwencji bajtów. Nierozpoznane bajty s zamieniane
      na specjalny znak Unicode ('uFFFD').

   java.nio.ByteBuffer 1.4

  Q   byte[] array()
      zwraca tablic bajtów, któr zarz dza ten bufor.
  Q   static ByteBuffer wrap(byte[] bytes)
  Q   static ByteBuffer wrap(byte[] bytes, int offset, int length)
      zwraca bufor, który zarz dza podan tablic bajtów lub jej okre lonym zakresem.

   java.nio.CharBuffer

  Q   char[] array()
      zwraca tablic kodów, któr zarz dza ten bufor.
  Q   char charAt(int index)
      zwraca kod o podanym indeksie.
40   Java. Techniki zaawansowane


       Q   String toString()
           zwraca áa cuch, który tworz kody zarz dzane przez ten bufor.



Odczyt i zapis danych binarnych
     Aby zapisaü liczb , znak, warto ü logiczn lub áa cuch, korzystamy z jednej z poni szych
     metod interfejsu DataOutput:
        writeChars
        writeByte
        writeInt
        writeShort
        writeLong
        writeFloat
        writeDouble
        writeChar
        writeBoolean
        writeUTF

     Na przykáad, writeInt zawsze zapisuje liczb integer jako warto ü czterobajtow , niezale nie
     od liczby jej cyfr, a writeDouble zawsze zapisuje liczby double jako warto ci o miobajtowe.
     Rezultat tych dziaáa nie jest czytelny dla czáowieka, ale poniewa wymagana ilo ü bajtów
     jest taka sama dla ka dej warto ci danego typu, to wczytanie ich z powrotem b dzie szybsze
     ni parsowanie zapisu tekstowego.


             Zale nie od platformy u ytkownika, liczby caäkowite i zmiennoprzecinkowe mogñ byè
             przechowywane w pamiöci na dwa ró ne sposoby. Zaäó my, e pracujesz z cztero-
        bajtowñ warto ciñ, takñ jak int, na przykäad 1234, czyli 4D2 w zapisie szesnastkowym
        (1234 = 4×256+13×16+2). Mo e ona zostaè przechowana w ten sposób, e pierwszym
        z czterech bajtów pamiöci bödzie bajt najbardziej znaczñcy (ang. most significant byte,
        MSB): 00 00 04 D2. Albo w taki sposób, e bödzie to bajt najmäodszy (ang. least signifi-
        cant byte, LSB): D2 04 00 00. Pierwszy sposób stosowany jest przez maszyny SPARC,
        a drugi przez procesory Pentium. Mo e to powodowaè problemy z przenoszeniem nawet
        najprostszych plików danych pomiödzy ró nymi platformami. W jözyku Java zawsze sto-
        sowany jest pierwszy sposób, niezale nie od procesora. Dziöki temu pliki danych progra-
        mów w jözyku Java sñ niezale ne od platformy.

     Metoda writeUTF zapisuje áa cuchy, u ywaj c zmodyfikowanej wersji 8-bitowego kodu UTF
     (ang. Unicode Text Format). Zamiast po prostu zastosowaü od razu standardowe kodowanie
     UTF-8 (przedstawione w tabeli 1.4), znaki áa cucha s najpierw reprezentowane w kodzie
     UTF-16 (patrz tabela 1.5), a dopiero potem przekodowywane na UTF-8. Wynik takiego kodo-
     wania ró ni si dla znaków o kodach wi kszych od 0xFFFF. Kodowanie takie stosuje si dla
     zachowania zgodno ci z maszynami wirtualnymi powstaáymi, gdy Unicode zadowalaá si
     tylko 16 bitami.

     Poniewa opisana modyfikacja kodowania UTF-8 stosowana jest wyá cznie na platformie Java,
     to metody writeUTF powinni my u ywaü tylko do zapisu áa cuchów przetwarzanych przez
     programy wykonywane przez maszyn wirtualn Java. W pozostaáych przypadkach nale y
     u ywaü metody writeChars.
RozdziaÄ 1.   Q   Strumienie i pliki   41


Tabela 1.4. Kodowanie UTF-8

 Zakres znaków       Kodowanie
 0...7F              0a6a5a4a3a2a1a0
 80...7FF            110a10a9a8a7a6 10a5a4a3a2a1a0
 800...FFFF          1110a15a14a13a12 10a11a10a9a8a7a6 10a5a4a3a2a1a0
 10000...10FFFF      11110a20a19a18 10a17a16a15a14a13a12 10a11a10a9a8a7a6 10a5a4a3a2a1a0

Tabela 1.5. Kodowanie UTF-16

 Zakres znaków       Kodowanie
 0...FFFF            a15a14a13a12a11a10a9a8 a7a6a5a4a3a2a1a0
 10000...10FFFF      110110b19b18b17b16a15a14a13a12a11a10 110111a9a8 a7a65a4a3a2a1a0 gdzie b19b18b17b16 =
                     a20a19a18a17a16 - 1


                  Definicje kodów UTF-8 i UTF-16 znajdziesz w dokumentach, odpowiednio: RFC 2279
                  (http://ietf.org/rfc/rfc2279.txt) i RFC 2781 (http://ietf.org/rfc/rfc2781.txt).

          Aby odczytaü dane, korzystamy z poni szych metod interfejsu DataInput:
             readInt
             readShort
             readLong
             readFloat
             readDouble
             readChar
             readBoolean
             readUTF

          Klasa DataInputStream implementuje interfejs DataInput. Aby odczytaü dane binarne z pli-
          ku, á czymy obiekt klasy DataInputStream ze ródáem bajtów, takim jak na przykáad obiekt
          klasy FileInputStream:
             DataInputStream in = new DataInputStream(new FileInputStream("employee.dat"));

          Podobnie, aby zapisaü dane binarne, u ywamy klasy DataOutputStream implementuj cej
          interfejs DataOutput:
             DataOutputStream out = new DataOutputStream(new FileOutputStream("employee.dat"));


             java.io.DataInput 1.0

            Q   boolean readBoolean()
            Q   byte readByte()
            Q   char readChar()
            Q   double readDouble()
            Q   float readFloat()
42   Java. Techniki zaawansowane


       Q   int readInt()
       Q   long readLong()
       Q   short readShort()
           wczytuje warto ü okre lonego typu.
       Q   void readFully(byte[] b)
           wczytuje bajty do tablicy b, blokuj c w tek, dopóki wszystkie bajty nie zostan
           wczytane.
           Parametry: b                  bufor, do którego zapisywane s dane.
       Q   void readFully(byte[] b, int off, int len)
           wczytuje bajty do tablicy b, blokuj c w tek, dopóki wszystkie bajty nie zostan
           wczytane.
           Parametry: b                  bufor, do którego zapisywane s dane.
                         off             indeks pierwszego bajta.
                         len             maksymalna ilo ü odczytanych bajtów.
       Q   String readUTF()
           wczytuje áa cuch znaków zapisanych w zmodyfikowanym formacie UTF-8.
       Q   int skipBytes(int n)
           ignoruje n bajtów, blokuj c w tek, dopóki wszystkie bajty nie zostan
           zignorowane.
           Parametry: n                 liczba ignorowanych bajtów.

        java.io.DataOutput 1.0

       Q   void writeBoolean(boolean b)
       Q   void writeByte(int b)
       Q   void writeChar(char c)
       Q   void writeDouble(double d)
       Q   void writeFloat(float f)
       Q   void writeInt(int i)
       Q   void writeLong(long l)
       Q   void writeShort(short s)
           zapisuj warto ü okre lonego typu.
       Q   void writeChars(String s)
           zapisuje wszystkie znaki podanego áa cucha.
       Q   void writeUTF(String s)
           zapisuje áa cuch znaków w zmodyfikowanym formacie UTF-8.
RozdziaÄ 1.   Q   Strumienie i pliki   43


Strumienie plików o swobodnym dostÂpie
    Strumie RandomAccessFile pozwala pobraü lub zapisaü dane w dowolnym miejscu pliku.
    Do plików dyskowych mo emy uzyskaü swobodny dost p, inaczej ni w przypadku strumieni
    danych pochodz cych z sieci. Plik o swobodnym dost pie mo emy otworzyü w trybie tylko
    do odczytu albo zarówno do odczytu, jak i do zapisu. Okre lamy to, u ywaj c jako drugiego
    argumentu konstruktora áa cucha "r" (odczyt) lub "rw" (odczyt i zapis).
       RandomAccesFile in = new RandomAccesFile("employee.dat", "r");
       RandomAccesFile inOut = new RandomAccesFile("employee.dat", "rw");

    Otwarcie istniej cego pliku przy u yciu RandomAccessFile nie powoduje jego skasowania.

    Plik o swobodnym dost pie posiada wska nik pliku. Wska nik pliku opisuje pozycj nast p-
    nego bajta, który zostanie wczytany lub zapisany. Metoda seek zmienia poáo enie wska nika,
    okre laj c numer bajta, na który wskazuje. Argumentem metody seek jest liczba typu long
    z przedziaáu od 0 do dáugo ci pliku w bajtach.

    Metoda getFilePointer zwraca aktualne poáo enie wska nika pliku.

    Klasa RandomAccessFile implementuje zarówno interfejs DataInput, jak i DataOutput. Aby
    czytaü z pliku o swobodnym dost pie, u ywamy tych samych metod, np. readInt/writeInt
    lub readChar/writeChar, które omówili my w poprzednim podrozdziale.

    Prze ledzimy teraz dziaáanie programu, który przechowuje rekordy pracowników w pliku
    o swobodnym dost pie. Ka dy z rekordów b dzie mieü ten sam rozmiar, co uáatwi nam ich
    wczytywanie. Zaáó my na przykáad, e chcemy ustawiü wska nik pliku na trzecim rekordzie.
    Musimy zatem wyznaczyü bajt, na którym nale y ustawiü ten wska nik, a nast pnie mo emy
    ju wczytaü rekord.
       long n = 3;
       in.seek((n - 1) * RECORD_SIZE);
       Employee e = new Employee();
       e.readData(in);

    Je li zmodyfikujemy rekord i b dziemy chcieli zapisaü go w tym samym miejscu pliku,
    musimy pami taü, aby przywróciü wska nik pliku na pocz tek tego rekordu:
       in.seek((n - 1) * RECORD_SIZE);
       e.writeData(out);

    Aby okre liü caákowit liczb bajtów w pliku, u ywamy metody length. Caákowit liczb
    rekordów w pliku ustalamy, dziel c liczb bajtów przez rozmiar rekordu.
       long nbytes = in.length(); // d ugo è w bajtach
       int nrecords = (int) (nbytes / RECORD_SIZE);

    Liczby caákowite i zmiennoprzecinkowe posiadaj reprezentacj binarn o staáej liczbie bajtów.
    W przypadku áa cuchów znaków sytuacja jest nieco trudniejsza. Stworzymy zatem dwie
    metody pomocnicze pozwalaj ce zapisywaü i wczytywaü áa cuchy o ustalonym rozmiarze.

    Metoda writeFixedString zapisuje okre lon liczb kodów, zaczynaj c od pocz tku áa cucha.
    (Je li jest ich za maáo, to dopeánia áa cuch warto ciami zerowymi).
44   Java. Techniki zaawansowane

        public static void writeFixedString(String s, int size, DataOutput out)
           throws IOException
        {
           for (int i = 0; i < size; i++)
           {
              char ch = 0;
              if (i < s.length()) ch = s.charAt(i);
              out.writeChar(ch);
           }
        }

     Metoda readFixedString wczytuje size kodów znaków ze strumienia wej ciowego lub do
     momentu napotkania warto ci zerowej. Wszystkie pozostaáe warto ci zerowe zostaj pomi-
     ni te. Dla lepszej efektywno ci metoda u ywa klasy StringBuilder do wczytania áa cucha.
        public static String readFixedString(int size, DataInput in)
           throws IOException
        {
           StringBuilder b = new StringBuilder(size);
           int i = 0;
           boolean more = true;
           while (more && i < size)
           {
              char ch = in.readChar();
              i++;
              if (ch == 0) more = false;
              else b.append(ch);
           }
           in.skipBytes(2 * (size - i));
           return b.toString();
        }

     Metody writeFixedString i readFixedString umie cili my w klasie pomocniczej DataIO.

     Aby zapisaü rekord o staáym rozmiarze, zapisujemy po prostu wszystkie jego pola w formacie
     binarnym.
        public void writeData(DataOutput out) throws IOException
        {
           DataIO.writeFixedString(name, NAME_SIZE, out);
           out.writeDouble(salary);

            GregorianCalendar calendar = new GregorianCalendar();
            calendar.setTime(hireDay);
            out.writeInt(calendar.get(Calendar.YEAR));
            out.writeInt(calendar.get(Calendar.MONTH) + 1);
            out.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
        }

     Odczyt rekordu jest równie prosty.
        public void readData(DataInput in) throws IOException
        {
           name = DataIO.readFixedString(NAME_SIZE, in);
           salary = in.readDouble();
           int y = in.readInt();
           int m = in.readInt();
RozdziaÄ 1.   Q   Strumienie i pliki   45

               int d = in.readInt();
               GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d);
               hireDay = calendar.getTime();
           }

       Wyznaczmy jeszcze rozmiar ka dego rekordu. àa cuchy znakowe przechowuj ce nazwiska
       b d miaáy 40 znaków dáugo ci. W rezultacie ka dy rekord b dzie zajmowaü 100 bajtów:
          Q    40 znaków = 80 bajtów dla pola name
          Q    1 double = 8 bajtów dla pola salary
          Q    3 int = 12 bajtów dla pola date

       Program przedstawiony na listingu 1.2 zapisuje trzy rekordy w pliku danych, a nast pnie
       wczytuje je w odwrotnej kolejno ci. Efektywne dziaáanie programu wymaga pliku o swobod-
       nym dost pie, poniewa najpierw zostanie wczytany ostatni rekord.

Listing 1.2. RandomFileTest.java
           import java.io.*;
           import java.util.*;

           /**
             * @version 1.11 2004-05-11
             * @author Cay Horstmann
             */
           public class RandomFileTest
           {
                public static void main(String[] args)
                {
                   Employee[] staff = new Employee[3];

                  staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
                  staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
                  staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);

                  try
                  {
                        // zapisuje rekordy wszystkich pracowników w pliku employee.dat
                        DataOutputStream out = new DataOutputStream(new
                        ´FileOutputStream("employee.dat"));
                        for (Employee e : staff)
                           e.writeData(out);
                        out.close();

                        // wczytuje wszystkie rekordy do nowej tablicy
                        RandomAccessFile in = new RandomAccessFile("employee.dat", "r");
                        // oblicza rozmiar tablicy
                        int n = (int)(in.length() / Employee.RECORD_SIZE);
                        Employee[] newStaff = new Employee[n];

                        // wczytuje rekordy pracowników w odwrotnej kolejno ci
                        for (int i = n - 1; i >= 0; i--)
                        {
                           newStaff[i] = new Employee();
                           in.seek(i * Employee.RECORD_SIZE);
46   Java. Techniki zaawansowane

                        newStaff[i].readData(in);
                     }
                     in.close();

                     // wy wietla wczytane rekordy
                     for (Employee e : newStaff)
                        System.out.println(e);
                  }
                  catch (IOException e)
                  {
                     e.printStackTrace();
                  }
            }
        }

        class Employee
        {
           public Employee() {}

            public Employee(String n, double s, int year, int month, int day)
            {
               name = n;
               salary = s;
               GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
               hireDay = calendar.getTime();
            }

            public String getName()
            {
               return name;
            }

            public double getSalary()
            {
               return salary;
            }

            public Date getHireDay()
            {
               return hireDay;
            }

            /**
                  Podnosi wynagrodzenie pracownika.
                  @byPercent podwy ka procentowo
            */
            public void raiseSalary(double byPercent)
            {
               double raise = salary * byPercent / 100;
               salary += raise;
            }

            public String toString()
            {
               return getClass().getName()
                  + "[name=" + name
                  + ",salary=" + salary
RozdziaÄ 1.   Q   Strumienie i pliki   47

             + ",hireDay=" + hireDay
             + "]";
    }

    /**
          Zapisuje dane pracownika
          @param out obiekt klasy DataOutput
    */
    public void writeData(DataOutput out) throws IOException
    {
       DataIO.writeFixedString(name, NAME_SIZE, out);
       out.writeDouble(salary);

          GregorianCalendar calendar = new GregorianCalendar();
          calendar.setTime(hireDay);
          out.writeInt(calendar.get(Calendar.YEAR));
          out.writeInt(calendar.get(Calendar.MONTH) + 1);
          out.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
    }

    /**
          Wczytuje dane pracownika
          @param in obiekt klasy DataOutput
    */
    public void readData(DataInput in) throws IOException
    {
       name = DataIO.readFixedString(NAME_SIZE, in);
       salary = in.readDouble();
       int y = in.readInt();
       int m = in.readInt();
       int d = in.readInt();
       GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d);
       hireDay = calendar.getTime();
    }

    public static final int NAME_SIZE = 40;
    public static final int RECORD_SIZE = 2 * NAME_SIZE + 8 + 4 + 4 + 4;

    private String name;
    private double salary;
    private Date hireDay;
}

class DataIO
{
   public static String readFixedString(int size, DataInput in)
      throws IOException
   {
      StringBuilder b = new StringBuilder(size);
      int i = 0;
      boolean more = true;
      while (more && i < size)
      {
         char ch = in.readChar();
         i++;
         if (ch == 0) more = false;
         else b.append(ch);
48   Java. Techniki zaawansowane

                }
                in.skipBytes(2 * (size - i));
                return b.toString();
            }

            public static void writeFixedString(String s, int size, DataOutput out)
               throws IOException
            {
               for (int i = 0; i < size; i++)
               {
                  char ch = 0;
                  if (i < s.length()) ch = s.charAt(i);
                  out.writeChar(ch);
               }
            }
        }



        java.io.RandomAccessFile 1.0

       Q    RandomAccessFile(String file, String mode)
       Q    RandomAccessFile(File file, String mode)
            Parametry:      file         plik, który ma zostaü otwarty.
                            tryb         "r" dla samego odczytu, "rw" dla odczytu i zapisu,
                                         "rws" dla odczytu i zapisu danych wraz
                                         z synchronicznym zapisem danych i metadanych
                                         dla ka dej aktualizacji, "rwd" dla odczytu i zapisu
                                         danych wraz z synchronicznym zapisem tylko
                                         samych danych.
       Q    long getFilePointer()
            zwraca aktualne poáo enie wska nika pliku.
       Q    void seek(long pos)
            zmienia poáo enie wska nika pliku, przesuwaj c go o pos bajtów od pocz tku pliku.
       Q    long length()
            zwraca dáugo ü pliku w bajtach.



Strumienie plików ZIP
     Pliki ZIP to archiwa, w których mo na przechowywaü jeden lub wi cej plików w postaci
     (zazwyczaj) skompresowanej. Ka dy plik ZIP posiada nagáówek zawieraj cy informacje, takie
     jak nazwa pliku i u yta metoda kompresji. W j zyku Java, aby czytaü z pliku ZIP, korzystamy
     z klasy ZipInputStream. Odczyt dotyczy okre lonej pozycji w archiwum. Metoda getNext
     ´Entry zwraca obiekt typu ZipEntry opisuj cy pozycj archiwum. Metoda read klasy Zip
RozdziaÄ 1.   Q   Strumienie i pliki   49


´InputStream zwraca warto ü –1, gdy napotka koniec pozycji archiwum, a nie koniec caáego
pliku ZIP. Aby odczytaü kolejn pozycj archiwum, musimy wtedy wywoáaü metod close
´Entry. Oto typowy kod wczytuj cy zawarto ü pliku ZIP:
   ZipInputStream zin = ZipInputStream
     (new FileInputStream(zipname));
   ZipEntry entry;
   while ((entry = zin.getNextEntry()) != null)
   {
     analizuj entry;
     wczytaj zawarto è zin;
     zin.closeEntry();
   }
   zin.close();

Wczytuj c zawarto ü pozycji pliku ZIP, zwykle zamiast z podstawowej metody read lepiej
b dzie skorzystaü z jakiego bardziej kompetentnego filtru strumienia. Dla przykáadu, aby
wydobyü z archiwum ZIP plik tekstowy, mo emy skorzystaü z poni szej p tli:
   Scanner in = new Scanner(zin);
   while (in.hasNextLine())
      operacje na in.nextLine();


        Strumieþ wej cia ZIP wyrzuca wyjñtek ZipException, je eli w czasie czytania pliku
        ZIP nastñpiä bäñd. Zazwyczaj dzieje siö tak, gdy archiwum zostaäo uszkodzone.

Aby zapisaü dane do pliku ZIP, u ywamy strumie ZipOutputStream. Dla ka dej pozycji, któr
chcemy umie ciü w archiwum ZIP, tworzymy obiekt ZipEntry. Nazw pliku przekazujemy
konstruktorowi ZipEntry; konstruktor sam okre la inne parametry, takie jak data pliku i metoda
dekompresji. Je li chcemy, mo emy zmieniü ich warto ci. Aby rozpocz ü zapis nowego pliku
w archiwum, wywoáujemy metod putNextEntry klasy ZipOutputStream. Nast pnie wysyáamy
dane do strumienia ZIP. Po zako czeniu zapisu pliku wywoáujemy metod closeEntry.
Wymienione operacje powtarzamy dla wszystkich plików, które chcemy skompresowaü
w archiwum. Oto schemat kodu:
   FileOutputStream fout = new FileOutputStream("test.zip");
   ZipOutputStream zout = new ZipOutputStream(fout);
   dla wszystkich plików
   {
     ZipEntry ze = new ZipEntry(nazwapliku);
     zout.putNextEntry(kze);
     wy lij dane do zout;
     zout.closeEntry();
   }
   zout.close();


       Pliki JAR (omówione w rozdziale 10. ksiñ ki Java 2. Podstawy sñ po prostu plikami
       ZIP, zawierajñcymi specjalny rodzaj pliku, tzw. manifest. Do wczytania i zapisania
   manifestu u ywamy klas JarInputStream i JarOutputStream.

Strumienie ZIP s dobrym przykáadem pot gi abstrakcji strumieni. Odczytuj c dane prze-
chowywane w skompresowanej postaci, nie musimy zajmowaü si ich dekompresj . ródáo
50      Java. Techniki zaawansowane


        bajtów formatu ZIP nie musi byü plikiem — dane ZIP mog byü ci gane przez poá czenie
        sieciowe. Na przykáad za ka dym razem, gdy mechanizm áadowania klas jakiego apletu wczy-
        tuje plik JAR, tak naprawd wczytuje i dekompresuje dane pochodz ce z sieci.

                  Artykuä dostöpny pod adresem http://www.javaworld.com/javaworld/jw-10-2000/
                  ´jw-1027-toolbox.html przedstawia sposób modyfikacji archiwum ZIP.

        Program przedstawiony na listingu 1.3 pozwala u ytkownikowi otworzyü archiwum ZIP.
        Nast pnie wy wietla pliki przechowywane w tym archiwum w postaci listy, w dolnej cz ci
        okna. Je eli u ytkownik kliknie dwukrotnie który z plików, jego zawarto ü zostanie wy wie-
        tlona w obszarze tekstowym, tak jak jest to pokazane na rysunku 1.5.

Listing 1.3. ZipTest.java
            import   java.awt.*;
            import   java.awt.event.*;
            import   java.io.*;
            import   java.util.*;
            import   java.util.List;
            import   java.util.zip.*;
            import   javax.swing.*;

            /**
              * @version 1.32 2007-06-22
              * @author Cay Horstmann
              */
            public class ZipTest
            {
                 public static void main(String[] args)
                 {
                    EventQueue.invokeLater(new Runnable()
                       {
                           public void run()
                           {
                              ZipTestFrame frame = new ZipTestFrame();
                              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                              frame.setVisible(true);
                           }
                       });
                 }
            }

            /**
              * Ramka zawierajæca obszar tekstowy wy wietlajæcy zawarto è pliku,
              * listú pozwalajæcæ na wybór plików w archiwum
              * oraz menu pozwalajæce wczytaè nowe archiwum.
              */
            class ZipTestFrame extends JFrame
            {
                public ZipTestFrame()
                {
                   setTitle("ZipTest");
                   setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

                     // dodaje menu i opcje Open oraz Exit
RozdziaÄ 1.   Q   Strumienie i pliki   51


Rysunek 1.5.
Program ZipTest
w dziaäaniu




                  JMenuBar menuBar = new JMenuBar();
                  JMenu menu = new JMenu("File");

                  JMenuItem openItem = new JMenuItem("Open");
                  menu.add(openItem);
                  openItem.addActionListener(new ActionListener()
                     {
                         public void actionPerformed(ActionEvent event)
                         {
                            JFileChooser chooser = new JFileChooser();
                            chooser.setCurrentDirectory(new File("."));
                            int r = chooser.showOpenDialog(ZipTestFrame.this);
                            if (r == JFileChooser.APPROVE_OPTION)
                            {
                               zipname = chooser.getSelectedFile().getPath();
                               fileCombo.removeAllItems();
                               scanZipFile();
                            }
                         }
                     });

                  JMenuItem exitItem = new JMenuItem("Exit");
                  menu.add(exitItem);
                  exitItem.addActionListener(new ActionListener()
                     {
                         public void actionPerformed(ActionEvent event)
                         {
                            System.exit(0);
                         }
                     });

                  menuBar.add(menu);
                  setJMenuBar(menuBar);

                  // dodaje obszar tekstowy i listú
                  fileText = new JTextArea();
                  fileCombo = new JComboBox();
                  fileCombo.addActionListener(new ActionListener()
52   Java. Techniki zaawansowane

                  {
                        public void actionPerformed(ActionEvent event)
                        {
                           loadZipFile((String) fileCombo.getSelectedItem());
                        }
                  });

               add(fileCombo, BorderLayout.SOUTH);
               add(new JScrollPane(fileText), BorderLayout.CENTER);
           }

           /**
            * Skanuje zawarto è archiwum ZIP i wype nia listú.
            */
           public void scanZipFile()
           {
               new SwingWorker<Void, String>()
                  {
                     protected Void doInBackground() throws Exception
                     {
                        ZipInputStream zin = new ZipInputStream(new
                        ´FileInputStream(zipname));
                        ZipEntry entry;
                        while ((entry = zin.getNextEntry()) != null)
                        {
                           publish(entry.getName());
                           zin.closeEntry();
                        }
                        zin.close();
                        return null;
                     }

                        protected void process(List<String> names)
                        {
                           for (String name : names)
                              fileCombo.addItem(name);

                     }
                  }.execute();
           }

           /**
            * aduje zawarto è pliku z archiwum ZIP
            * do obszaru tekstowego
            * @param name nazwa pliku w archiwum

            */
           public void loadZipFile(final String name)
           {
               fileCombo.setEnabled(false);
               fileText.setText("");
               new SwingWorker<Void, Void>()
                  {
                     protected Void doInBackground() throws Exception
                     {
                        try
                        {
RozdziaÄ 1.   Q   Strumienie i pliki   53

                    ZipInputStream zin = new ZipInputStream(new
                    ´FileInputStream(zipname));
                    ZipEntry entry;

                    // znajduje element archiwum o odpowiedniej nazwie
                    while ((entry = zin.getNextEntry()) != null)
                    {
                       if (entry.getName().equals(name))
                       {
                          // wczytuje go do obszaru tekstowego
                          Scanner in = new Scanner(zin);
                          while (in.hasNextLine())
                          {
                             fileText.append(in.nextLine());
                             fileText.append("n");
                          }
                       }
                       zin.closeEntry();
                    }
                    zin.close();
                 }
                 catch (IOException e)
                 {
                    e.printStackTrace();
                 }
                 return null;
             }

             protected void done()
             {
                fileCombo.setEnabled(true);
             }
          }.execute();
    }

    public static final int DEFAULT_WIDTH = 400;
    public static final int DEFAULT_HEIGHT = 300;

    private JComboBox fileCombo;
    private JTextArea fileText;
    private String zipname;
}



java.util.zip.ZipInputStream 1.1

Q   ZipInputStream(InputStream in)
    tworzy obiekt typu ZipInputStream umo liwiaj cy dekompresj danych z podanego
    strumienia InputStream.
Q   ZipEntry getNextEntry()
    zwraca obiekt typu ZipEntry opisuj cy nast pn pozycj archiwum lub null, je eli
    archiwum nie ma wi cej pozycji.
54   Java. Techniki zaawansowane


       Q   void closeEntry()
           zamyka aktualnie otwart pozycj archiwum ZIP. Dzi ki temu mo emy odczytaü
           nast pn pozycj , wywoáuj c metod getNextEntry().

        java.util.zip.ZipOutputStream 1.1

       Q   ZipOutputStream(OutputStream out)
           tworzy obiekt typu ZipOutputStream, który umo liwia kompresj i zapis danych
           w podanym strumieniu OutputStream.
       Q   void putNextEntry(ZipEntry ze)
           zapisuje informacje podanej pozycji ZipEntry do strumienia i przygotowuje strumie
           do odbioru danych. Dane mog zostaü zapisane w strumieniu przy u yciu metody
           write().
       Q   void closeEntry()
           zamyka aktualnie otwart pozycj archiwum ZIP. Aby otworzyü nast pn pozycj ,
           wywoáujemy metod putNextEntry.
       Q   void setLevel(int level)
           okre la domy lny stopie kompresji nast pnych pozycji archiwum o trybie DEFLATED.
           Domy ln warto ci jest Deflater.DEFAULT_COMPRESSION. Wyrzuca wyj tek
           IllegalArgumentException, je eli podany stopie jest nieprawidáowy.
           Parametry: level           stopie kompresji, od 0 (NO_COMPRESSION)
                                      do 9 (BEST_COMPRESSION).
       Q   void setMethod(int method)
           okre la domy ln metod kompresji dla danego ZipOutputStream dla wszystkich
           pozycji archiwum, dla których metoda kompresji nie zostaáa okre lona.
           Parametry: method           metoda kompresji, DEFLATED lub STORED.

        java.util.zip.ZipEntry 1.1

       Q   ZipEntry(String name)
           Parametry:      name         nazwa elementu.
       Q   long getCrc()
           zwraca warto ü sumy kontrolnej CRC32 danego elementu.
       Q   String getName()
           zwraca nazw elementu.
       Q   long getSize()
           zwraca rozmiar danego elementu po dekompresji lub –1, je eli rozmiar nie jest
           znany.
       Q   boolean isDirectory()
           zwraca warto ü logiczn , która okre la, czy dany element archiwum jest katalogiem.
RozdziaÄ 1.   Q   Strumienie i pliki   55


     Q   void setMethod(int method)
         Parametry:     method        metoda kompresji danego elementu, DEFLATED
                                      lub STORED.
     Q   void setSize(long rozmiar)
         okre la rozmiar elementu. Wymagana, je eli metod kompresji jest STORED.
         Parametry: rozmiar         rozmiar nieskompresowanego elementu.
     Q   void setCrc(long crc)
         okre la sum kontroln CRC32 dla danego elementu. Aby obliczyü t sum
         u ywamy klasy CRC32. Wymagana, je eli metod kompresji jest STORED.
         Parametry: crc          suma kontrolna elementu.

      java.util.ZipFile 1.1

     Q   ZipFile(String name)
         ten konstruktor tworzy obiekt typu ZipFile, otwarty do odczytu, na podstawie
         podanego áa cucha.
     Q   ZipFile(File file)
         tworzy obiekt typu ZipFile, otwarty do odczytu, na podstawie podanego áa cucha
         lub obiektu typu File.
     Q   Enumeration entries()
         zwraca obiekt typu Enumeration, wyliczaj cy obiekty ZipEntry opisuj ce elementy
         archiwum ZipFile.
     Q   ZipEntry getEntry(String name)
         zwraca element archiwum o podanej nazwie lub null, je eli taki element nie istnieje.
         Parametry: name            nazwa elementu.
     Q   InputStream getInputStream(ZipEntry ze)
         zwraca obiekt InputStream dla podanego elementu.
         Parametry: ze              element ZipEntry w pliku ZIP.
     Q   String getName() zwraca cie k dost pu do pliku ZIP.




Strumienie obiektów i serializacja
   Korzystanie z rekordów o staáej dáugo ci jest dobrym rozwi zaniem, pod warunkiem e zapi-
   sujemy dane tego samego typu. Jednak obiekty, które tworzymy w programie zorientowanym
   obiektowo, rzadko nale do tego samego typu. Dla przykáadu: mo emy u ywaü tablicy o nazwie
   staff, której nominalnym typem jest Employee, ale która zawiera obiekty b d ce instancjami
   klas pochodnych, np. klasy Manager.
56   Java. Techniki zaawansowane


     Z pewno ci mo na zaprojektowaü format danych, który pozwoli przechowywaü takie poli-
     morficzne kolekcje, ale na szcz cie ten dodatkowy wysiáek nie jest konieczny. J zyk Java
     obsáuguje bowiem bardzo ogólny mechanizm zwany serializacj obiektów. Pozwala on na
     wysáanie do strumienia dowolnego obiektu i umo liwia jego pó niejsze wczytanie (w dalszej
     cz ci tego rozdziaáu wyja nimy, sk d wzi á si termin „serializacja”).

     Aby zachowaü dane obiektu, musimy najpierw otworzyü strumie ObjectOutputStream:
        ObjectOutputStream out = new ObjectOutputStream(new
        ´FileOutputStream("employee.dat"));

     Teraz, aby zapisaü obiekt, wywoáujemy metod writeObject klasy ObjectOutputStream:
        Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
        Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
        out.writeObject(harry);
        out.writeObject(boss);

     Aby z powrotem zaáadowaü obiekty, u ywamy strumienia ObjectInputStream:
        ObjectInputStream in = new ObjectInputStream(new FIleInputStream("employee.dat"));

     Nast pnie pobieramy z niego obiekty w tym samym porz dku, w jakim zostaáy zapisane,
     korzystaj c z metody readObject:
        Employee p1 = (Employee)in.readObject();
        Employee p2 = (Employee)in.readObject();

     Je li chcemy zapisywaü i odtwarzaü obiekty za pomoc strumieni obiektów, to konieczne jest
     wprowadzenie jednej modyfikacji w klasie tych obiektów. Klasa ta musi implementowaü
     interfejs Serializable:
        class Employee implements Serializable { . . . }

     Interfejs Serializable nie posiada metod, nie musimy zatem wprowadzaü adnych innych
     modyfikacji naszych klas. Pod tym wzgl dem Serializable jest podobny do interfejsu Clone-
     able, który omówili my w rozdziale 6. ksi ki Java 2. Podstawy. Aby jednak móc klonowaü
     obiekty, musimy przesáoniü metod clone klasy Object. Aby móc serializowaü, nie nale y
     robiü nic poza dopisaniem powy szych sáów.

     Jednak e musimy rozwa yü jeszcze jedn sytuacj . Co si stanie, je eli dany obiekt jest wspóá-
     dzielony przez kilka innych obiektów jako element ich stanu?

     Aby zilustrowaü ten problem, zmodyfikujemy troch klas Manager. Zaáó my, e ka dy
     mened er ma asystenta:
        class Manager extends Employee
        {

            . . .
            private Employee secretary;
        }

     Ka dy obiekt typu Manager przechowuje teraz referencj do obiektu klasy Employee opisuj -
     cego asystenta, a nie osobn kopi tego obiektu.
RozdziaÄ 1.   Q   Strumienie i pliki   57


      Oznacza to, e dwóch mened erów mo e mieü tego samego asystenta, tak jak zostaáo to przed-
      stawione na rysunku 1.6 i w poni szym kodzie:
          harry = new Employee("Harry Hacker", . . .);
          Manager carl = new Manager("Carl Cracker", . . .);
          carl.setSecretary(harry);
          Manager tony = new Manager("Tony Tester", . . .);
          tony.setSecretary(harry);

Rysunek 1.6.
Dwóch
mened erów
mo e mieè
wspólnego
asystenta




      Teraz zaáó my, e zapisujemy dane pracowników na dysk.

      Oczywi cie nie mo emy zapisaü i przywróciü adresów obiektów asystentów, poniewa po
      ponownym zaáadowaniu obiekt asystenta najprawdopodobniej znajdzie si w zupeánie innym
      miejscu pami ci.

      Zamiast tego ka dy obiekt zostaje zapisany z numerem seryjnym i st d wáa nie pochodzi
      okre lenie serializacja. Oto jej algorytm:
         Q     Wszystkim napotkanym referencjom do obiektów nadawane s numery seryjne
               (patrz rysunek 1.7).
         Q     Je li referencja do obiektu zostaáa napotkana po raz pierwszy, obiekt zostaje
               zapisany w strumieniu.
         Q     Je eli obiekt zostaá ju zapisany, Java zapisuje, e w danym miejscu znajduje si
               „ten sam obiekt, co pod numerem seryjnym x”.

      Wczytuj c obiekty z powrotem, Java odwraca caá procedur .
         Q     Gdy obiekt pojawia si w strumieniu po raz pierwszy, Java tworzy go, inicjuje
               danymi ze strumienia i zapami tuje zwi zek pomi dzy numerem i referencj
               do obiektu.
58     Java. Techniki zaawansowane


Rysunek 1.7.
Przykäad serializacji
obiektów




          Q   Gdy natrafi na znacznik „ten sam obiekt, co pod numerem seryjnym x”, sprawdza,
              gdzie znajduje si obiekt o danym numerze, i nadaje referencji do obiektu adres
              tego miejsca.

                 W tym rozdziale korzystamy z serializacji, aby zapisaè zbiór obiektów na dysk, a pó -
                 niej z powrotem je wczytaè. Innym bardzo wa nym zastosowaniem serializacji jest
           przesyäanie obiektów przez sieè na inny komputer. Podobnie jak adresy pamiöci sñ bezu-
            yteczne dla pliku, tak samo sñ bezu yteczne dla innego rodzaju procesora. Poniewa
           serializacja zastöpuje adresy pamiöci numerami seryjnymi, mo emy transportowaè zbiory
           danych z jednej maszyny na drugñ. Omówimy to zastosowanie przy okazji wywoäywania
           zdalnych metod w rozdziale 5.

       Listing 1.4 zawiera program zapisuj cy i wczytuj cy sieü powi zanych obiektów klas Em-
       ployee i Manager (niektóre z nich maj referencj do tego samego asystenta). Zwróü uwag ,
        e po wczytaniu istnieje tylko jeden obiekt ka dego asystenta — gdy pracownik newStaff[1]
       dostaje podwy k , znajduje to odzwierciedlenie za pomoc pól secretary obiektów klasy
       Manager.

Listing 1.4. ObjectStreamTest.java
           import java.io.*;
           import java.util.*;

           /**
             * @version 1.10 17 Aug 1998
             * @author Cay Horstmann
             */
           class ObjectStreamTest
           {
                public static void main(String[] args)
RozdziaÄ 1.   Q   Strumienie i pliki   59

    {
        Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
        Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
        carl.setSecretary(harry);
        Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15);
        tony.setSecretary(harry);

        Employee[] staff = new Employee[3];

        staff[0] = carl;
        staff[1] = harry;
        staff[2] = tony;

        try
        {
              // zapisuje rekordy wszystkich pracowników w pliku employee.dat
              ObjectOutputStream out = new ObjectOutputStream(new
              ´FileOutputStream("employee.dat"));
              out.writeObject(staff);
              out.close();

              // wczytuje wszystkie rekordy do nowej tablicy
              ObjectInputStream in = new ObjectInputStream(new
              ´FileInputStream("employee.dat"));
              Employee[] newStaff = (Employee[]) in.readObject();
              in.close();

              // podnosi wynagrodzenie asystenta
              newStaff[1].raiseSalary(10);

              // wy wietla wszystkie rekordy
              for (Employee e : newStaff)
                 System.out.println(e);
        }
        catch (Exception e)
        {
           e.printStackTrace();
        }
    }
}

class Employee implements Serializable
{
   public Employee()
   {
   }

    public Employee(String n, double s, int year, int month, int day)
    {
       name = n;
       salary = s;
       GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
       hireDay = calendar.getTime();
    }

    public String getName()
    {
60   Java. Techniki zaawansowane

                return name;
            }

            public double getSalary()
            {
               return salary;
            }

            public Date getHireDay()
            {
               return hireDay;
            }

            public void raiseSalary(double byPercent)
            {
               double raise = salary * byPercent / 100;
               salary += raise;
            }

            public String toString()
            {
               return getClass().getName() + "[name=" + name + ",salary=" + salary +
               ´",hireDay=" + hireDay
                     + "]";
            }

            private String name;
            private double salary;
            private Date hireDay;
        }

        class Manager extends Employee
        {
           /**
            * Tworzy obiekt klasy Manager nie inicjujæc pola secretary
            * @param n nazwisko pracownika
            * @param s wynagrodzenie
            * @param year rok zatrudnienia
            * @param month miesiæc zatrudnienia

            * @param day dzie   zatrudnienia

             */
            public Manager(String n, double s, int year, int month, int day)
            {
                super(n, s, year, month, day);
                secretary = null;
            }

            /**
             * Przypisuje asystenta mened erowi.
             * @param s asystent
             */
            public void setSecretary(Employee s)
            {
                secretary = s;
            }
RozdziaÄ 1.   Q   Strumienie i pliki   61

            public String toString()
            {
               return super.toString() + "[secretary=" + secretary + "]";
            }

            private Employee secretary;
        }



        java.io.ObjectOutputStream 1.1

       Q    ObjectOutputStream(OutputStream wy)
            tworzy obiekt ObjectOutputStream, dzi ki któremu mo esz zapisywaü obiekty
            do podanego strumienia wyj cia.
       Q    void writeObject(Object ob)
            zapisuje podany obiekt do ObjectOutputStream. Metoda ta zachowuje klas
            obiektu, sygnatur klasy oraz warto ci wszystkich niestatycznych,
            nieprzechodnich pól skáadowych tej klasy, a tak e jej nadklas.

        java.io.ObjectInputStream 1.1

       Q    ObjectInputStream(InputStream we)
            tworzy obiekt ObjectInputStream, dzi ki któremu mo esz odczytywaü informacje
            z podanego strumienia wej cia.
       Q    Object readObject()
            wczytuje obiekt z ObjectInputStream. Pobiera klas obiektu, sygnatur klasy oraz
            warto ci wszystkich niestatycznych, nieprzechodnich pól skáadowych tej klasy,
            a tak e jej nadklas. Przeprowadza deserializacj , pozwalaj c na przyporz dkowanie
            obiektów referencjom.


Format pliku serializacji obiektów
     Serializacja obiektów powoduje zapisanie danych obiektu w okre lonym formacie. Oczy-
     wi cie, mo emy u ywaü metod writeObject/readObject, nie wiedz c nawet, która sekwencja
     bajtów reprezentuje dany obiekt w pliku. Niemniej jednak doszli my do wniosku, e poznanie
     formatu danych b dzie bardzo pomocne, poniewa daje wgl d w proces obsáugi obiektów
     przez strumienie. Poniewa poni szy tekst jest peáen technicznych detali, to je li nie jeste
     zainteresowany implementacj serializacji, mo esz pomin ü lektur tego podrozdziaáu.

     Ka dy plik zaczyna si dwubajtow „magiczn liczb ”:
        AC ED

     po której nast puje numer wersji formatu serializacji obiektów, którym aktualnie jest
        00 05

     (w tym podrozdziale do opisywania bajtów b dziemy u ywaü notacji szesnastkowej). Pó niej
     nast puje sekwencja obiektów, w takiej kolejno ci, w jakiej zostaáy one zapisane.
62   Java. Techniki zaawansowane


     àa cuchy zapisywane s jako
            74      dáugo ü (2 bajty)      znaki

     Dla przykáadu, áa cuch "Harry" b dzie wygl daá tak:
        74 00 05 Harry

     Znaki Unicode zapisywane s w zmodyfikowanym formacie UTF-8.

     Wraz z obiektem musi zostaü zapisana jego klasa. Opis klasy zawiera:
       Q    nazw klasy,
       Q    unikalny numer ID stanowi cy „odcisk” wszystkich danych skáadowych
            i sygnatur metod,
       Q    zbiór flag opisuj cy metod serializacji,
       Q    opis pól skáadowych.

     Java tworzy wspomniany „odcisk” klasy, pobieraj c opisy klasy, klasy bazowej, interfejsów,
     typów pól danych oraz sygnatury metod w postaci kanonicznej, a nast pnie stosuje do nich
     algorytm SHA (Secure Hash Algorithm).

     SHA to szybki algorytm, tworz cy „odciski palców” dla du ych bloków danych. Niezale nie
     od rozmiaru oryginalnych danych, „odciskiem” jest zawsze pakiet 20 bajtów. Jest on two-
     rzony za pomoc sekwencji operacji binarnych, dzi ki którym mo emy mieü stuprocentow
     pewno ü, e je eli zachowana informacja zmieni si , zmianie ulegnie równie jej „odcisk
     palca”. SHA jest ameryka skim standardem, rekomendowanym przez Narodowy Instytut Nauki
     i Technologii (National Institute of Science and Technology — NIST; aby dowiedzieü si
     wi cej o SHA, zajrzyj np. do Cryptography and Network Security: Principle and Practice,
     autorstwa Williama Stallingsa, wydanej przez Prentice Hall). Jednak e Java korzysta jedynie
     z pierwszych o miu bajtów kodu SHA. Mimo to nadal jest bardzo prawdopodobne, e „odcisk”
     zmieni si , je eli ulegn zmianie pola skáadowe lub metody.

     W chwili odczytu danych Java sprawdza, u ywaj c „odcisku” klasy, czy definicja klasy nie
     ulegáa zmianie. Oczywi cie, w praktyce klasy ulegaj zmianie i mo e si okazaü, e program
     b dzie musiaá wczytaü starsze wersje obiektów. Zagadnienie to omówimy w punkcie „Wersje”
     na stronie 71.

     Oto, w jaki sposób przechowywany jest identyfikator klasy:
           72
           dáugo ü nazwy klasy (2 bajty)
           nazwa klasy
           „odcisk” klasy (8 bajtów)
           zbiór flag (1 bajt)
           liczba deskryptorów pól skáadowych (2 bajty)
           deskryptory pól skáadowych
RozdziaÄ 1.   Q   Strumienie i pliki   63


      78 (znacznik ko ca)
      typ klasy bazowej (70, je li nie istnieje)

Bajt flag skáada si z trzybitowych masek, zdefiniowanych w java.io.ObjectStreamCon
´stants:
   java.io.ObjectStreamConstants:
   static final byte SC_WRITE_METHOD = 1;
    // klasa posiada metodú writeObject zapisujæcæ dodatkowe dane
   static final byte SC_SERIALIZABLE = 2;
    // klasa implementuje interfejs Serializable
   static final byte SC_EXTERNALIZABLE = 4;
    // klasa implementuje interfejs Externalizable

Interfejs Externalizable omówimy w dalszej cz ci rozdziaáu. Klasy implementuj ce Exter
´nalizable udost pniaj wáasne metody wczytuj ce i zapisuj ce, które przejmuj obsáug
nad swoimi polami skáadowymi. Klasy, które budujemy, implementuj interfejs Serializable
i b d mieü bajt flag o warto ci 02. Jednak np. klasa java.util.Date implementuje Externa
´lizable i jej bajt flag ma warto ü 03.

Ka dy deskryptor pola skáadowego skáada si z nast puj cych elementów:
  Q    kod typu (1 bajt),
  Q    dáugo ü nazwy pola (2 bajty),
  Q    nazwa pola,
  Q    nazwa klasy (je eli pole skáadowe jest obiektem).

Kod typu mo e mieü jedn z nast puj cych warto ci:
       B   byte
       C   char
       D   double
       F   float
       I   int
       J   long
       L   obiekt
       S   short
       Z   boolean
       [   tablica

Je eli kodem typu jest L, zaraz za nazw pola skáadowego znajdzie si nazwa jego typu.
àa cuchy nazw klas i pól skáadowych nie zaczynaj si od 74, w przeciwie stwie do typów
pól skáadowych. Typy pól skáadowych u ywaj troch innego sposobu kodowania nazw, a dokáa-
dniej — formatu u ywanego przez metody macierzyste.

Dla przykáadu, pole pensji klasy Employee zostanie zapisane jako:
   D 00 06 salary
64   Java. Techniki zaawansowane


     A oto kompletny opis klasy Employee:
           72 00 08 Employee
                E6 D2 86 7D AE AC 18 1B 02             „Odcisk” oraz flagi
                00 03                                  Liczba pól skáadowych
                D 00 06 salary                         Typ i nazwa pola skáadowego
                L 00 07 hireDay                        Typ i nazwa pola skáadowego
                74 00 10 Ljava/util/Date;              Nazwa klasy pola skáadowego — String
                L 00 04 name                           Typ i nazwa pola skáadowego
                74 00 12 Ljava/lang/String;            Nazwa klasy pola skáadowego — String
                78                                     Znacznik ko ca
                70                                     Brak nadklasy

     Opisy te s do ü dáugie. Je eli w pliku jeszcze raz musi si znale ü opis tej samej klasy, zostanie
     u yta forma skrócona:
                71       numer seryjny (4 bajty)

     Numer seryjny wskazuje na poprzedni opis danej klasy. Schemat numerowania omówimy
     pó niej.

     Obiekt jest przechowywany w nast puj cej postaci:
           73 opis klasy          dane obiektu

     Dla przykáadu, oto zapis obiektu klasy Employee:
           40 E8 6A 00 00 00 00                Warto ü pola salary — double
           73                                  Warto ü pola hireDay — nowy obiekt
              71 00 7E 00 08                   Istniej ca klasa java.util.Date
              77 08 00 00 00 91 1B 4E B1 80 78 Zawarto ü zewn trzna — szczegóáy
                                               poni ej
           74 00 0C Harry Hacker               Warto ü pola name — String

     Jak widzimy, plik danych zawiera informacje wystarczaj ce do odtworzenia obiektu klasy
     Employee.

     Tablice s zapisywane w nast puj cy sposób:
           75 opis klasy          liczba elementów (4 bajty)            elementy

     Nazwa klasy tablicy jest zachowywana w formacie u ywanym przez metody macierzyste
     (ró ni si on troch od formatu nazw innych klas). W tym formacie nazwy klas zaczynaj
     si od L, a ko cz rednikiem.

     Dla przykáadu, tablica trzech obiektów typu Employee zaczyna si tak:
RozdziaÄ 1.   Q   Strumienie i pliki   65


        75                                      Tablica
             72 00 0C [LEmployee;               Nowa klasa, dáugo ü áa cucha, nazwa
                                                klasy Employee[]
                  FC BF 36 11 C5 91 11 C7 02    „Odcisk” oraz flagi
                  00 00                         Liczba pól skáadowych
                  78                            Znacznik ko ca
                  70                            Brak nadklasy
                  00 00 00 03                   Liczba komórek tablicy

Zauwa my, e „odcisk” tablicy obiektów Employee ró ni si od „odcisku” samej klasy
Employee.

Wszystkie obiekty (á cznie z tablicami i áa cuchami) oraz wszystkie opisy klas w chwili
zapisywania do pliku otrzymuj numery seryjne. Numery seryjne zaczynaj si od warto ci
00 7E 00 00.

Przekonali my si ju , e peány opis jest wykonywany tylko raz dla ka dej klasy. Nast pne
opisy po prostu wskazuj na pierwszy. W poprzednim przykáadzie kolejna referencja klasy
Date zostaáa zakodowana w nast puj cy sposób:
   71 00 7E 00 08

Ten sam mechanizm jest stosowany dla obiektów. Je eli zapisywana jest referencja obiektu,
który zostaá ju wcze niej zapisany, nowa referencja zostanie zachowana w dokáadnie ten
sam sposób, jako 71 plus odpowiedni numer seryjny. Z kontekstu zawsze jasno wynika, czy
dany numer seryjny dotyczy opisu klasy, czy obiektu.

Referencja null jest zapisywana jako
   70

Oto plik zapisany przez program ObjectRefTest z poprzedniego podrozdziaáu, wraz z komen-
tarzami. Je li chcesz, uruchom program, spójrz na zapis pliku employee.dat w notacji szes-
nastkowej i porównaj z poni szymi komentarzami. Zwróü uwag na wiersze zamieszczone
pod koniec pliku, zawieraj ce referencje zapisanego wcze niej obiektu.
        AC ED 00 05                             Nagáówek pliku
        75                                      Tablica staff (nr #1)
             72 00 0C [LEmployee;               Nowa klasa, dáugo ü áa cucha, nazwa
                                                klasy Employee[] (nr #0)
                  FC BF 36 11 C5 91 11 C7 02    „Odcisk” oraz flagi
                  00 00                         Liczba pól skáadowych
                  78                            Znacznik ko ca
                  70                            Brak klasy bazowej
                  00 00 00 03                   Liczba elementów tablicy
             73                                 staff[0] — nowy obiekt (nr #7)
66   Java. Techniki zaawansowane


                72 00 08 Manager                    Nowa klasa, dáugo ü áa cucha,
                                                    nazwa klasy (nr #2)
                     36 06 AE 13 63 8F 59 B7 02     „Odcisk” oraz flagi
                     00 01                          Liczba pól skáadowych
                     L 00 08 secretary              Typ i nazwa pola skáadowego
                     74 00 0B LEmployee;            Nazwa klasy pola skáadowego
                                                    — String
                                                    (nr #3)
                     78                             Znacznik ko ca
                     72 00 09 Employee              Nadklasa — nowa klasa, dáugo ü
                                                    áa cucha, nazwa klasy (nr #4)
                        E6 D2 86 7D AE AC 18 1B 02 „Odcisk” oraz flagi
                        00 03                       Liczba pól skáadowych
                        D 00 06 salary              Typ i nazwa pola skáadowego
                        L 00 11 hireDay             Typ i nazwa pola skáadowego
                        74 00 10 Ljava/util/Date; Nazwa klasy pola skáadowego
                                                    — String
                                                    (nr #5)
                        L 00 08 name                Typ i nazwa pola skáadowego
                        74 00 12 Ljava/lang/String; Nazwa klasy pola skáadowego
                                                    — String
                                                    (nr #6)
                        78                          Znacznik ko ca
                        70                          Brak klasy bazowej
                40   F3 88 00 00 00 00 00           Warto ü pola salary — double
                73                                  Warto ü pola hireDay
                                                    — nowy obiekt (nr #9)
                     72 00 0E java.util.Date        Nowa klasa, dáugo ü áa cucha,
                                                    nazwa klasy (nr #8)
                        68 6A 81 01 4B 59 74 19 03 „Odcisk” oraz flagi
                        00 00                       Brak zmiennych skáadowych
                        78                          Znacznik ko ca
                        70                          Brak klasy bazowej
                     77 08                          Zawarto ü zewn trzna, liczba bajtów
                     00 00 00 83 E9 39 E0 00        Data
                     78                             Znacznik ko ca
                74   00 0C Carl Cracker             Warto ü pola name — String (nr #10)
                73                                  Warto ü pola secretary — nowy obiekt
                                                    (nr #11)
                     71 00 7E 00 04                 Istniej ca klasa (u yj nr #4)
                     40 E8 6A 00 00 00 00 00        Warto ü pola pensja — double
RozdziaÄ 1.   Q   Strumienie i pliki   67


                       73                             Warto ü pola dzienZatrudnienia
                                                      — nowy obiekt (nr #12)
                            71 00 7E 00 08            Istniej ca klasa (u yj nr #8)
                            77 08                     Zawarto ü zewn trzna, liczba bajtów
                            00 00 00 91 1B 4E B1 80   Data
                            78                        Znacznik ko ca
                       74 00 0C Harry Hacker          Warto ü pola name — String (nr #13)
             71 00 7E 00 0B                           staff[1] — istniej cy obiekt
                                                      (u yj nr #11)
             73                                       obsluga[2] — nowy obiekt (nr #14)
                  71 00 7E 00 08                      Istniej ca klasa (u yj nr #4)
                  40 E3 88 00 00 00 00 00             Warto ü pola salary — double
                  73                                  Warto ü pola hireDay
                                                      — nowy obiekt (nr #15)
                       71 00 7E 00 08                 Istniej ca klasa (u yj nr #8)
                       77 08                          Zawarto ü zewn trzna, liczba bajtów
                       00 00 00 94 6D 3E EC 00 00     Data
                       78                             Znacznik ko ca
                  74 00 0D Tony Tester                Warto ü pola name — String (nr #16)

          71 00 7E 00 0B                              Warto ü pola secretary — istniej cy
                                                      obiekt (u yj nr #11)

    Zazwyczaj znajomo ü dokáadnego format pliku nie jest wa na (o ile nie próbujesz dokonaü
    zmian w samym pliku).

    Powinni my jednak pami taü, e:
      Q   strumie obiektów zapisuje typy i pola skáadowe wszystkich obiektów,
      Q   ka demu obiektowi zostaje przypisany numer seryjny,
      Q   powtarzaj ce si odwoáania do tego samego obiektu s przechowywane jako
          referencje jego numeru seryjnego.


Modyfikowanie domyÊlnego mechanizmu serializacji
    Niektóre dane nie powinny byü serializowane — np. warto ci typu integer reprezentuj ce
    uchwyty plików lub okien, czytelne wyá cznie dla metod rodzimych. Gdy wczytamy takie dane
    ponownie lub przeniesiemy je na inn maszyn , najcz ciej oka si bezu yteczne. Co gorsza,
    nieprawidáowe warto ci tych zmiennych mog spowodowaü bá dy w dziaáaniu metod rodzi-
    mych. Dlatego Java obsáuguje prosty mechanizm zapobiegaj cy serializacji takich danych.
    Wystarczy oznaczyü je sáowem kluczowym transient. Sáowem tym nale y równie oznaczyü
    pola, których klasy nie s serializowalne. Pola oznaczone jako transient s zawsze pomi-
    jane w procesie serializacji.
68   Java. Techniki zaawansowane


     Mechanizm serializacji na platformie Java udost pnia sposób, dzi ki któremu indywidualne
     klasy mog sprawdzaü prawidáowo ü danych lub w jakikolwiek inny sposób wpáywaü na
     zachowanie strumienia podczas domy lnych operacji odczytu i zapisu. Klasa implementuj ca
     interfejs Serializable mo e zdefiniowaü metody o sygnaturach
        private   void readObject(ObjectInputStream in)
         throws   IOException, ClassNotFoundException;
        private   void writeObject(ObjectOutputStream out)
         throws   IOException;

     Dzi ki temu obiekty nie b d automatycznie serializowane — Java wywoáa dla nich powy sze
     metody.

     A oto typowy przykáad. Wielu klas nale cych do pakietu java.awt.geom, takich jak na przykáad
     klasa Point2D.Double, nie da si serializowaü. Przypu ümy zatem, e chcemy serializowaü klas
     LabeledPoint zawieraj c pola typu String i Point2D.Double. Najpierw musimy oznaczyü pole
     Point2D.Double sáowem kluczowym transient, aby unikn ü wyj tku NotSerializableEx
     ´ception.
        public class LabeledPoint implements Serializable
        {
           . . .
           private String label;
           private transient Point2D.Double point;
        }

     W metodzie writeObject najpierw zapiszemy opis obiektu i pole typu String, wywoáuj c
     metod defaultWriteObject. Jest to specjalna metoda klasy ObjectOutputStream, która mo e
     byü wywoáywana jedynie przez metod writeObject klasy implementuj cej interfejs Seria
     ´lizable. Nast pnie zapiszemy wspóárz dne punktu, u ywaj c standardowych wywoáa klasy
     DataOutput.
        private void writeObject(ObjectOutputStream out)
           throws IOException
        {
           out.defaultWriteObject();
           out.writeDouble(point.getX());
           out.writeDouble(point.getY());
        }

     Implementuj c metod readObject, odwrócimy caáy proces:
        private void readObject(ObjectInputStream in)
           throws IOException
        {
           in.defaultReadObject();
           double x = in.readDouble();
           double y = in.readDouble();
           point = new Point2D.Double(x, y);
        }

     Innym przykáadem jest klasa java.util.Date, która dostarcza wáasnych metod readObject
     i writeObject. Metody te zapisuj dat jako liczb milisekund, które upáyn áy od póánocy 1 sty-
     cznia 1970 roku, czasu UTC. Klasa Date stosuje skomplikowan reprezentacj wewn trzn ,
RozdziaÄ 1.   Q   Strumienie i pliki   69


która przechowuje zarówno obiekt klasy Calendar, jak i licznik milisekund, co pozwala
zoptymalizowaü operacje wyszukiwania. Stan obiektu klasy Calendar jest wtórny i nie musi
byü zapisywany.

Metody readObject i writeObject odczytuj i zapisuj jedynie dane wáasnej klasy. Nie zajmuj
si przechowywaniem i odtwarzaniem danych klasy bazowej b d jakichkolwiek innych infor-
macji o klasie.

Klasa mo e równie zdefiniowaü wáasny mechanizm zapisywania danych, nie ogl daj c si na
serializacj . Aby tego dokonaü, klasa musi zaimplementowaü interfejs Externalizable. Oznacza
to implementacj dwóch metod:
   public void readExternal(ObjectInputSream in)
    throws IOException, ClassNotFoundException;
   public void writeExternal(ObjectOutputStream out)
    throws IOException;

W przeciwie stwie do omówionych wcze niej metod readObject i writeObject, te metody s
caákowicie odpowiedzialne za zapisanie i odczytanie obiektu, á cznie z danymi klasy bazowej.
Mechanizm serializacji zapisuje jedynie klas obiektu w strumieniu. Odtwarzaj c obiekt imple-
mentuj cy interfejs Externalizable, strumie obiektów wywoáuje domy lny konstruktor, a na-
st pnie metod readExternal. Oto jak mo emy zaimplementowaü te metody w klasie Employee:
   public void readExternal(ObjectInput s)
      throws IOException
   {
      name = s.readUTF();
      salary = s.readDouble();
      hireDay = new Date(s.readLong());
   }

   public void writeExternal(ObjectOutput s)
      throws IOException
   {
     s.writeUTF(name);
     s.writeDouble(salary);
     s.writeLong(hireDay.getTime());
   }

         Serializacja jest do è powolnym mechanizmem, poniewa maszyna wirtualna musi
         rozpoznaè strukturö ka dego obiektu. Je eli zale y nam na efektywno ci dziaäania,
   a jednocze nie chcemy wczytywaè i zapisywaè du ñ liczbö obiektów pewnej klasy, powin-
   ni my rozwa yè mo liwo è zastosowania interfejsu Externalizable. Artykuä na stronie
   http://java.sun.com/developer/TechTips/2000/tt0425.html stwierdza, e w przypadku
   klasy reprezentujñcej pracowników skorzystanie z wäasnych metod zapisu i odczytu byäo
   o 35 – 40% szybsze ni domy lna serializacja.


        W przeciwieþstwie do metod writeObject i readObject, które sñ prywatne i mogñ
        zostaè wywoäane wyäñcznie przez mechanizm serializacji, metody writeExternal i read
   ´External sñ publiczne. W szczególno ci metoda readExternal potencjalnie mo e byè
   wykorzystana do modyfikacji stanu istniejñcego obiektu.
70   Java. Techniki zaawansowane


Serializacja singletonów i wyliczeÆ
     Szczególn uwag nale y zwróciü na serializacj obiektów, które z zaáo enia maj byü uni-
     kalne. Ma to miejsce w przypadku implementacji singletonów i wylicze .

     Je li u ywamy w programach konstrukcji enum wprowadzonej w Java SE 5.0, to nie musimy
     przejmowaü si serializacj — wszystko b dzie dziaáaü poprawnie. Zaáó my jednak, e mamy
     starszy kod, który tworzy typy wyliczeniowe w nast puj cy sposób:
        public class Orientation
        {
           public static final Orientation HORIZONTAL = new Orientation(1);
           public static final Orientation VERTICAL = new Orientation(2);
           private Orientation(int v) { value = v; }
           private int value;
        }

     Powy szy sposób zapisu byá powszechnie stosowany, zanim wprowadzono typ wyliczeniowy
     w j zyku Java. Zwróümy uwag , e konstruktor klasy Orientation jest prywatny. Dzi ki temu
     powstan jedynie obiekty Orientation.HORIZONTAL i Orientation.VERTICAL. Obiekty tej klasy
     mo emy porównywaü za pomoc operatora ==:
        if (orientation == Orientation.HORIZONTAL) . . .

     Je li taki typ wyliczeniowy implemenuje interfejs Serializable, to domy lny sposób seria-
     lizacji oka e si w tym przypadku niewáa ciwy. Przypu ümy, e zapisali my warto ü typu
     Orientation i wczytujemy j ponownie:
        Orientation original = Orientation.HORIZONTAL;
        ObjectOutputStream out = . . .;
        out.write(value);
        out.close();
        ObjectInputStream in = . . .;
        Orientation saved = (Orientation) in.read();

     Oka e si , e porównanie
        if (saved == Orientation.HORIZONTAL) . . .

     da wynik negatywny. W rzeczywisto ci bowiem warto ü saved jest zupeánie nowym obiek-
     tem typu Orientation i nie jest ona równa adnej ze staáych wst pnie zdefiniowanych przez
     t klas . Mimo e konstruktor klasy jest prywatny, mechanizm serializacji mo e tworzyü
     zupeánie nowe obiekty tej klasy!

     Aby rozwi zaü ten problem, musimy zdefiniowaü specjaln metod serializacji o nazwie
     readResolve. Je li metoda readResolve jest zdefiniowana, zostaje wywoáana po deserializacji
     obiektu. Musi ona zwróciü obiekt, który nast pnie zwróci metoda readObject. W naszym przy-
     káadzie metoda readResolve sprawdzi pole value i zwróci odpowiedni staá :
        protected Object readResolve() throws ObjectStreamException
        {
           if (value == 1) return Orientation.HORIZONTAL;
           if (value == 2) return Orientation.VERTICAL;
           return null; // to nie powinno siú zdarzyè
        }
RozdziaÄ 1.   Q   Strumienie i pliki   71


      Musimy zatem pami taü o zdefiniowaniu metody readResolve dla wszystkich wylicze kon-
      struowanych w tradycyjny sposób i wszystkich klas implementuj cych wzorzec singletonu.


Wersje
      Je li u ywamy serializacji do przechowywania obiektów, musimy zastanowiü si , co si z nimi
      stanie, gdy powstan nowe wersje programu. Czy wersja 1.1 b dzie potrafiáa czytaü starsze
      pliki? Czy u ytkownicy wersji 1.0 b d mogli wczytywaü pliki tworzone przez now wersj ?

      Na pierwszy rzut oka wydaje si to niemo liwe. Wraz ze zmian definicji klasy zmienia si
      kod SHA, a strumie obiektów nie odczyta obiektu o innym „odcisku palca”. Jednak e klasa
      mo e zaznaczyü, e jest kompatybilna ze swoj wcze niejsz wersj . Aby tego dokonaü, mu-
      simy pobraü „odcisk palca” wcze niejszej wersji tej klasy. Do tego celu u yjemy serialver,
      programu b d cego cz ci JDK. Na przykáad, uruchamiaj c
          serialver Employee

      otrzymujemy:
          Employee:      static final long serialVersionUID =
          -876875122904779311L

      Je eli uruchomimy serialver z opcj -show, program wy wietli okno dialogowe (rysunek 1.8).

Rysunek 1.8.
Wersja graficzna
programu serialver


      Wszystkie pó niejsze wersje tej klasy musz definiowaü staá serialVersionUID o tym samym
      „odcisku palca”, co wersja oryginalna.
          class Employee implements Serializable // wersja 1.1
          {
              . . .
              public static final long serialVersionUID = -1814239825517340645L;
          }

      Klasa posiadaj ca statyczne pole skáadowe o nazwie serialVersionUID nie obliczy wáasnego
      „odcisku palca”, ale skorzysta z ju istniej cej warto ci.

      Od momentu, gdy w danej klasie umie cisz powy sz staá , system serializacji b dzie mógá
      odczytywaü ró ne wersje obiektów tej samej klasy.

      Je li zmieni si tylko metody danej klasy, sposób odczytu danych nie ulegnie zmianie. Jednak e
      je eli zmieni si pole skáadowe, mo emy mieü pewne problemy. Dla przykáadu, stary obiekt
      mo e posiadaü wi cej lub mniej pól skáadowych ni aktualny, albo te typy danych mog si
      ró niü. W takim wypadku strumie obiektów spróbuje skonwertowaü obiekt na aktualn
      wersj danej klasy.
72     Java. Techniki zaawansowane


       Strumie obiektów porównuje pola skáadowe aktualnej wersji klasy z polami skáadowymi
       wersji znajduj cej si w strumieniu. Oczywi cie, strumie bierze pod uwag wyá cznie niesta-
       tyczne, nieulotne pola skáadowe. Je eli dwa pola maj te same nazwy, lecz ró ne typy, strumie
       nawet nie próbuje konwersji — obiekty s niekompatybilne. Je eli obiekt w strumieniu posiada
       pola skáadowe nieobecne w aktualnej wersji, strumie ignoruje te dodatkowe dane. Je eli aktu-
       alna wersja posiada pola skáadowe nieobecne w zapisanym obiekcie, dodatkowe zmienne
       otrzymuj swoje domy lne warto ci (null dla obiektów, 0 dla liczb i false dla warto ci
       logicznych).

       Oto przykáad. Zaáó my, e zapisali my na dysku pewn liczb obiektów klasy Employee,
       u ywaj c przy tym oryginalnej (1.0) wersji klasy. Teraz wprowadzamy now wersj 2.0 klasy
       Employee, dodaj c do niej pole skáadowe department. Rysunek 1.9 przedstawia, co si dzieje,
       gdy obiekt wersji 1.0 jest wczytywany przez program korzystaj cy z obiektów 2.0. Pole depart-
       ment otrzymuje warto ü null. Rysunek 1.10 ilustruje odwrotn sytuacj — program korzy-
       staj cy z obiektów 1.0 wczytuje obiekt 2.0. Dodatkowe pole department jest ignorowane.




Rysunek 1.9. Odczytywanie obiektu o mniejszej liczbie pól




Rysunek 1.10. Odczytywanie obiektu o wiökszej liczbie pól

       Czy ten proces jest bezpieczny? To zale y. Opuszczanie pól skáadowych wydaje si byü bez-
       bolesne — odbiorca wci posiada dane, którymi potrafi manipulowaü. Nadawanie warto ci
       null nie jest ju tak bezpieczne. Wiele klas inicjalizuje wszystkie pola skáadowe, nadaj c im
       w konstruktorach niezerowe warto ci, tak wi c metody mog byü nieprzygotowane do obsáu-
RozdziaÄ 1.   Q   Strumienie i pliki   73


       giwania warto ci null. Od projektanta klasy zale y, czy zaimplementuje w metodzie readObject
       dodatkowy kod poprawiaj cy wyniki wczytywania ró nych wersji danych, czy te doá czy
       do metod obsáug warto ci null.


Serializacja w roli klonowania
       Istnieje jeszcze jedno, ciekawe zastosowanie mechanizmu serializacji — umo liwia on áatwe
       klonowanie obiektów klas implementuj cych interfejs Serializable. Aby sklonowaü obiekt,
       po prostu zapisujemy go w strumieniu, a nast pnie odczytujemy z powrotem. W efekcie otrzy-
       mujemy nowy obiekt, b d cy dokáadn kopi istniej cego obiektu. Nie musisz zapisywaü
       tego obiektu do pliku — mo esz skorzystaü z ByteArrayOutputStream i zapisaü dane do tablicy
       bajtów.

       Kod z listingu 1.5 udowadnia, e aby otrzymaü metod clone „za darmo”, wystarczy roz-
       szerzyü klas SerialCloneable.

Listing 1.5. SerialCloneTest.java
            /**
              @version 1.20 17 Aug 1998
              @author Cay Horstmann
           */

           import java.io.*;
           import java.util.*;

           public class SerialCloneTest
           {
              public static void main(String[] args)
              {
                 Employee harry = new Employee("Harry Hacker", 35000, 1989, 10, 1);
                 // klonuje obiekt harry
                 Employee harry2 = (Employee) harry.clone();

                     // modyfikuje obiekt harry
                     harry.raiseSalary(10);

                     // teraz obiekt harry i jego klon sæ ró ne
                     System.out.println(harry);
                     System.out.println(harry2);
                 }
           }

           /**
              Klasa, której metoda clone wykorzystuje serializacjú.
           */
           class SerialCloneable implements Cloneable, Serializable
           {
              public Object clone()
              {
                 try
                 {
                     // zapisuje obiekt w tablicy bajtów
                     ByteArrayOutputStream bout = new ByteArrayOutputStream();
74   Java. Techniki zaawansowane

                     ObjectOutputStream out = new ObjectOutputStream(bout);
                     out.writeObject(this);
                     out.close();

                     // wczytuje klon obiektu z tablicy bajtów
                     ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
                     ObjectInputStream in = new ObjectInputStream(bin);
                     Object ret = in.readObject();
                     in.close();

                     return ret;
                  }
                  catch (Exception e)
                  {
                     return null;
                  }
              }
        }

        /**
              Znana ju klasa Employee,
              tym razem jako pochodna klasy SerialCloneable.
        */
        class Employee extends SerialCloneable
        {
           public Employee(String n, double s, int year, int month, int day)
           {
              name = n;
              salary = s;
              GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
              hireDay = calendar.getTime();
           }

              public String getName()
              {
                 return name;
              }

              public double getSalary()
              {
                 return salary;
              }

              public Date getHireDay()
              {
                 return hireDay;
              }

              public void raiseSalary(double byPercent)
              {
                 double raise = salary * byPercent / 100;
                 salary += raise;
              }

              public String toString()
              {
                 return getClass().getName()
RozdziaÄ 1.   Q   Strumienie i pliki   75

                +   "[name=" + name
                +   ",salary=" + salary
                +   ",hireDay=" + hireDay
                +   "]";
          }

          private String name;
          private double salary;
          private Date hireDay;
      }


   Nale y jednak byü wiadomym, e opisany sposób klonowania, jakkolwiek sprytny, zwykle
   oka e si znacznie wolniejszy ni metoda clone jawnie tworz ca nowy obiekt i kopiuj ca
   lub klonuj ca pola danych.



Zarz¾dzanie plikami
   Potrafimy ju zapisywaü i wczytywaü dane z pliku. Jednak e obsáuga plików to co wi cej ni
   tylko operacje zapisu i odczytu. Klasa File zawiera metody potrzebne do obsáugi systemu pli-
   ków na komputerze u ytkownika. Na przykáad, mo emy wykorzystaü klas File, aby spraw-
   dziü, kiedy nast piáa ostatnia modyfikacja danego pliku, oraz usun ü lub zmieniü nazw tego
   pliku. Innymi sáowy, klasy strumieni zajmuj si zawarto ci plików, natomiast klasa File
   interesuje si sposobem przechowywania pliku na dysku.

           Jak to ma czösto miejsce w jözyku Java, klasa File przyjmuje metodö najmniejszego
           wspólnego mianownika. Na przykäad, w systemie Windows mo emy sprawdziè (lub
      zmieniè) flagö „tylko do odczytu” dla danego pliku, ale mimo i mo emy równie sprawdziè,
      czy plik jest ukryty, nie mo emy ukryè go samemu, je li nie zastosujemy w tym celu
      odpowiedniej metody macierzystej.

   Najprostszy konstruktor obiektu File pobiera peán nazw pliku. Je eli nie podamy cie ki
   dost pu, Java u ywa bie cego katalogu. Na przykáad:
      File p = new File("test.txt");

   tworzy obiekt pliku o podanej nazwie, znajduj cego si w bie cym katalogu (bie cym
   katalogiem jest katalog, w którym program jest uruchamiany).

           Poniewa znak  w äaþcuchach na platformie Java jest traktowany jako poczñtek sek-
           wencji specjalnej, musimy pamiötaè, aby w cie kach dostöpu do plików systemu
      Windows u ywaè sekwencji  (np. C:Windowswin.ini). W systemie Windows mo emy
      równie korzystaè ze znaku / (np. C:/Windows/win.ini), poniewa wiökszo è systemów
      obsäugi plików Windows interpretuje znaki / jako separatory cie ki dostöpu. Jednak e nie
      zalecamy tego rozwiñzania — zachowanie funkcji systemu Windows mo e siö zmieniaè,
      a inne systemy operacyjne mogñ u ywaè jeszcze innych separatorów. Je eli piszemy apli-
      kacjö przeno nñ, powinni my u ywaè separatora odpowiedniego dla danego systemu
      operacyjnego. Jego znak jest przechowywany jako staäa File.separator.
76   Java. Techniki zaawansowane


     Wywoáanie tego konstruktora nie tworzy nowego pliku, je eli plik o danej nazwie nie istnieje.
     Tworzenie nowego pliku na podstawie obiektu File odbywa si przy u yciu jednego z kon-
     struktorów klas strumieni lub metody createNewFile klasy File. Metoda createNewFile tworzy
     nowy plik tylko pod warunkiem, e plik o podanej nazwie nie istnieje i zwraca warto ü logiczn ,
     by poinformowaü, czy operacja si udaáa.

     Z drugiej strony, gdy utworzymy obiekt typu File, dzi ki metodzie exists mo emy sprawdziü,
     czy plik o danej nazwie istnieje. Dla przykáadu, poni szy program prawie na pewno wydru-
     kuje „false”, równocze nie podaj c cie k dost pu do nieistniej cego pliku.
        import java.io.*;

        public class Test
        {
          public static void main(String args[])
          {
            File f = new File("plikktóryprawdopodobnieistnieje");
            System.out.println(f.getAbsolutePath());
            System.our.println(f.exists());
          }
        }

     Klasa File posiada jeszcze dwa inne konstruktory:
        File(String path, String name)

     tworz ce obiekt typu File o podanej nazwie i katalogu okre lonym przez parametr path (je eli
     path ma warto ü null, konstruktor tworzy obiekt File, korzystaj c z katalogu bie cego).

     Oprócz tego mo emy równie posáu yü si istniej cym ju obiektem File, wywoáuj c kon-
     struktor:
        File(File dir, String name)

     gdzie obiekt File reprezentuje katalog i, tak jak poprzednio, je eli dir ma warto ü null,
     konstruktor tworzy nowy obiekt w katalogu bie cym.

     Co ciekawe, obiekt File mo e reprezentowaü zarówno plik, jak i katalog (byü mo e dlatego,
      e system operacyjny, na którym pracowali projektanci j zyka Java, pozwalaá traktowaü
     katalogi tak, jakby byáy plikami). Aby sprawdziü, czy obiekt reprezentuje katalog, czy plik,
     korzystamy z metod isDirectory i isFile. Dziwne — w zorientowanym obiektowo systemie
     mogli my si spodziewaü osobnej klasy Directory, byü mo e rozszerzaj cej klas File.

     Aby obiekt reprezentowaá katalog, musimy podaü konstruktorowi nazw tego katalogu:
        File tempDir = new File(File.separator + "temp");

     Je eli katalog nie istnieje, mo emy utworzyü go przy pomocy metody mkdir:
        tempDir.mkdir();

     Je eli dany obiekt reprezentuje katalog, metoda list() zwróci nam tablic nazw plików znaj-
     duj cych si w tym katalogu. Program zamieszczony na listingu 1.6 korzysta ze wszystkich
     omówionych metod, aby wydrukowaü struktur dowolnego katalogu przekazanego jako argu-
     ment wywoáania w wierszu polece (áatwo mo na zmieniü ten program w klas zwracaj c
     list podkatalogów do dalszego przetwarzania).
RozdziaÄ 1.   Q   Strumienie i pliki   77


Listing 1.6. FindDirectories.java
                import java.io.*;

            /**
              * @version 1.00 05 Sep 1997
              * @author Gary Cornell
              */
            public class FindDirectories
            {
                 public static void main(String[] args)
                 {
                    // je li brak parametru wywo ania,
                    // rozpoczyna od katalogu nadrzúdnego
                    if (args.length == 0) args = new String[] { ".." };

                     try
                     {
                           File pathName = new File(args[0]);
                           String[] fileNames = pathName.list();

                           // wy wietla wszystkie pliki w katalogu
                           for (int i = 0; i < fileNames.length; i++)
                           {
                              File f = new File(pathName.getPath(), fileNames[i]);

                              // je li kolejny katalog,
                              // wywo uje rekurencyjnie metodú main
                              if (f.isDirectory())
                              {
                                 System.out.println(f.getCanonicalPath());
                                 main(new String[] { f.getPath() });
                              }
                        }
                     }
                     catch (IOException e)
                     {
                        e.printStackTrace();
                     }
                 }
            }


        Zamiast otrzymywaü list wszystkich plików w danym katalogu, mo emy u yü obiektu klasy
        FileNameFilter jako parametru metody list, aby zmniejszyü rozmiar wynikowej listy plików.
        Na list b d wtedy wpisywane tylko te obiekty, które speániaj warunki postawione przez
        interfejs FilenameFilter.

        Aby zaimplementowaü interfejs FilenameFilter, klasa musi zdefiniowaü metod o nazwie
        accept. Oto przykáad prostej klasy implementuj cej FilenameFilter, która akceptuje wyá cznie
        pliki o okre lonym rozszerzeniu:
            public class ExtensionFilter implements FilenameFilter
            {
                 public ExtensionFilter(String ext)
78   Java. Techniki zaawansowane

            {
                extension = "." + ext;
            }


            public boolean accept(File dir, String name)
            {
                return name.endsWith(extension);
            }


            private String extension;
        }

     W przypadku programów przeno nych wyspecyfikowanie nazwy pliku znajduj cego si
     w okre lonym katalogu jest znacznie trudniejsze. Jak ju o tym wspominali my, okazuje si ,
      e nawet w systemie Windows mo esz u ywaü znaku / jako separatora katalogów (mimo i
     jest to separator systemu Unix), ale inne systemy mog na to nie pozwoliü, dlatego nie pole-
     camy tego sposobu.

             Je eli tworzñc obiekt klasy File, u ywasmy znaków / jako separatorów katalogów,
             metoda getAbsolutePath zwróci cie kö dostöpu równie zawierajñcñ znaki /, co
        w systemie Windows bödzie wyglñdaè do è dziwnie. W takim przypadku warto skorzystaè
        z metody getCanonicalPath — zamienia ona znaki / na znaki .

     Lepszym pomysáem jest wykorzystanie informacji o u ywanym separatorze, przechowywanej
     przez statyczne pole skáadowe separator klasy File (w rodowisku Windows b dzie to znak
     , w rodowisku Unix znak /). Na przykáad:
        File foo = new File("Documents" + File.separator + "data.txt")

     Oczywi cie, je eli skorzystamy z drugiej wersji konstruktora File:
        File foo = new File("Documents", "data.txt");

     Java automatycznie wstawi odpowiednie separatory.

     Poni sze notki API opisuj (naszym zdaniem) najwa niejsze spo ród pozostaáych metod
     klasy File; sposób ich u ycia powinien byü oczywisty.

        java.io.File 1.0

       Q    boolean canRead()
       Q    boolean canWrite()
       Q    boolean canExecute() 6
            sprawdza, czy na pliku mog byü wykonywane operacje odczytu i zapisu oraz
            czy plik mo e byü wykonywany.
RozdziaÄ 1.   Q   Strumienie i pliki   79


Q   boolean setReadable(boolean state, boolean ownerOnly) 6
Q   boolean setWritable(boolean state, boolean ownerOnly) 6
Q   boolean setExecutable(boolean state, boolean ownerOnly) 6
    nadaje plikowi odpowiedni wáa ciwo ü. Je li parametr ownerOnly ma warto ü true,
    to wáa ciwo ü ta jest dost pna jedynie dla wáa ciciela pliku. W przeciwnym razie
    dotyczy wszystkich u ytkowników. Metoda ta zwraca warto ü true, je li operacja
    nadania wáa ciwo ci powiodáa si .
Q   static boolean createTempFile(String prefi, String sufix) 1.2
Q   static boolean createTempFile(String prefix, String sufix,
    File directory) 1.2
    tworzy tymczasowy plik w domy lnym katalogu tymczasowym systemu operacyjnego
    lub w katalogu podanym przez u ytkownika, u ywaj c przedrostka i przyrostka
    do utworzenia tymczasowej nazwy.
    Parametry: prefix         áa cuch przedrostka o dáugo ci co najmniej trzech
                              znaków.
                  sufix       opcjonalny przyrostek. Je eli ma warto ü null, u ywane
                              jest rozszerzenie .tmp.
                  directory katalog, w którym plik ma si znajdowaü. Je eli ma
                              warto ü null, plik jest tworzony w bie cym katalogu
                              roboczym.
Q   boolean delete()
    próbuje skasowaü plik; zwraca true, je eli plik zostaá skasowany; w przeciwnym
    wypadku zwraca false.
Q   void deleteOnExit()
      da, aby plik zostaá skasowany, gdy zostanie wyá czona maszyna wirtualna.
Q   boolean exists()
    zwraca true, je eli dany plik lub katalog istnieje; w przeciwnym wypadku zwraca
    false.
Q   String getAbsolutePath()
    zwraca áa cuch zawieraj cy absolutn cie k dost pu. Wskazówka: zamiast tej
    funkcji lepiej korzystaü z getCanonicalPath.
Q   File getCanonicalFile() 1.2
    zwraca obiekt klasy File zawieraj cy kanoniczn cie k dost pu do danego
    pliku. Oznacza to, e usuwane s zb dne katalogi ".", u ywany jest odpowiedni
    separator katalogów oraz zale na od systemu operacyjnego obsáuga wielkich
    i maáych liter.
Q   String getCanonicalPath()
    zwraca áa cuch zawieraj cy kanoniczn form cie ki dost pu. Oznacza to,
     e usuwane s zb dne katalogi ".", u ywany jest odpowiedni separator katalogów
    oraz zale na od systemu operacyjnego obsáuga wielkich i maáych liter.
80   Java. Techniki zaawansowane


       Q   String getName()
           zwraca áa cuch zawieraj cy nazw pliku danego obiektu File (áa cuch ten nie
           zawiera cie ki dost pu).
       Q   String getParent()
           zwraca áa cuch zawieraj cy nazw katalogu, w którym znajduje si „rodzic” danego
           obiektu File. Je eli obiekt jest plikiem, „rodzicem” jest po prostu katalog, w którym
           dany plik si znajduje. Je eli obiekt jest katalogiem, jego „rodzicem” jest jego
           katalog bazowy lub null, je eli katalog bazowy nie istnieje.
       Q   File getParentFile() 1.2
           zwraca obiekt klasy File „rodzica” danego pliku. W notce o getParent znajdziesz
           definicj „rodzica”.
       Q   String getPath()
           zwraca áa cuch zawieraj cy cie k dost pu do pliku.
       Q   boolean isDirectory()
           zwraca true, je eli obiekt reprezentuje katalog; w przeciwnym wypadku zwraca
           false.
       Q   boolean isFile()
           zwraca true, je eli obiekt reprezentuje plik — pozostaáe opcje to katalog lub
           urz dzenie.
       Q   boolean isHidden() 1.2
           zwraca true, je eli obiekt reprezentuje plik lub katalog ukryty.
       Q   long lastModified()
           zwraca dat ostatniej modyfikacji pliku (liczba milisekund od póánocy 1 stycznia
           1970 GMT) lub 0, je eli plik nie istnieje. Aby zmieniü t warto ü w obiekt Date,
           u ywamy konstruktora Date(long).
       Q   long length()
           zwraca dáugo ü pliku w bajtach lub 0, je eli plik nie istnieje.
       Q   String[] list()
           zwraca tablic áa cuchów, zawieraj cych nazwy plików i katalogów znajduj cych
           si w danym obiekcie File, lub null, je eli dany obiekt nie reprezentuje katalogu.
       Q   String[] list(FilenameFilter filter)
           zwraca tablic nazw plików i katalogów, znajduj cych si w danym obiekcie
           i speániaj cych warunki filtra, lub null, je eli nie ma takich elementów.
           Parametry: filter          u ywany obiekt typu FilenameFilter.
       Q   File[] listFiles() 1.2
           zwraca tablic obiektów klasy File, odpowiadaj cych plikom i katalogom
           znajduj cym si w danym obiekcie klasy File, lub null, je eli dany obiekt
           nie reprezentuje katalogu.
RozdziaÄ 1.   Q   Strumienie i pliki   81


Q   File[] listFiles(FilenameFilter filter) 1.2
    zwraca tablic obiektów klasy File, odpowiadaj cych plikom i katalogom
    znajduj cym si w danym obiekcie klasy File i speániaj cym warunki filtra,
    lub null, je eli nie ma takich elementów.
    Parametry: filter             u ywany obiekt typu FilenameFilter.
Q   static File[] listRoots() 1.2
    zwraca tablic obiektów klasy File odpowiadaj c dost pnym katalogom
    najwy szego poziomu (np. w systemie Windows otrzymasz obiekty klasy File
    reprezentuj ce zainstalowane dyski — zarówno dyski lokalne, jak i mapowane
    dyski sieciowe; w systemie Unix otrzymasz po prostu "/").
Q   boolean createNewFile() 1.2
    je eli plik o nazwie podanej przez obiekt pliku nie istnieje, automatycznie tworzy
    taki plik. Oznacza to, e sprawdzanie nazwy oraz tworzenie nowego pliku nie
    zostanie zakáócone przez inn dziaáalno ü systemu. Je eli udaáo si utworzyü nowy
    plik, metoda zwraca true.
Q   boolean mkdir()
    tworzy podkatalog, którego nazwa zostaáa podana przez obiekt pliku. Zwraca true,
    je eli udaáo si utworzyü katalog; w przeciwnym wypadku zwraca false.
Q   boolean mkdirs()
    w przeciwie stwie do mkdir, ta metoda tworzy równie wymagane katalogi
    po rednie. Zwraca false, je eli którykolwiek z wymaganych katalogów nie mógá
    zostaü utworzony.
Q   boolean renameTo(File newName)
    zwraca true, je eli nazwa zostaáa zmieniona; w przeciwnym wypadku zwraca
    false.
    Parametry: newName         obiekt klasy File okre laj cy now nazw pliku.
Q   boolean setLastModified(long time) 1.2
    okre la dat ostatniej modyfikacji pliku. Zwraca true, je eli zmiana si powiodáa,
    w przeciwnym wypadku zwraca false.
    Parametry: time             liczba typu long reprezentuj ca ilo ü milisekund, jakie
                                upáyn áy od póánocy 1 stycznia 1970 GMT. Metoda
                                getTime klasy Date pozwala obliczyü t warto ü.
Q   boolean setReadOnly() 1.2
    zmienia tryb pliku na „tylko do odczytu”. Zwraca true, je eli operacja si powiodáa,
    w przeciwnym wypadku zwraca false.
Q   URL toURL() 1.2
    konwertuje obiekt klasy File na plik URL.
Q   long getTotalSpace() 6
Q   long getFreeSpace() 6
82   Java. Techniki zaawansowane


       Q   long getUsableSpace() 6
           zwraca caákowity rozmiar, liczb nieprzydzielonych bajtów i liczb dost pnych
           bajtów partycji reprezentowanej przez obiekt klasy File. Je li obiekt klasy File
           nie reprezentuje partycji, metoda ta zwraca warto ü 0.

        java.io.FilenameFilter 1.0

       Q   boolean accept(File dir, String name)
           powinna zostaü tak zdefiniowana, aby zwracaáa true, je eli dany plik speánia
           warunki filtra.
           Parametry: dir             obiekt typu File reprezentuj cy katalog, w którym
                                      znajduje si dany plik.
                           name       nazwa danego pliku.



Ulepszona obsÄuga wejÊcia i wyjÊcia
     Java SE 1.4 udost pnia w pakiecie java.nio szereg nowych rozwi za poprawiaj cych obsáug
     wej cia i wyj cia w programach.

     Wspomniany pakiet obsáuguje nast puj ce rozwi zania:
       Q   kodery i dekodery zbiorów znaków,
       Q   nieblokuj ce operacje wej cia i wyj cia,
       Q   pliki mapowane w pami ci,
       Q   blokowanie dost pu do plików.

     Kodowanie i dekodowanie znaków omówili my ju w punkcie „Zbiory znaków” na stronie 35.
     Nieblokuj ce operacje wej cia i wyj cia zostan omówione w rozdziale 3., poniewa maj one
     szczególne znaczenie w przypadku komunikacji w sieci. W nast pnych podrozdziaáach zaj-
     miemy si zatem omówieniem mapowania plików w pami ci oraz blokowaniem plików.


Mapowanie plików w pamiÂci
     Wi kszo ü systemów operacyjnych oferuje mo liwo ü wykorzystania pami ci wirtualnej do
     stworzenia „mapy” pliku lub jego fragmentu w pami ci. Dost p do pliku odbywa si wtedy
     znacznie szybciej ni w tradycyjny sposób.

     Na ko cu tego podrozdziaáu zamie cili my program, który oblicza sum kontroln CRC32
     dla pliku, u ywaj c standardowych operacji wej cia i wyj cia, a tak e pliku mapowanego
     w pami ci. Na jednej i tej samej maszynie otrzymali my wyniki jego dziaáania przedstawione
     w tabeli 1.6 dla pliku rt.jar (37 MB) znajduj cego si w katalogu jre/lib pakietu JDK.
RozdziaÄ 1.   Q   Strumienie i pliki   83


Tabela 1.6. Czasy wykonywania operacji na pliku

 Metoda                             Czas
 Zwykáy strumie wej ciowy           110 sekund
 Buforowany strumie wej ciowy       9,9 sekundy
 Plik o swobodnym dost pie          162 sekundy
 Mapa pliku w pami ci               7,2 sekundy

       Jak áatwo zauwa yü, na naszym komputerze mapowanie pliku daáo nieco lepszy wynik ni
       zastosowanie buforowanego wej cia i znacznie lepszy ni u ycie klasy RandomAccessFile.

       Oczywi cie dokáadne warto ci pomiarów b d si znacznie ró niü dla innych komputerów,
       ale áatwo domy liü si , e w przypadku swobodnego dost pu do pliku zastosowanie mapo-
       wania da zawsze popraw efektywno ci dziaáania programu. Natomiast w przypadku sekwen-
       cyjnego odczytu plików o umiarkowanej wielko ci zastosowanie mapowania nie ma sensu.

       Pakiet java.nio znakomicie upraszcza stosowanie mapowania plików. Poni ej podajemy prze-
       pis na jego zastosowanie.

       Najpierw musimy uzyskaü kanaá dost pu do pliku. Kanaá jest abstrakcj stworzon dla plików
       dyskowych, pozwalaj c na korzystanie z takich mo liwo ci systemów operacyjnych jak
       mapowanie plików w pami ci, blokowanie plików czy szybki transfer danych pomi dzy pli-
       kami. Kanaá uzyskujemy, wywoáuj c metod getChannel dodan do klas FileInputStream,
       FileOutputStream i RandomAccessFile.
           FileInputStream in = new FileInputStream(. . .);
           FileChannel channel = in.getChannel();

       Nast pnie uzyskujemy z kanaáu obiekt klasy MappedByteBuffer, wywoáuj c metod map klasy
       FileChannel. Okre lamy przy tym interesuj cy nas obszar pliku oraz tryb mapowania. Dost pne
       s trzy tryby mapowania:
          Q   FileChannel.MapMode.READ_ONLY: otrzymany bufor umo liwia wyá cznie odczyt
              danych. Jakakolwiek próba zapisu do bufora spowoduje wyrzucenie wyj tku
              ReadOnlyBufferException.
          Q   FileChannel.MapMode.READ_WRITE: otrzymany bufor umo liwia zapis danych, które
              w pewnym momencie zostan równie zaktualizowane w pliku dyskowym. Nale y
              pami taü, e modyfikacje mog nie byü od razu widoczne dla innych programów,
              które mapuj ten sam plik. Dokáadny sposób dziaáania równolegáego mapowania
              tego samego pliku przez wiele programów zale y od systemu operacyjnego.
          Q   FileChannel.MapMode.PRIVATE: otrzymany bufor umo liwia zapis danych,
              ale wprowadzone w ten sposób modyfikacje pozostaj lokalne i nie s propagowane
              do pliku dyskowego.

       Gdy mamy ju bufor, mo emy czytaü i zapisywaü dane, stosuj c w tym celu metody klasy
       ByteBuffer i jej klasy bazowej Buffer.
84   Java. Techniki zaawansowane


     Bufory obsáuguj zarówno dost p sekwencyjny, jak i swobodny. Pozycja w buforze zmienia
     si na skutek wykonywania operacji get i put. Wszystkie bajty bufora mo emy przejrzeü
     sekwencyjnie na przykáad w poni szy sposób:
        while (buffer.hasRemaining())
        {
           byte b = buffer.get();
           . . .
        }

     Alternatywnie mo emy równie wykorzystaü dost p swobodny:
        for (int i = 0; i < buffer.limit(); i++)
        {
           byte b = buffer.get(i);
           . . .
        }

     Mo emy tak e czytaü tablice bajtów, stosuj c metody:
        get(byte[] bytes)
        get(byte[], int offset, int length)

     Dost pne s równie poni sze metody:
        getInt
        getLong
        getShort
        getChar
        getFloat
        getDouble

     umo liwiaj ce odczyt warto ci typów podstawowych zapisanych w pliku w postaci binarnej.
     Jak ju wyja nili my wcze niej, Java zapisuje dane w postaci binarnej, pocz wszy od najbar-
     dziej znacz cego bajta. Je li musimy przetworzyü plik, który zawiera dane zapisane od naj-
     mniej znacz cego bajta, to wystarczy zastosowaü poni sze wywoáanie:
        buffer.order(ByteOrder.LITTLE_ENDIAN);

     Aby poznaü bie cy sposób uporz dkowania bajtów w buforze, wywoáujemy:
        ByteOrder b = buffer.order()


             Ta para metod nie stosuje konwencji nazw set/get.



     Aby zapisaü warto ci typów podstawowych w buforze, u ywamy poni szych metod:
        putInt
        putLong
        putShort
        putChar
        putFloat
        putDouble

     Program przedstawiony na listingu 1.7 oblicza sum kontroln CRC32 pliku. Suma taka jest
     cz sto u ywana do kontroli naruszenia zawarto ci pliku. Uszkodzenie zawarto ci pliku powo-
RozdziaÄ 1.   Q   Strumienie i pliki   85


        duje zwykle zmian warto ci jego sumy kontrolnej. Pakiet java.util.zip zawiera klas CRC32
        pozwalaj c wyznaczyü sum kontroln sekwencji bajtów przy zastosowaniu nast puj cej
        p tli:
            CRC32 crc = new CRC32();
            while (wiúcej bajtów)
               crc.update(nastúpny bajt)
            long checksum = crc.getValue();

Listing 1.7. NIOTest.java
            import   java.io.*;
            import   java.nio.*;
            import   java.nio.channels.*;
            import   java.util.zip.*;

            /**
              * Program obliczajæcy sumú kontrolnæ CRC pliku.
              * Uruchamianie: java NIOTest nazwapliku
              * @version 1.01 2004-05-11
              * @author Cay Horstmann
              */
            public class NIOTest
            {
                 public static long checksumInputStream(String filename) throws IOException
                 {
                    InputStream in = new FileInputStream(filename);
                    CRC32 crc = new CRC32();

                     int c;
                     while ((c = in.read()) != -1)
                        crc.update(c);
                     return crc.getValue();
               }

               public static long checksumBufferedInputStream(String filename) throws
               ´IOException
               {
                  InputStream in = new BufferedInputStream(new FileInputStream(filename));
                  CRC32 crc = new CRC32();

                     int c;
                     while ((c = in.read()) != -1)
                        crc.update(c);
                     return crc.getValue();
               }

               public static long checksumRandomAccessFile(String filename) throws IOException
               {
                  RandomAccessFile file = new RandomAccessFile(filename, "r");
                  long length = file.length();
                  CRC32 crc = new CRC32();

                     for (long p = 0; p < length; p++)
                     {
                        file.seek(p);
                        int c = file.readByte();
86   Java. Techniki zaawansowane

                   crc.update(c);
                }
                return crc.getValue();
            }

            public static long checksumMappedFile(String filename) throws IOException
            {
               FileInputStream in = new FileInputStream(filename);
               FileChannel channel = in.getChannel();

                CRC32 crc = new CRC32();
                int length = (int) channel.size();
                MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
                ´length);

                for (int p = 0; p < length; p++)
                {
                   int c = buffer.get(p);
                   crc.update(c);
                }
                return crc.getValue();
            }

            public static void main(String[] args) throws IOException
            {
               System.out.println("Input Stream:");
               long start = System.currentTimeMillis();
               long crcValue = checksumInputStream(args[0]);
               long end = System.currentTimeMillis();
               System.out.println(Long.toHexString(crcValue));
               System.out.println((end - start) + " milliseconds");

                System.out.println("Buffered Input Stream:");
                start = System.currentTimeMillis();
                crcValue = checksumBufferedInputStream(args[0]);
                end = System.currentTimeMillis();
                System.out.println(Long.toHexString(crcValue));
                System.out.println((end - start) + " milliseconds");

                System.out.println("Random Access File:");
                start = System.currentTimeMillis();
                crcValue = checksumRandomAccessFile(args[0]);
                end = System.currentTimeMillis();
                System.out.println(Long.toHexString(crcValue));
                System.out.println((end - start) + " milliseconds");

                System.out.println("Mapped File:");
                start = System.currentTimeMillis();
                crcValue = checksumMappedFile(args[0]);
                end = System.currentTimeMillis();
                System.out.println(Long.toHexString(crcValue));
                System.out.println((end - start) + " milliseconds");
            }
        }
RozdziaÄ 1.   Q   Strumienie i pliki   87


        Opis dziaäania algorytmu CRC znajdziesz na stronie http://www.relisoft.com/Science/
        ´CrcMath.html.

Szczegóáy oblicze sumy kontrolnej CRC nie s dla nas istotne. Stosujemy j jedynie jako
przykáad pewnej praktycznej operacji na pliku.

Program uruchamiamy w nast puj cy sposób:
   java NIOTest nazwapliku


   java.io.FileInputStream 1.0

  Q   FileChannel getChannel() 1.4
      zwraca kanaá dost pu do strumienia.

   java.io.FileOutputStream 1.0

  Q   FileChannel getChannel() 1.4
      zwraca kanaá dost pu do strumienia.

   java.io.RandomAccessFile 1.0

  Q   FileChannel getChannel() 1.4
      zwraca kanaá dost pu do pliku.

   java.nio.channels.FileChannel 1.4

  Q   MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
      tworzy w pami ci map fragmentu pliku.
      Parametry: mode       jedna ze staáych READ_ONLY, READ_WRITE lub PRIVATE
                            zdefiniowanych w klasie FileChannel.MapMode
                   position pocz tek mapowanego fragmentu
                   size     rozmiar mapowanego fragmentu

   java.nio.Buffer 1.4

  Q   boolean hasRemaining()
      zwraca warto ü true, je li bie ca pozycja bufora nie osi gn áa jeszcze jego ko ca.
  Q   int limit()
      zwraca pozycj ko cow bufora; jest to pierwsza pozycja, na której nie s ju
      dost pne kolejne dane bufora.
88   Java. Techniki zaawansowane


        java.nio.ByteBuffer 1.4

       Q   byte get()
           pobiera bajt z bie cej pozycji bufora i przesuwa pozycj do kolejnego bajta.
       Q   byte get(int index)
           pobiera bajt o podanym indeksie.
       Q   ByteBuffer put(byte b)
           umieszcza bajt na bie cej pozycji bufora i przesuwa pozycj do kolejnego bajta.
       Q   ByteBuffer put(int index, byte b)
           umieszcza bajt na podanej pozycji bufora. Zwraca referencj do bufora.
       Q   ByteBuffer get(byte[] destination)
       Q   ByteBuffer get(byte[] destination, int offset, int length)
           wypeánia tablic bajtów lub jej zakres bajtami z bufora i przesuwa pozycj bufora
           o liczb wczytanych bajtów. Je li bufor nie zawiera wystarczaj cej liczby bajtów,
           to nie s one w ogóle wczytywane i zostaje wyrzucony wyj tek
           BufferUnderflowException. Zwracaj referencj do bufora.
           Parametry: destination wypeániana tablica bajtów
                          offset       pocz tek wypeánianego zakresu
                          length       rozmiar wypeánianego zakresu
       Q   ByteBuffer put(byte[] source)
       Q   ByteBuffer put(byte[] source, int offset, int length)
           umieszcza w buforze wszystkie bajty z tablicy lub jej zakresu i przesuwa pozycj
           bufora o liczb umieszczonych bajtów. Je li w buforze nie ma wystarczaj cego
           miejsca, to nie s zapisywane adne bajty i zostaje wyrzucony wyj tek
           BufferOverflowException. Zwraca referencj do bufora.
           Parametry: source        tablica stanowi ca ródáo bajtów zapisywanych w buforze
                           offset   pocz tek zakresu ródáa
                           length   rozmiar zakresu ródáa
       Q   Xxx getXxx()
       Q   Xxx getXxx(int index)
       Q   ByteBuffer putXxx(xxx value)
       Q   ByteBuffer putXxx(int index, xxx value)
           pobiera lub zapisuje warto ü typu podstawowego. Xxx mo e byü typu Int, Long,
           Short, Char, Float lub Double.
       Q   ByteBuffer order(ByteOrder order)
       Q   ByteOrder order()
           okre la lub pobiera uporz dkowanie bajtów w buforze. Warto ci parametru order
           jest staáa BIG_ENDIAN lub LITTLE_ENDIAN zdefiniowana w klasie ByteOrder.
RozdziaÄ 1.   Q   Strumienie i pliki   89


Struktura bufora danych
        Gdy u ywamy mapowania plików w pami ci, tworzymy pojedynczy bufor zawieraj cy caáy
        plik lub interesuj cy nas fragment pliku. Buforów mo emy równie u ywaü podczas odczytu
        i zapisu mniejszych porcji danych.

        W tym podrozdziale omówimy krótko podstawowe operacje na obiektach typu Buffer. Bufor
        jest w rzeczywisto ci tablic warto ci tego samego typu. Abstrakcyjna klasa Buffer posiada
        klasy pochodne ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer
        i ShortBuffer.

                  Klasa StringBuffer nie jest zwiñzana z omawianñ tutaj hierarchiñ klas.



        W praktyce najcz ciej u ywane s klasy ByteBuffer i CharBuffer. Na rysunku 1.11 poka-
        zali my, e bufor jest scharakteryzowany przez:
           Q    pojemno ü, która nigdy nie ulega zmianie;
           Q    pozycj wskazuj c nast pn warto ü do odczytu lub zapisu;
           Q    granic , poza któr odczyt i zapis nie maj sensu;
           Q    opcjonalny znacznik dla powtarzaj cych si operacji odczytu lub zapisu.

Rysunek 1.11.
Bufor




        Wymienione warto ci speániaj nast puj cy warunek:
                0 d znacznik d pozycja d granica d pojemno ü

        Podstawowa zasada funkcjonowania bufora brzmi: „najpierw zapis, potem odczyt”. Na pocz tku
        pozycja bufora jest równa 0, a granic jest jego pojemno ü. Nast pnie bufor jest wypeániany
        danymi za pomoc metody put. Gdy dane sko cz si lub wypeániony zostanie caáy bufor,
        pora przej ü do operacji odczytu.

        Metoda flip przenosi granic bufora do bie cej pozycji, a nast pnie zeruje pozycj . Teraz
        mo emy wywoáywaü metod get, dopóki metoda remaining zwraca warto ü wi ksz od zera
        (metoda ta zwraca ró nic granica – pozycja). Po wczytaniu wszystkich warto ci z bufora
        wywoáujemy metod clear, aby przygotowaü bufor do nast pnego cyklu zapisu. Jak áatwo si
        domy liü, metoda ta przywraca pozycji warto ü 0, a granicy nadaje warto ü pojemno ci bufora.

        Je li chcemy ponownie odczytaü bufor, u ywamy metody rewind lub metod mark/reset. Wi cej
        szczegóáów na ten temat w opisie metod zamieszczonym poni ej.
90   Java. Techniki zaawansowane


        java.nio.Buffer 1.4

       Q   Buffer clear()
           przygotowuje bufor do zapisu, nadaj c pozycji warto ü 0, a granicy warto ü równ
           pojemno ci bufora; zwraca this.
       Q   Buffer flip()
           przygotowuje bufor do zapisu, nadaj c granicy warto ü równ pozycji, a nast pnie
           zeruj c warto ü pozycji; zwraca this.
       Q   Buffer rewind()
           przygotowuje bufor do ponownego odczytu tych samych warto ci, nadaj c pozycji
           warto ü 0 i pozostawiaj c warto ü granicy bez zmian; zwraca this.
       Q   Buffer mark()
           nadaje znacznikowi warto ü pozycji; zwraca this.
       Q   Buffer reset()
           nadaje pozycji bufora warto ü znacznika, umo liwiaj c w ten sposób ponowny
           odczyt lub zapis danych; zwraca this.
       Q   int remaining()
           zwraca liczb warto ci pozostaj cych do odczytu lub zapisu; jest to ró nica pomi dzy
           warto ci granicy i pozycji.
       Q   int position()
           zwraca pozycj bufora.
       Q   int capacity()
           zwraca pojemno è bufora.

        java.nio.CharBuffer 1.4

       Q   char get()
       Q   CharBuffer get(char[] destination)
       Q   CharBuffer get(char[] destination, int offset, int length)
           zwraca jedn warto ü typu char lub zakres warto ci typu char, pocz wszy od bie cej
           pozycji bufora, która w efekcie zostaje przesuni ta za ostatni wczytan warto ü.
           Ostatnie dwie wersje zwracaj this.
       Q   CharBuffer put(char c)
       Q   CharBuffer put(char[] source)
       Q   CharBuffer put(char[] source, int offset, int length)
       Q   CharBuffer put(String source)
       Q   CharBuffer put(CharBuffer source)
           zapisuje w buforze jedn warto ü typu char lub zakres warto ci typu char,
           pocz wszy od bie cej pozycji bufora, która w efekcie zostaje przesuni ta
           za ostatni zapisan warto ü. Wszystkie wersje zwracaj this.
RozdziaÄ 1.   Q   Strumienie i pliki   91


      Q     CharBuffer read(CharBuffer destination)
            pobiera warto ci typu char z bufora i umieszcza je w buforze destination,
            do momentu a zostanie osi gni ta granica bufora destination. Zwraca this.


Blokowanie plików
    Rozwa my sytuacj , w której wiele równocze nie wykonywanych programów musi zmo-
    dyfikowaü ten sam plik. Je li pomi dzy programami nie b dzie mieü miejsca pewien rodzaj
    komunikacji, to bardzo prawdopodobne jest, e plik zostanie uszkodzony.

    Blokady plików pozwalaj kontrolowaü dost p do plików lub pewnego zakresu bajtów w pliku.
    Jednak implementacja blokad plików ró ni si istotnie w poszczególnych systemach opera-
    cyjnych i dlatego wcze niejsze wersje JDK nie umo liwiaáy stosowania takich blokad.

    Blokowanie plików nie jest wcale tak powszechne w przypadku typowych aplikacji. Wiele
    z nich przechowuje swoje dane w bazach danych, które dysponuj mechanizmami pozwala-
    j cymi na równolegáy dost p. Je li nasz program przechowuje dane w plikach, a problem
    równolegáego dost pu zaczyna zaprz taü nasz uwag , to cz sto áatwiej b dzie wáa nie sko-
    rzystaü z bazy danych zamiast projektowaü skomplikowany system blokowania plików.

    Istniej jednak sytuacje, w których blokowanie plików jest niezb dne. Zaáó my na przykáad,
     e nasza aplikacja zapisuje preferencje u ytkownika w pliku konfiguracyjnym. Je li uruchomi
    on dwie instancje aplikacji, to mo e si zdarzyü, e obie b d chciaáy zapisaü dane w pliku
    konfiguracyjnym w tym samym czasie. W takiej sytuacji pierwsza instancja powinna zablo-
    kowaü dost p do pliku. Gdy druga instancja natrafi na blokad , mo e zaczekaü na odblo-
    kowanie pliku lub po prostu pomin ü zapis danych.

    Aby zablokowaü plik, wywoáujemy metod lock lub tryLock klasy FileChannel:
          FileLock lock = channel.lock();

    lub
          FileLock lock = channel.tryLock();

    Pierwsze wywoáanie blokuje wykonanie programu do momentu, gdy blokada pliku b dzie
    dost pna. Drugie wywoáanie nie powoduje blokowania, lecz natychmiast zwraca blokad lub
    warto ü null, je li blokada nie jest dost pna. Plik pozostaje zablokowany do momentu zam-
    kni cia kanaáu lub wywoáania metody release dla danej blokady.

    Mo na równie zablokowaü dost p do fragmentu pliku za pomoc wywoáania
          FileLock lock(long start, long size, boolean exclusive)

    lub
          FileLock tryLock(long start, long size, boolean exclusive)

    Parametrowi flag nadajemy warto ü true, aby zablokowaü dost p do pliku zarówno dla operacji
    odczytu, jak i zapisu. W przypadku blokady wspóádzielonej parametr flag otrzymuje warto ü
    false, co umo liwia wielu procesom odczyt pliku, zapobiegaj c jednak uzyskaniu przez
    którykolwiek z nich wyá cznej blokady pliku. Nie wszystkie systemy operacyjne obsáuguj
92   Java. Techniki zaawansowane


     jednak blokady wspóádzielone. W takim przypadku mo emy uzyskaü blokad wyá czn , nawet
     je li dali my jedynie blokady wspóádzielonej. Metoda isShared klasy FileLock pozwala nam
     dowiedzieü si , któr z blokad otrzymali my.

             Je li zablokujemy dostöp do koþcowego fragmentu pliku, a rozmiar pliku zwiökszy
             siö poza granicö zablokowanego fragmentu, to dostöp do dodatkowego obszaru nie
        bödzie zablokowany. Aby zablokowaè dostöp do wszystkich bajtów, nale y parametrowi
        size nadaè warto è Long.MAX_VALUE.

     Nale y pami taü, e mo liwo ci blokad zale w znacznej mierze od konkretnego systemu
     operacyjnego. Poni ej wymieniamy kilka aspektów tego zagadnienia, na które warto zwróciü
     szczególn uwag :
       Q   W niektórych systemach blokady plików maj jedynie charakter pomocniczy.
           Nawet je li aplikacji nie uda si zdobyü blokady, to mo e zapisywaü dane w pliku
           „zablokowanym” wcze niej przez inn aplikacj .
       Q   W niektórych systemach nie jest mo liwe zablokowanie dost pu do mapy pliku
           w pami ci.
       Q   Blokady plików s przydzielane na poziomie maszyny wirtualnej Java. Je li zatem
           dwa programy dziaáaj na tej samej maszynie wirtualnej, to nie mog uzyskaü blokady
           tego samego pliku. Metody lock i tryLock wyrzuc wyj tek OverlappingFile
           ´LockException w sytuacji, gdy maszyna wirtualna jest ju w posiadaniu blokady
           danego pliku.
       Q   W niektórych systemach zamkni cie kanaáu zwalnia wszystkie blokady pliku
           b d ce w posiadaniu maszyny wirtualnej Java. Dlatego te nale y unikaü wielu
           kanaáów dost pu do tego samego, zablokowanego pliku.
       Q   Dziaáanie blokad plików w sieciowych systemach plików zale y od konkretnego
           systemu i dlatego nale y unikaü stosowania blokad w takich systemach.

        java.nio.channels.FileChannel 1.4

       Q   FileLock lock()
           uzyskuje wyá czn blokad pliku. Blokuje dziaáanie programu do momentu
           uzyskania blokady.
       Q   FileLock tryLock()
           uzyskuje wyá czn blokad caáego pliku lub zwraca null, je li nie mo e uzyskaü
           blokady.
       Q   FileLock lock(long position, long size, boolean shared)
       Q   FileLock tryLock(long position, long size, boolean shared)
           uzyskuje blokad dost pu do fragmentu pliku. Pierwsza wersja blokuje dziaáanie
           programu do momentu uzyskania blokady, a druga zwraca natychmiast warto ü
           null, je li nie mo e uzyskaü od razu blokady.
           Parametry: position pocz tek blokowanego fragmentu
                           size       rozmiar blokowanego fragmentu
RozdziaÄ 1.   Q   Strumienie i pliki   93


                        shared          warto ü true dla blokady wspóádzielonej, false
                                        dla wyá cznej

      java.nio.channels.FileLock 1.4

     Q   void release()
         zwalnia blokad .



WyraÑenia regularne
   Wyra enia regularne stosujemy do okre lenia wzorców wyst puj cych w áa cuchach znaków.
   U ywamy ich najcz ciej wtedy, gdy potrzebujemy odnale ü áa cuchy zgodne z pewnym
   wzorcem. Na przykáad jeden z naszych przykáadowych programów odnajdywaá w pliku HTML
   wszystkie hiperá cza, wyszukuj c áa cuchy zgodne ze wzorcem <a href= "... ">.

   Oczywi cie zapis ... nie jest wystarczaj co precyzyjny. Specyfikuj c wzorzec, musimy
   dokáadnie okre liü znaki, które s dopuszczalne. Dlatego te opis wzorca wymaga zastoso-
   wania odpowiedniej skáadni.

   Oto prosty przykáad. Z wyra eniem regularnym
      [Jj]ava.+

   mo e zostaü uzgodniony dowolny áa cuch znaków nast puj cej postaci:
     Q   Pierwsz jego liter jest J lub j.
     Q   Nast pne trzy litery to ava.
     Q   Pozostaáa cz ü áa cucha mo e zawieraü jeden lub wi cej dowolnych znaków.

   Na przykáad áa cuch "javanese" zostanie dopasowany do naszego wyra enia regularnego,
   "Core Java" ju nie.

   Aby posáugiwaü si wyra eniami regularnymi, musimy nieco bli ej poznaü ich skáadni . Na
   szcz cie na pocz tek wystarczy kilka do ü oczywistych konstrukcji.
     Q   Przez klas znaków rozumiemy zbiór alternatywnych znaków uj ty w nawiasy
         kwadratowe, na przykáad [Jj], [0-9], [A-Za-z] czy [^0-9]. Znak - oznacza zakres
         (czyli wszystkie znaki, których kody Unicode le w podanych granicach), a znak ^
         oznacza dopeánienie (wszystkie znaki oprócz podanych).
     Q   Istnieje wiele wst pnie zdefiniowanych klas znaków, takich jak d (cyfry) czy p{Sc}
         (symbol waluty w Unicode). Patrz przykáady w tabelach 1.7 i 1.8.
     Q   Wi kszo ü znaków oznacza sam siebie, tak jak znaki ava w poprzednim przykáadzie.
     Q   Symbol . oznacza dowolny znak (z wyj tkiem, byü mo e, znaków ko ca wiersza,
         co zale y od stanu odpowiedniego znacznika).
94          Java. Techniki zaawansowane


Tabela 1.7. Skäadnia wyra eþ regularnych

 SkÄadnia                         ObjaÊnienie
 Znaki
 c                                Znak c.
 unnnn, xnn, 0n, 0nn, 0nnn   Znak o kodzie, którego warto ü zostaáa podana w notacji szesnastkowej
                                  lub ósemkowej.
 t, n, r, f, a, e           Znaki steruj ce tabulatora, nowego wiersza, powrotu karetki, ko ca strony,
                                  alertu i sekwencji steruj cej.
 cc                              Znak steruj cy odpowiadaj cy znakowi c.
 Klasy znaków
 [C1C2. . .]                      Dowolny ze znaków reprezentowanych przez C1C2. . ., gdzie Ci jest znakiem,
                                  zakresem znaków (c1-c2) lub klas znaków.
 [^. . .]                         Dopeánienie klasy znaków.
 [. . .&&. . .]                   Cz ü wspólna (przeci cie) dwóch klas znaków.
 Wst pnie zdefiniowane klasy znaków
 .                                Dowolny znak oprócz ko cz cego wiersz (lub dowolny znak, je li znacznik
                                  DOTALL zostaá ustawiony).

 d                               Cyfra [0-9].
 D                               Znak, który nie jest cyfr [^0-9].
 s                               Znak odst pu [ tnrfx0B].
 S                               Znak, który nie jest odst pem.
 w                               Znak sáowa [a-zA-Z0-9_].
 W                               Znak inny ni znak sáowa.
 p{nazwa}                        Klasa znaków o podanej nazwie (patrz tabela 1.8).
 P{nazwa}                        Dopeánienie klasy znaków o podanej nazwie.
 Granice dopasowania
 ^$                               Pocz tek, koniec wej cia (lub pocz tek, koniec wiersza w trybie
                                  wielowierszowym).
 b                               Granica sáowa.
 B                               Granica inna ni sáowa.
 A                               Pocz tek wej cia.
 z                               Koniec wej cia.
 Z                               Koniec wej cia oprócz ostatniego zako czenia wiersza.
 G                               Koniec poprzedniego dopasowania.
 Kwantyfikatory
 X?                               Opcjonalnie X.
RozdziaÄ 1.   Q   Strumienie i pliki   95


Tabela 1.7. Skäadnia wyra eþ regularnych — ciñg dalszy

 SkÄadnia                       ObjaÊnienie
 X*                             X, 0 lub wi cej razy.
 X+                             X, 1 lub wi cej razy.
 X{n} X{n,} X{n,m}              X n razy, co najmniej n razy, pomi dzy n i m razy.
 Przyrostki kwantyfikatora
 ?                              Powoduje dopasowanie najmniejszej liczby wyst pie .
 +                              Powoduje dopasowanie najwi kszej liczby wyst pie , nawet kosztem
                                ogólnego powodzenia dopasowania.
 Operacje na zbiorach
 XY                             Dowolny áa cuch z X, po którym nast puje dowolny áa cuch z Y.
 X|Y                            Dowolny áa cuch z X lub Y.
 Grupowanie
 (X)                            Grupa.
 n                             Dopasowanie n-tej grupy.
 Sekwencje steruj ce
 c                             Znak c (nie mo e byü znakiem alfabetu).
 Q...E                        Cytat… dosáownie.
 (?...)                         Specjalna konstrukcja — patrz opis klasy Pattern.

Tabela 1.8. Wstöpnie zdefiniowane nazwy klas znaków

 Nazwa klasy znaków             ObjaÊnienie
 Lower                          Maáe litery ASCII [a-z]
 Upper                          Du e litery ASCII [A-Z]
 Alpha                          Litery alfabetu ASCII [A-Za-z]
 Digit                          Cyfry ASCII [0-9]
 Alnum                          Litery alfabetu b d cyfry ASCII [A-Za-z0-9]
 XDigit                         Cyfry szesnastkowe [0-9A-Fa-f]
 Print lub Graph                Znaki ASCII posiadaj ce reprezentacj graficzn (na wydruku) [x21-x7E]
 Punct                          Znaki, które nie nale do znaków alfanumerycznych, b d cyfry
                                [p{Print}&&P{Alnum}]
 ASCII                          Wszystkie znaki ASCII [x00-x7F]
 Cntrl                          Znaki steruj ce ASCII [x00-x1F]
 Blank                          Spacja lub tabulacja [t]
 Space                          Odst p [ tnrf0x0B]
96     Java. Techniki zaawansowane


Tabela 1.8. Wstöpnie zdefiniowane nazwy klas znaków — ciñg dalszy

 Nazwa klasy znaków                ObjaÊnienie
 javaLowerCase                     Maáa litera, zgodnie z wynikiem metody Character.isLowerCase()
 javaUpperCase                     Du a litera, zgodnie z wynikiem metody Character.isUpperCase()
 javaWhitespace                    Odst p, zgodnie z wynikiem metody Character.isWhiteSpace()
 javaMirrored                      Ekwiwalent wyniku metody Character.isMirrored()
 InBlok                            Blok jest nazw bloku znaków Unicode z usuni tymi spacjami, na przykáad
                                   BasicLatin lub Mongolian. List nazw bloków znajdziesz na stronach
                                   witryny http://www.unicode.org
 Kategoria lub InKategoria         Kategoria jest nazw kategorii znaków Unicode, na przykáad L (litera)
                                   czy Sc (symbol waluty). List nazw kategorii znajdziesz na stronach
                                   witryny http://www.unicode.org

          Q      speánia rol znaku specjalnego, na przykáad . oznacza znak kropki, a  znak
                lewego uko nika.
          Q     ^ i $ oznaczaj odpowiednio pocz tek i koniec wiersza.
          Q     Je li X i Y s wyra eniami regularnymi, to XY oznacza „dowolne dopasowanie do X,
                po którym nast puje dowolne dopasowanie do Y”, a X | Y „dowolne dopasowanie
                do X lub Y”.
          Q     Do wyra enia regularnego X mo emy stosowaü kwantyfikatory X+ (raz lub wi cej),
                X* (0 lub wi cej) i X? (0 lub 1).
          Q     Domy lnie kwantyfikator dopasowuje najwi ksz mo liw liczb wyst pie ,
                która gwarantuje ogólne powodzenie dopasowania. Zachowanie to mo emy
                zmodyfikowaü za pomoc przyrostka ? (dopasowanie najmniejszej liczby wyst pie )
                i przyrostka + (dopasowanie najwi kszej liczby wyst pie , nawet je li nie gwarantuje
                ono ogólnego powodzenia dopasowania).
                Na przykáad áa cuch cab mo e zostaü dopasowany do wyra enia [a-z]*ab, ale nie
                do [a-z]*+ab. W pierwszym przypadku wyra enie [a-z]* dopasuje jedynie znak c,
                wobec czego znaki ab zostan dopasowane do reszty wzorca. Jednak wyra enie
                [a-z]*+ dopasuje znaki cab, wobec czego reszta wzorca pozostanie bez dopasowania.
          Q     Grupy pozwalaj definiowaü podwyra enia. Grupy ujmujemy w znaki nawiasów
                ( ); na przykáad ([+-]?)([0-9]+). Mo emy nast pnie za daü dopasowania
                do wszystkich grup lub do wybranej grupy, do której odwoáujemy si przez n,
                gdzie n jest numerem grupy (numeracja rozpoczyna si od 1).

       A oto przykáad nieco skomplikowanego, ale potencjalnie u ytecznego wyra enia regularnego,
       które opisuje liczby caákowite zapisane dziesi tnie lub szesnastkowo:
          [+-]?[0-9]+|0[Xx][0-9A-Fa-f]+

       Niestety, skáadnia wyra e regularnych nie jest caákowicie ustandaryzowana. Istnieje zgodno ü
       w zakresie podstawowych konstrukcji, ale diabeá tkwi w szczegóáach. Klasy j zyka Java
       zwi zane z przetwarzaniem wyra e regularnych u ywaj skáadni podobnej do zastosowanej
       w j zyku Perl. Wszystkie konstrukcje tej skáadni zostaáy przedstawione w tabeli 1.7. Wi cej
RozdziaÄ 1.   Q   Strumienie i pliki   97


informacji na temat skáadni wyra e regularnych znajdziesz w dokumentacji klasy Pattern
lub ksi ce Wyra enia regularne autorstwa J.E.F. Friedla (Wydawnictwo Helion, 2001).

Najprostsze zastosowanie wyra enia regularnego polega na sprawdzeniu, czy dany áa cuch
znaków pasuje do tego wyra enia. Oto w jaki sposób zaprogramowaü taki test w j zyku Java.
Najpierw musimy utworzyü obiekt klasy Pattern na podstawie áa cucha opisuj cego wyra-
 enie regularne. Nast pnie pobraü obiekt klasy Matcher i wywoáaü jego metod matches:
   Pattern pattern = Pattern.compile(patternString);
   Matcher matcher = pattern.matcher(input);
   if (matcher.matches()) . . .

Wej cie obiektu Matcher stanowi obiekt dowolnej klasy implementuj cej interfejs Char
´Sequence, na przykáad String, StringBuilder czy CharBuffer.

Kompiluj c wzorzec, mo emy skonfigurowaü jeden lub wi cej znaczników, na przykáad:
   Pattern pattern = Pattern.compile(patternString,
      Pattern.CASE_INSENSITIVE + Pattern.UNICODE_CASE);

Obsáugiwanych jest sze ü nast puj cych znaczników:
  Q   CASE_INSENSITIVE — dopasowanie niezale nie od wielko ci liter. Domy lnie dotyczy
      to tylko znaków US ASCII.
  Q   UNICODE_CASE — zastosowany w poá czeniu z CASE_INSENSITIVE, dotyczy wszystkich
      znaków Unicode.
  Q   MULTILINE — ^ i $ oznaczaj pocz tek i koniec wiersza, a nie caáego wej cia.
  Q   UNIX_LINES — tylko 'n' jest rozpoznawany jako zako czenie wiersza podczas
      dopasowywania do ^ i $ w trybie wielowierszowym.
  Q   DOTALL — symbol . oznacza wszystkie znaki, w tym ko ca wiersza.
  Q   CANON_EQ — bierze pod uwag kanoniczny odpowiednik znaków Unicode.
      Na przykáad znak u, po którym nast puje znak ¨ (diareza), zostanie dopasowany
      do znaku ü.

Je li wyra enie regularne zawiera grupy, obiekt Matcher pozwala ujawniü granice grup. Metody:
   int start(int groupIndex)
   int end(int groupIndex)

zwracaj indeks pocz tkowy i ko cowy podanej grupy.

Dopasowany áa cuch mo emy pobraü, wywoáuj c
   String group(int groupIndex)

Grupa 0 oznacza caáe wej cie; indeks pierwszej grupy równy jest 1. Metoda groupCount zwraca
caákowit liczb grup.

Grupy zagnie d one s uporz dkowane wedáug nawiasów otwieraj cych. Na przykáad wzo-
rzec opisany wyra eniem
   ((1?[0-9]):([0-5][0-9]))[ap]m
98     Java. Techniki zaawansowane


       dla danych
           11:59am

       spowoduje, e obiekt klasy Matcher b dzie raportowaü grupy w poni szy sposób:
           Indeks grupy          Pocz tek           Koniec             àa cuch
                0                   0                 7                11:59am
                1                   0                 5                 11:59
                2                   0                 2                   11
                3                   3                 5                   59

       Program przedstawiony na listingu 1.8 umo liwia wprowadzenie wzorca, a nast pnie áa cucha,
       którego dopasowanie zostanie sprawdzone. Je li áa cuch pasuje do wzorca zawieraj cego
       grupy, to program wy wietla granice grup w postaci nawiasów, na przykáad:
            ((11):(59))am

Listing 1.8. RegExTest.java
           import java.util.*;
           import java.util.regex.*;

           /**
                 Program testujæcy zgodno è z wyra eniem regularnym.
                 Wprowad wzorzec i dopasowywany a cuch.
                 Je li wzorzec zawiera grupy, program
                 wy wietli ich granice po uzgodnieniu a cucha ze wzorcem.
                 @version 1.01 2004-05-11
                 @author Cay Horstmann
           */
           public class RegExTest
           {
              public static void main(String[] args)
              {
                 Scanner in = new Scanner(System.in);
                 System.out.println("Enter pattern: ");
                 String patternString = in.nextLine();

                   Pattern pattern = null;
                   try
                   {
                       pattern = Pattern.compile(patternString);
                   }
                   catch (PatternSyntaxException e)
                   {
                       System.out.println("Pattern syntax error");
                       System.exit(1);
                   }

                   while (true)
                   {
                      System.out.println("Enter string to match: ");
                      String input = in.nextLine();
                      if (input == null || input.equals("")) return;
                      Matcher matcher = pattern.matcher(input);
                      if (matcher.matches())
RozdziaÄ 1.   Q   Strumienie i pliki   99

                       {
                           System.out.println("Match");
                           int g = matcher.groupCount();
                           if (g > 0)
                           {
                              for (int i = 0; i < input.length(); i++)
                              {
                                 for (int j = 1; j <= g; j++)
                                    if (i == matcher.start(j))
                                       System.out.print('(');
                                 System.out.print(input.charAt(i));
                                 for (int j = 1; j <= g; j++)
                                    if (i + 1 == matcher.end(j))
                                       System.out.print(')');
                              }
                              System.out.println();
                           }
                       }
                       else
                          System.out.println("No match");
                   }
               }
           }


       Zwykle nie chcemy dopasowywaü do wzorca caáego áa cucha wej ciowego, lecz jedynie odna-
       le ü jeden lub wi cej podáa cuchów. Aby znale ü kolejne dopasowanie, u ywamy metody find
       klasy Matcher. Je li zwróci ona warto ü true, to stosujemy metody start i end w celu odnale-
       zienia dopasowanego podáa cucha.
           while (matcher.find())
           {
              int start = matcher.start();
              int end = matcher.end();
              String match = input.substring(start, end);
              . . .
           }

       Program przedstawiony na listingu 1.9 wykorzystuje powy szy mechanizm. Odnajduje on
       wszystkie hiperá cza na stronie internetowej i wy wietla je. Uruchamiaj c program, podajemy
       adres URL jako parametr w wierszu polece , na przykáad:
           java HrefMatch http://www.horstmann.com

Listing 1.9. HrefMatch.java
           import java.io.*;
           import java.net.*;
           import java.util.regex.*;

           /**
            * Program wy wietlajæcy wszystkie adresy URL na stronie WWW
            * poprzez dopasowanie wyra enia regularnego
            * opisujæcego znacznik <a href=...> júzyka HTML.
            * Uruchamianie: java HrefMatch adresURL
            * @version 1.01 2004-06-04
            * @author Cay Horstmann
            */
100   Java. Techniki zaawansowane

         public class HrefMatch
         {
            public static void main(String[] args)
            {
               try
               {
                   // pobiera URL z wiersza polece lub u ywa domy lnego
                   String urlString;
                   if (args.length > 0) urlString = args[0];
                   else urlString = "http://java.sun.com";

                    // otwiera InputStreamReader dla podanego URL
                    InputStreamReader in = new InputStreamReader(new
                    ´URL(urlString).openStream());

                    // wczytuje zawarto è do obiektu klasy StringBuilder
                    StringBuilder input = new StringBuilder();
                    int ch;
                    while ((ch = in.read()) != -1)
                       input.append((char) ch);

                    // poszukuje wszystkich wystæpie wzorca
                    String patternString = "<as+hrefs*=s*("[^"]*"|[^s>])s*>";
                    Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
                    Matcher matcher = pattern.matcher(input);

                    while (matcher.find())
                    {
                       int start = matcher.start();
                       int end = matcher.end();
                       String match = input.substring(start, end);
                       System.out.println(match);
                    }
                 }
                 catch (IOException e)
                 {
                    e.printStackTrace();
                 }
                 catch (PatternSyntaxException e)
                 {
                    e.printStackTrace();
                 }
             }
         }


      Metoda replaceAll klasy Matcher zast puje wszystkie wyst pienia wyra enia regularnego
      podanym áa cuchem. Na przykáad poni szy kod zast pi wszystkie sekwencje cyfr znakiem #:
         Pattern pattern = Pattern.compile("[0-9]+");
         Matcher matcher = pattern.matcher(input);
         String output = matcher.replaceAll("#");

      àa cuch zast puj cy mo e zawieraü referencje grup wzorca: $n zostaje zast pione przez n-t
      grup . Sekwencja $ pozwala umie ciü znak $ w zast puj cym tek cie.

      Metoda replaceFirst zast puje jedynie pierwsze wyst pienie wzorca.
RozdziaÄ 1.   Q   Strumienie i pliki   101


Klasa Pattern dysponuje równie metod split, która dzieli áa cuch wej ciowy na tablic
áa cuchów, u ywaj c dopasowa wyra enia regularnego jako granic podziaáu. Na przykáad
poni szy kod podzieli áa cuch wej ciowy na tokeny na podstawie znaków interpunkcyjnych
otoczonych opcjonalnym odst pem.
   Pattern pattern = Pattern.compile("s*p{Punct}s*");
   String[] tokens = pattern.split(input);


   java.util.regex.Pattern 1.4

  Q   static Pattern compile(String expression)
  Q   static Pattern compile(String expression, int flags)
      kompiluje áa cuch wyra enia regularnego, tworz c obiekt wzorca przyspieszaj cy
      przetwarzanie.
      Parametry: expression wyra enie regularne
                     flags       jeden lub wi cej znaczników CASE_INSENSITIVE,
                                 UNICODE_CASE, MULTILINE, UNIX_LINES, DOTALL
                                 i CANON_EQ.
  Q   Matcher matcher(CharSequence input)
      tworzy obiekt pozwalaj cy odnajdywaü dopasowania do wzorca w áa cuchu
      wej ciowym.
  Q   String[] split(CharSequence input)
  Q   String[] split(CharSequence input, int limit)
      rozbija áa cuch wej ciowy na tokeny, stosuj c wzorzec do okre lenia granic podziaáu.
      Zwraca tablic tokenów, które nie zawieraj granic podziaáu.
      Parametry: input             áa cuch rozbijany na tokeny
                     limit         maksymalna liczba utworzonych áa cuchów. Je li
                                   dopasowanych zostaáo limit - 1 granic podziaáu,
                                   to ostatni element zwracanej tablicy zawiera
                                   niepodzielon reszt áa cucha wej ciowego.
                                   Je li limit jest równy lub mniejszy od 0, to zostanie
                                   podzielony caáy áa cuch wej ciowy. Je li limit
                                   jest równy 0, to puste áa cuchy ko cz ce dane
                                   wej ciowe nie s umieszczane w tablicy

   java.util.regex.Matcher 1.4

  Q   boolean matches()
      zwraca true, je li áa cuch wej ciowy pasuje do wzorca.
  Q   boolean lookingAt()
      zwraca true, je li pocz tek áa cucha wej ciowego pasuje do wzorca.
  Q   boolean find()
102   Java. Techniki zaawansowane


        Q   boolean find(int start)
            próbuje odnale ü nast pne dopasowanie i zwraca true, je li próba si powiedzie.
            Parametry: start            indeks, od którego nale y rozpocz ü poszukiwanie
        Q   int start()
        Q   int end()
            zwraca pozycj pocz tkow dopasowania lub nast pn pozycj za dopasowaniem.
        Q   String group()
            zwraca bie ce dopasowanie.
        Q   int groupCount()
            zwraca liczb grup we wzorcu wej ciowym.
        Q   int start(int groupIndex)
        Q   int end(int groupIndex)
            zwraca pozycj pocz tkow grupy lub nast pn pozycj za grup dla danej grupy
            bie cego dopasowania.
            Parametry: groupIndex indeks grupy (warto ci indeksu rozpoczynaj si
                                    od 1) lub 0 dla oznaczenia caáego dopasowania
        Q   String group(int groupIndex)
            zwraca áa cuch dopasowany do podanej grupy.
            Parametry: groupIndex indeks grupy (warto ci indeksu rozpoczynaj si
                                      od 1) lub 0 dla oznaczenia caáego dopasowania
        Q   String replaceAll(String replacement)
        Q   String replaceFirst(String replacement)
            zwracaj áa cuch powstaáy przez zast pienie podanym áa cuchem wszystkich
            dopasowa lub tylko pierwszego dopasowania.
            Parametry: replacement áa cuch zast puj cy mo e zawieraü referencje do grup
                                     wzorca postaci $n. Aby umie ciü w áancuchu symbol $,
                                     stosujemy sekwencj $.
        Q   Matcher reset()
        Q   Matcher reset(CharSequence input)
            resetuje stan obiektu Matcher. Druga wersja powoduje przej cie obiektu Matcher
            do pracy z innymi danymi wej ciowymi. Obie wersje zwracaj this.

      W tym rozdziale omówili my metody obsáugi plików i katalogów, a tak e metody zapisywania
      informacji do plików w formacie tekstowym i binarnym i wczytywania informacji z plików
      w formacie tekstowym i binarnym, jak równie szereg ulepsze , które do obsáugi wej cia
      i wyj cia wprowadziá pakiet java.nio. W nast pnym rozdziale omówimy mo liwo ci biblioteki
      j zyka Java zwi zane z przetwarzaniem j zyka XML.

More Related Content

PDF
Ajax, JavaScript i PHP. Intensywny trening
PDF
Programowanie w języku C. Szybki start
PDF
Profesjonalne programowanie. Część 1. Zrozumieć komputer
PDF
Po prostu PHP. Techniki zaawansowane
PDF
Java. Rozmówki
PDF
mod_rewrite. Podręcznik administratora
PDF
FreeBSD 7. Instalacja i konfiguracja
PDF
Rozbudowa i naprawa serwerów
Ajax, JavaScript i PHP. Intensywny trening
Programowanie w języku C. Szybki start
Profesjonalne programowanie. Część 1. Zrozumieć komputer
Po prostu PHP. Techniki zaawansowane
Java. Rozmówki
mod_rewrite. Podręcznik administratora
FreeBSD 7. Instalacja i konfiguracja
Rozbudowa i naprawa serwerów

What's hot (20)

PDF
Czytanie kodu. Punkt widzenia twórców oprogramowania open source
PDF
Linux. Kurs. Wydanie II
PDF
Core Java Servlets i JavaServer Pages. Tom II. Wydanie II
PDF
Master Thesis - Comparative analysis of programming Environments based on Rub...
PDF
Access w biurze i nie tylko
PDF
Jak działa Linux
PDF
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
PDF
Asembler. Sztuka programowania
PDF
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
PDF
Hack Proofing XML. Edycja polska
PDF
ABC ochrony komputera przed atakami hakera
PDF
Po prostu Mac OS X 10.5 Leopard PL
PDF
Java 2. Techniki zaawansowane
PDF
J2ME. Praktyczne projekty
PDF
Windows XP PL. Ilustrowany przewodnik
PDF
Mandrake Linux
PDF
Flash CS3 Professional PL. Techniki zaawansowane. Klatka po klatce
PDF
Java 2. Podstawy
PDF
Bezpieczeństwo aplikacji tworzonych w technologii Ajax
PDF
Po prostu własny serwer internetowy
Czytanie kodu. Punkt widzenia twórców oprogramowania open source
Linux. Kurs. Wydanie II
Core Java Servlets i JavaServer Pages. Tom II. Wydanie II
Master Thesis - Comparative analysis of programming Environments based on Rub...
Access w biurze i nie tylko
Jak działa Linux
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Asembler. Sztuka programowania
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Hack Proofing XML. Edycja polska
ABC ochrony komputera przed atakami hakera
Po prostu Mac OS X 10.5 Leopard PL
Java 2. Techniki zaawansowane
J2ME. Praktyczne projekty
Windows XP PL. Ilustrowany przewodnik
Mandrake Linux
Flash CS3 Professional PL. Techniki zaawansowane. Klatka po klatce
Java 2. Podstawy
Bezpieczeństwo aplikacji tworzonych w technologii Ajax
Po prostu własny serwer internetowy
Ad

Similar to Java. Techniki zaawansowane. Wydanie VIII (20)

PDF
Java 2. Techniki zaawansowane. Wydanie II
PDF
Praktyczny kurs Java
PDF
Praktyczny kurs Java. Wydanie II
PDF
Spring. Zapiski programisty
PDF
Java. Tworzenie aplikacji sieciowych za pomocą Springa, Hibernate i Eclipse
PDF
Java. Wprowadzenie
PDF
Java. Sztuka programowania
PDF
Struktury danych i techniki obiektowe na przykładzie Javy 5.0
PDF
Java. Potrzaski
PDF
Java. Programowanie, biblioteki open-source i pomysły na nowe projekty
PDF
Java. Kompendium programisty
PDF
Java Data Objects
PDF
Perl. Zaawansowane programowanie
PDF
Efektywne programowanie w języku Java
PDF
Spring Framework. Profesjonalne tworzenie oprogramowania w Javie
PDF
Java. Aplikacje bazodanowe. Najlepsze rozwiązania
PDF
Po prostu Java 2
PDF
Efekty graficzne i animowane dla aplikacji Desktop Java. Tworzenie atrakcyjny...
PDF
Visual Basic .NET. Ćwiczenia
PDF
Aplikacje w Visual C++ 2005. Przykłady
Java 2. Techniki zaawansowane. Wydanie II
Praktyczny kurs Java
Praktyczny kurs Java. Wydanie II
Spring. Zapiski programisty
Java. Tworzenie aplikacji sieciowych za pomocą Springa, Hibernate i Eclipse
Java. Wprowadzenie
Java. Sztuka programowania
Struktury danych i techniki obiektowe na przykładzie Javy 5.0
Java. Potrzaski
Java. Programowanie, biblioteki open-source i pomysły na nowe projekty
Java. Kompendium programisty
Java Data Objects
Perl. Zaawansowane programowanie
Efektywne programowanie w języku Java
Spring Framework. Profesjonalne tworzenie oprogramowania w Javie
Java. Aplikacje bazodanowe. Najlepsze rozwiązania
Po prostu Java 2
Efekty graficzne i animowane dla aplikacji Desktop Java. Tworzenie atrakcyjny...
Visual Basic .NET. Ćwiczenia
Aplikacje w Visual C++ 2005. Przykłady
Ad

More from Wydawnictwo Helion (20)

PDF
Tworzenie filmów w Windows XP. Projekty
PDF
Blog, więcej niż internetowy pamiętnik
PDF
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
PDF
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
PDF
Makrofotografia. Magia szczegółu
PDF
Windows PowerShell. Podstawy
PDF
Java. Efektywne programowanie. Wydanie II
PDF
JavaScript. Pierwsze starcie
PDF
PowerPoint 2007 PL. Seria praktyk
PDF
Excel 2007 PL. Seria praktyk
PDF
Access 2007 PL. Seria praktyk
PDF
Word 2007 PL. Seria praktyk
PDF
Serwisy społecznościowe. Budowa, administracja i moderacja
PDF
AutoCAD 2008 i 2008 PL
PDF
Bazy danych. Pierwsze starcie
PDF
Inventor. Pierwsze kroki
PDF
Serwer SQL 2008. Administracja i programowanie
PDF
USB. Praktyczne programowanie z Windows API w C++
PDF
Microsoft Visual Studio 2008. Księga eksperta
PDF
Rewizor GT. Prowadzenie ewidencji księgowej
Tworzenie filmów w Windows XP. Projekty
Blog, więcej niż internetowy pamiętnik
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
Makrofotografia. Magia szczegółu
Windows PowerShell. Podstawy
Java. Efektywne programowanie. Wydanie II
JavaScript. Pierwsze starcie
PowerPoint 2007 PL. Seria praktyk
Excel 2007 PL. Seria praktyk
Access 2007 PL. Seria praktyk
Word 2007 PL. Seria praktyk
Serwisy społecznościowe. Budowa, administracja i moderacja
AutoCAD 2008 i 2008 PL
Bazy danych. Pierwsze starcie
Inventor. Pierwsze kroki
Serwer SQL 2008. Administracja i programowanie
USB. Praktyczne programowanie z Windows API w C++
Microsoft Visual Studio 2008. Księga eksperta
Rewizor GT. Prowadzenie ewidencji księgowej

Java. Techniki zaawansowane. Wydanie VIII

  • 1. Java. Techniki zaawansowane. Wydanie VIII Autor: Cay S. Horstmann, Gary Cornell T³umaczenie: Jaromir Senczyk ISBN: 978-83-246-1483-7 Tytu³ orygina³u: Core Java(tm), Volume II--Advanced Features: Eighth Edition Format: 172x245, stron: 1064 • Jak wykorzystaæ strumienie? • Jak stworzyæ efektowny interfejs u¿ytkownika? • Jak zapewniæ bezpieczeñstwo w tworzonych aplikacjach? Co spowodowa³o, ¿e jêzyk programowania Java zyska³ tak wielk¹ popularnoœæ? Przyczyn jest kilka: mo¿liwoœæ przenoszenia kodu miêdzy programami, wydajnoœæ i to, co programiœci lubi¹ najbardziej – mechanizm automatycznego oczyszczania pamiêci. Nie bez znaczenia jest równie¿ to, ¿e Java jest jêzykiem zorientowanym obiektowo, udostêpnia obs³ugê programowania rozproszonego oraz œwietn¹ dokumentacjê. Ponadto liczne publikacje oraz pomocna spo³ecznoœæ sprawiaj¹, ¿e Java zajmuje poczesne miejsce wœród innych jêzyków programowania. Kolejne wydanie ksi¹¿ki „Java. Techniki zaawansowane. Wydanie VIII” zosta³o zaktualizowane o wszystkie te elementy, które pojawi³y siê w wersji szóstej platformy Java Standard Edition. Dziêki tej ksi¹¿ce dowiesz siê, w jaki sposób wykorzystaæ strumienie, jak parsowaæ dokumenty XML czy te¿ w jaki sposób tworzyæ aplikacje sieciowe. Poznasz interfejs JDBC, sposób wykorzystania transakcji oraz wykonywania zapytañ SQL. Autorzy w szczegó³owy sposób poka¿¹ Ci, jak tworzyæ aplikacje z wykorzystaniem biblioteki Swing. Dodatkowo przedstawi¹, w jaki sposób zapewniæ bezpieczeñstwo w tworzonych przez Ciebie aplikacjach. Wszystkie te – oraz wiele innych – zagadnienia zostan¹ przedstawione w przystêpny i sprawdzony sposób! • Wykorzystanie strumieni • Dokumenty XML i ich wykorzystanie w jêzyku Java • Programowanie aplikacji sieciowych • Wykorzystanie interfejsu JDBC • Tworzenie aplikacji wielojêzycznych • Mo¿liwoœci pakietu Swing • Wykorzystanie biblioteki AWT • Bezpieczeñstwo w aplikacjach • Zastosowanie podpisu cyfrowego • Sposoby wykorzystania obiektów rozproszonych (RMI) Wykorzystaj zaawansowane mo¿liwoœci jêzyka Java w swoich projektach!
  • 2. Spis treÊci Przedmowa ...............................................................................................................................................11 PodziÂkowania .........................................................................................................................................15 RozdziaÄ 1. Strumienie i pliki ....................................................................................................................17 Strumienie ................................................................................................................... 17 Odczyt i zapis bajtów ............................................................................................... 18 Zoo peäne strumieni ................................................................................................ 20 ãñczenie filtrów strumieni ........................................................................................ 24 Strumienie tekstowe ..................................................................................................... 27 Zapisywanie tekstu ................................................................................................. 28 Wczytywanie tekstu ................................................................................................. 31 Zapis obiektów w formacie tekstowym ...................................................................... 31 Zbiory znaków ......................................................................................................... 35 Odczyt i zapis danych binarnych ..................................................................................... 40 Strumienie plików o swobodnym dostöpie ................................................................. 43 Strumienie plików ZIP ................................................................................................... 48 Strumienie obiektów i serializacja .................................................................................. 55 Format pliku serializacji obiektów ............................................................................. 61 Modyfikowanie domy lnego mechanizmu serializacji .................................................. 67 Serializacja singletonów i wyliczeþ ............................................................................ 70 Wersje ................................................................................................................... 71 Serializacja w roli klonowania ................................................................................... 73 Zarzñdzanie plikami ...................................................................................................... 75 Ulepszona obsäuga wej cia i wyj cia .............................................................................. 82 Mapowanie plików w pamiöci ................................................................................... 82 Struktura bufora danych .......................................................................................... 89 Blokowanie plików ................................................................................................... 91 Wyra enia regularne ..................................................................................................... 93 RozdziaÄ 2. JÂzyk XML ...........................................................................................................................103 Wprowadzenie do jözyka XML ...................................................................................... 104 Struktura dokumentu XML ..................................................................................... 106 Parsowanie dokumentów XML ..................................................................................... 109
  • 3. 4 Java. Techniki zaawansowane Kontrola poprawno ci dokumentów XML ...................................................................... 120 Definicje typów dokumentów .................................................................................. 122 XML Schema ........................................................................................................ 129 Praktyczny przykäad ............................................................................................... 131 Wyszukiwanie informacji i XPath .................................................................................. 145 Przestrzenie nazw ....................................................................................................... 151 Parsery strumieniowe ................................................................................................. 154 Wykorzystanie parsera SAX .................................................................................... 154 Wykorzystanie parsera StAX ................................................................................... 159 Tworzenie dokumentów XML ....................................................................................... 163 Tworzenie dokumentu XML za pomocñ parsera StAX ................................................ 167 Przeksztaäcenia XSL ................................................................................................... 174 RozdziaÄ 3. Programowanie aplikacji sieciowych ...............................................................................185 Poäñczenia z serwerem ............................................................................................... 185 Limity czasu gniazd ............................................................................................... 190 Adresy internetowe ............................................................................................... 191 Implementacja serwerów ............................................................................................ 193 Obsäuga wielu klientów .......................................................................................... 196 Poäñczenia czö ciowo zamkniöte ............................................................................ 200 Przerywanie dziaäania gniazd sieciowych .................................................................. 201 Wysyäanie poczty elektronicznej ................................................................................... 207 Poäñczenia wykorzystujñce URL .................................................................................... 212 URL i URI ............................................................................................................. 212 Zastosowanie klasy URLConnection do pobierania informacji ................................... 214 Wysyäanie danych do formularzy ............................................................................. 224 RozdziaÄ 4. Poľczenia do baz danych: JDBC .......................................................................................233 Architektura JDBC ...................................................................................................... 234 Typy sterowników JDBC ......................................................................................... 235 Typowe zastosowania JDBC ................................................................................... 236 Jözyk SQL .................................................................................................................. 237 Instalacja JDBC .......................................................................................................... 243 Adresy URL baz danych ......................................................................................... 243 Pliki JAR zawierajñce sterownik .............................................................................. 244 Uruchamianie bazy danych ..................................................................................... 244 Rejestracja klasy sterownika .................................................................................. 245 Nawiñzywanie poäñczenia z bazñ danych ................................................................. 246 Wykonywanie poleceþ jözyka SQL ........................................................................... 248 Zarzñdzanie poäñczeniami, poleceniami i zbiorami wyników ....................................... 251 Analiza wyjñtków SQL ............................................................................................ 252 Wypeänianie bazy danych ....................................................................................... 255 Wykonywanie zapytaþ ................................................................................................. 258 Polecenia przygotowane ........................................................................................ 259 Odczyt i zapis du ych obiektów ............................................................................... 267 Sekwencje sterujñce ............................................................................................. 269 Zapytania o wielu zbiorach wyników ........................................................................ 270 Pobieranie warto ci kluczy wygenerowanych automatycznie ...................................... 271 Przewijalne i aktualizowalne zbiory wyników zapytaþ ...................................................... 272 Przewijalne zbiory wyników ..................................................................................... 272 Aktualizowalne zbiory rekordów .............................................................................. 274
  • 4. Spis treÊci 5 Zbiory rekordów .......................................................................................................... 279 Buforowane zbiory rekordów ................................................................................... 279 Metadane .................................................................................................................. 282 Transakcje ................................................................................................................. 292 Punkty kontrolne ................................................................................................... 293 Aktualizacje wsadowe ............................................................................................ 293 Zaawansowane typy jözyka SQL ............................................................................. 295 Zaawansowane zarzñdzanie poäñczeniami .................................................................... 297 Wprowadzenie do LDAP .............................................................................................. 298 Konfiguracja serwera LDAP .................................................................................... 299 Dostöp do informacji katalogu LDAP ....................................................................... 303 RozdziaÄ 5. Internacjonalizacja .............................................................................................................315 Lokalizatory ............................................................................................................... 316 Formaty liczb .............................................................................................................. 321 Waluty .................................................................................................................. 326 Data i czas ................................................................................................................ 328 Porzñdek alfabetyczny ................................................................................................. 335 Moc uporzñdkowania ............................................................................................. 337 Rozkäad ................................................................................................................ 337 Formatowanie komunikatów ........................................................................................ 343 Formatowanie z wariantami .................................................................................... 345 Pliki tekstowe i zbiory znaków ...................................................................................... 347 Internacjonalizacja a pliki ródäowe programów ........................................................ 347 Komplety zasobów ..................................................................................................... 348 Lokalizacja zasobów .............................................................................................. 349 Pliki wäa ciwo ci ................................................................................................... 350 Klasy kompletów zasobów ..................................................................................... 351 Kompletny przykäad .................................................................................................... 353 RozdziaÄ 6. Zaawansowane moÑliwoÊci pakietu Swing ......................................................................367 Listy .......................................................................................................................... 367 Komponent JList ................................................................................................... 368 Modele list ........................................................................................................... 374 Wstawianie i usuwanie .......................................................................................... 379 Odrysowywanie zawarto ci listy .............................................................................. 381 Tabele ....................................................................................................................... 386 Najprostsze tabele ................................................................................................ 386 Modele tabel ........................................................................................................ 390 Wiersze i kolumny ................................................................................................. 394 Drzewa ...................................................................................................................... 421 Najprostsze drzewa ............................................................................................... 422 Przeglñdanie wözäów .............................................................................................. 438 Rysowanie wözäów ................................................................................................. 440 Nasäuchiwanie zdarzeþ w drzewach ........................................................................ 443 Wäasne modele drzew ........................................................................................... 450 Komponenty tekstowe ................................................................................................ 458 ledzenie zmian zawarto ci komponentów tekstowych ............................................. 459 Sformatowane pola wej ciowe ............................................................................... 463 Komponent JSpinner ............................................................................................. 479 Prezentacja HTML za pomocñ JEditorPane .............................................................. 487
  • 5. 6 Java. Techniki zaawansowane Wska niki postöpu ..................................................................................................... 494 Paski postöpu ...................................................................................................... 494 Monitory postöpu .................................................................................................. 498 Monitorowanie postöpu strumieni wej cia ............................................................... 501 Organizatory komponentów ......................................................................................... 507 Panele dzielone .................................................................................................... 507 Panele z zakäadkami .............................................................................................. 511 Panele pulpitu i ramki wewnötrzne .......................................................................... 518 Rozmieszczenie kaskadowe i sñsiadujñce ............................................................... 521 Zgäaszanie weta do zmiany wäa ciwo ci .................................................................. 529 RozdziaÄ 7. Zaawansowane moÑliwoÊci biblioteki AWT ......................................................................537 Potokowe tworzenie grafiki .......................................................................................... 538 Figury ........................................................................................................................ 540 Wykorzystanie klas obiektów graficznych ................................................................. 542 Pola .......................................................................................................................... 555 lad pödzla ................................................................................................................ 556 Wypeänienia ............................................................................................................... 564 Przeksztaäcenia ukäadu wspóärzödnych ......................................................................... 566 Przycinanie ................................................................................................................ 571 Przezroczysto è i skäadanie obrazów ............................................................................ 573 Wskazówki operacji graficznych ................................................................................... 581 Czytanie i zapisywanie plików graficznych ..................................................................... 587 Wykorzystanie obiektów zapisu i odczytu plików graficznych ...................................... 588 Odczyt i zapis plików zawierajñcych sekwencje obrazów ............................................ 592 Operacje na obrazach ................................................................................................. 598 Dostöp do danych obrazu ...................................................................................... 598 Filtrowanie obrazów ............................................................................................... 604 Drukowanie ............................................................................................................... 613 Drukowanie grafiki ................................................................................................ 614 Drukowanie wielu stron ......................................................................................... 623 Podglñd wydruku ................................................................................................... 624 Usäugi drukowania ................................................................................................. 633 Usäugi drukowania za po rednictwem strumieni ....................................................... 637 Atrybuty drukowania .............................................................................................. 638 Schowek ................................................................................................................... 644 Klasy i interfejsy umo liwiajñce przekazywanie danych ............................................. 645 Przekazywanie tekstu ............................................................................................ 646 Interfejs Transferable i formaty danych ................................................................... 650 Przekazywanie obrazów za pomocñ schowka ........................................................... 652 Wykorzystanie lokalnego schowka do przekazywania referencji obiektów ................... 657 Wykorzystanie schowka systemowego do przekazywania obiektów Java ..................... 657 Zastosowanie lokalnego schowka do przekazywania referencji obiektów .................... 661 Mechanizm „przeciñgnij i upu è” ................................................................................. 662 Przekazywanie danych pomiödzy komponentami Swing ............................................. 664 ródäa przeciñganych danych .................................................................................. 667 Cele upuszczanych danych ..................................................................................... 670 Integracja z macierzystñ platformñ ............................................................................... 678 Ekran powitalny .................................................................................................... 678 Uruchamianie macierzystych aplikacji pulpitu .......................................................... 683 Zasobnik systemowy ............................................................................................. 688
  • 6. Spis treÊci 7 RozdziaÄ 8. JavaBeans ..........................................................................................................................693 Dlaczego ziarnka? ...................................................................................................... 694 Proces tworzenia ziarnek JavaBeans ............................................................................ 696 Wykorzystanie ziarnek do tworzenia aplikacji ................................................................ 698 Umieszczanie ziarnek w plikach JAR ....................................................................... 699 Korzystanie z ziarnek ............................................................................................. 700 Wzorce nazw wäa ciwo ci ziarnek i zdarzeþ ................................................................... 705 Typy wäa ciwo ci ziarnek ............................................................................................. 709 Wäa ciwo ci proste ............................................................................................... 709 Wäa ciwo ci indeksowane ...................................................................................... 710 Wäa ciwo ci powiñzane ......................................................................................... 710 Wäa ciwo ci ograniczone ....................................................................................... 712 Klasa informacyjna ziarnka .......................................................................................... 719 Edytory wäa ciwo ci .................................................................................................... 722 Implementacja edytora wäa ciwo ci ........................................................................ 726 Indywidualizacja ziarnka .............................................................................................. 733 Implementacja klasy indywidualizacji ...................................................................... 735 Trwaäo è ziarnek JavaBeans ........................................................................................ 742 Zastosowanie mechanizmu trwaäo ci JavaBeans dla dowolnych danych ..................... 746 Kompletny przykäad zastosowania trwaäo ci JavaBeans ............................................ 752 RozdziaÄ 9. BezpieczeÆstwo .................................................................................................................763 ãadowanie klas .......................................................................................................... 764 Hierarchia klas äadowania ...................................................................................... 766 Zastosowanie procedur äadujñcych w roli przestrzeni nazw ........................................ 768 Implementacja wäasnej procedury äadujñcej ............................................................. 769 Weryfikacja kodu maszyny wirtualnej ............................................................................ 774 Mened ery bezpieczeþstwa i pozwolenia ...................................................................... 779 Bezpieczeþstwo na platformie Java ........................................................................ 781 Pliki polityki bezpieczeþstwa .................................................................................. 784 Tworzenie wäasnych klas pozwoleþ ......................................................................... 790 Implementacja klasy pozwoleþ ............................................................................... 792 Uwierzytelnianie u ytkowników ............................................................................... 798 Moduäy JAAS ......................................................................................................... 804 Podpis cyfrowy ........................................................................................................... 813 Skróty wiadomo ci ................................................................................................ 814 Podpisywanie wiadomo ci ..................................................................................... 820 Certyfikaty X.509 .................................................................................................. 822 Weryfikacja podpisu .............................................................................................. 823 Problem uwierzytelniania ....................................................................................... 825 Podpisywanie certyfikatów ..................................................................................... 827 ñdania certyfikatu ............................................................................................... 829 Podpisywanie kodu ..................................................................................................... 830 Podpisywanie plików JAR ....................................................................................... 830 Certyfikaty twórców oprogramowania ...................................................................... 835 Szyfrowanie ............................................................................................................... 837 Szyfrowanie symetryczne ....................................................................................... 837 Generowanie klucza .............................................................................................. 839 Strumienie szyfrujñce ............................................................................................ 843 Szyfrowanie kluczem publicznym ............................................................................ 844
  • 7. 8 Java. Techniki zaawansowane RozdziaÄ 10. Obiekty rozproszone .........................................................................................................851 Role klienta i serwera ................................................................................................. 852 Wywoäania zdalnych metod .......................................................................................... 854 Namiastka i szeregowanie parametrów ................................................................... 854 Model programowania RMI .......................................................................................... 856 Interfejsy i implementacje ...................................................................................... 856 Rejestr RMI .......................................................................................................... 858 Przygotowanie wdro enia ....................................................................................... 861 Rejestrowanie aktywno ci RMI ............................................................................... 864 Parametry zdalnych metod i warto ci zwracane ............................................................. 866 Przekazywanie obiektów zdalnych ........................................................................... 866 Przekazywanie obiektów, które nie sñ zdalne ........................................................... 866 Dynamiczne äadowanie klas ................................................................................... 868 Zdalne referencje obiektów o wielu interfejsach ....................................................... 873 Zdalne obiekty i metody equals, hashCode oraz clone ............................................. 874 Aktywacja zdalnych obiektów ....................................................................................... 874 Usäugi sieciowe i JAX-WS ............................................................................................. 880 Stosowanie JAX-WS ............................................................................................... 881 Klient usäugi Web .................................................................................................. 884 Usäuga Amazon ..................................................................................................... 886 RozdziaÄ 11. Skrypty, kompilacja i adnotacje .......................................................................................893 Skrypty na platformie Java .......................................................................................... 893 Wybór silnika skryptów .......................................................................................... 894 Przekierowanie wej cia i wyj cia ............................................................................. 897 Wywoäywanie funkcji i metod skryptów .................................................................... 898 Kompilacja skryptu ................................................................................................ 900 Przykäad: skrypty i graficzny interfejs u ytkownika ..................................................... 901 Interfejs kompilatora .................................................................................................. 905 Kompilacja w najprostszy sposób ........................................................................... 906 Stosowanie zadaþ kompilacji ................................................................................. 906 Przykäad: dynamiczne tworzenie kodu w jözyku Java ................................................. 911 Stosowanie adnotacji ................................................................................................. 916 Przykäad — adnotacje obsäugi zdarzeþ .......................................................................... 918 Skäadnia adnotacji ...................................................................................................... 922 Adnotacje standardowe .............................................................................................. 926 Adnotacje kompilacji ............................................................................................. 927 Adnotacje zarzñdzania zasobami ............................................................................ 928 Metaadnotacje ...................................................................................................... 928 Przetwarzanie adnotacji w kodzie ródäowym ................................................................. 931 In ynieria kodu bajtowego ........................................................................................... 937 Modyfikacja kodu bajtowego podczas äadowania ...................................................... 943 RozdziaÄ 12. Metody macierzyste .........................................................................................................947 Wywoäania funkcji jözyka C z programów w jözyku Java .................................................. 948 Numeryczne parametry metod i warto ci zwracane ........................................................ 954 Wykorzystanie funkcji printf do formatowania liczb ................................................... 955 ãaþcuchy znaków jako parametry ................................................................................. 956 Dostöp do skäadowych obiektu .................................................................................... 961 Dostöp do pól instancji .......................................................................................... 962 Dostöp do pól statycznych ..................................................................................... 965
  • 8. Spis treÊci 9 Sygnatury .................................................................................................................. 966 Wywoäywanie metod jözyka Java .................................................................................. 967 Wywoäywanie metod obiektów ................................................................................ 968 Wywoäywanie metod statycznych ............................................................................. 972 Konstruktory ......................................................................................................... 973 Alternatywne sposoby wywoäywania metod .............................................................. 973 Tablice ...................................................................................................................... 975 Obsäuga bäödów ......................................................................................................... 978 Interfejs programowy wywoäaþ jözyka Java .................................................................... 983 Kompletny przykäad: dostöp do rejestru systemu Windows ............................................. 988 Rejestr systemu Windows ...................................................................................... 988 Interfejs dostöpu do rejestru na platformie Java ...................................................... 990 Implementacja dostöpu do rejestru za pomocñ metod macierzystych ........................ 990 Skorowidz ............................................................................................................................................1005
  • 9. 1 Strumienie i pliki W tym rozdziale: Q strumienie, Q strumienie tekstowe, Q odczyt i zapis danych binarnych, Q strumienie plików ZIP, Q strumienie obiektów i serializacja, Q zarz dzanie plikami, Q ulepszona obsáuga wej cia i wyj cia, Q wyra enia regularne. W tym rozdziale omówimy metody obsáugi plików i katalogów, a tak e metody zapisywa- nia do i wczytywania informacji z plików w formacie tekstowym i binarnym. W rozdziale przedstawiony jest równie mechanizm serializacji obiektów, który umo liwia przechowy- wanie obiektów z tak áatwo ci , z jak przechowujesz tekst i dane numeryczne. Nast pnie omówimy szereg ulepsze , które do obsáugi wej cia i wyj cia wprowadziá pakiet java.nio udost pniony w wersji Java SE 1.4. Rozdziaá zako czymy przedstawieniem problematyki wyra e regularnych, mimo e nie jest ona bezpo rednio zwi zana ze strumieniami i pli- kami. Nie potrafili my jednak znale ü dla niej lepszego miejsca w ksi ce. W naszym wybo- rze nie byli my zreszt osamotnieni, poniewa zespóá Javy doá czyá specyfikacj interfejsów programowych zwi zanych z przetwarzaniem wyra e regularnych do specyfikacji ulepszo- nej obsáugi wej cia i wyj cia w Java SE 1.4. Strumienie W j zyku Java obiekt, z którego mo emy odczytaü sekwencj bajtów, nazywamy strumieniem wej cia. Obiekt, do którego mo emy zapisaü sekwencj bajtów, nazywamy strumieniem wyj cia. ródáem b d celem tych sekwencji bajtów mog byü, i cz sto wáa nie s , pliki,
  • 10. 18 Java. Techniki zaawansowane ale tak e i poá czenia sieciowe, a nawet bloki pami ci. Klasy abstrakcyjne InputStream i Out ´putStream stanowi baz hierarchii klas opisuj cych wej cie i wyj cie programów Java. Poniewa strumienie binarne nie s zbyt wygodne do manipulacji danymi przechowywanymi w standardzie Unicode (przypomnijmy tutaj, e Unicode opisuje ka dy znak za pomoc dwóch bajtów), stworzono osobn hierarchi klas operuj cych na znakach Unicode i dziedzicz cych po klasach abstrakcyjnych Reader i Writer. Klasy te s przystosowane do wykonywania ope- racji odczytu i zapisu, opartych na dwubajtowych znakach Unicode, nie przydaj si nato- miast do znaków jednobajtowych. Odczyt i zapis bajtów Klasa InputStream posiada metod abstrakcyjn : abstract int read() Metoda ta wczytuje jeden bajt i zwraca jego warto ü lub –1, je eli natrafi na koniec ródáa danych. Projektanci konkretnych klas strumieni wej cia przeáadowuj t metod , dostarcza- j c w ten sposób u ytecznej funkcjonalno ci. Dla przykáadu, w klasie FileInputStream metoda read czyta jeden bajt z pliku. System.in to predefiniowany obiekt klasy pochodnej od Input ´Stream, pozwalaj cy pobieraü informacje z klawiatury. Klasa InputStream posiada równie nieabstrakcyjn metod pozwalaj c pobraü lub zigno- rowaü tablic bajtów. Metody te wywoáuj abstrakcyjna metod read, tak wi c podklasy musz przeáadowaü tylko t jedn metod . Analogicznie, klasa OutputStream definiuje metod abstrakcyjn abstract void write(int b) która wysyáa jeden bajt do aktualnego wyj cia. Metody read i write potrafi zablokowaü w tek, dopóki dany bajt nie zostanie wczytany lub zapisany. Oznacza to, e je eli strumie nie mo e natychmiastowo wczytaü lub zapisaü danego bajta (zazwyczaj z powodu powolnego poá czenia sieciowego), Java zawiesza w tek doko- nuj cy wywoáania. Dzi ki temu inne w tki mog wykorzystaü czas procesora, w którym wywoáana metoda czeka na udost pnienie strumienia. Metoda available pozwala sprawdziü liczb bajtów, które w danym momencie odczytaü. Oznacza to, e poni szy kod prawdopodobnie nigdy nie zostanie zablokowany: int bytesAvaible = System.in.available(); if (bytesAvaible > 0) { byte[] dane = new byte[bytesAvaible]; System.in.read(data); } Gdy sko czymy odczytywaü albo zapisywaü dane do strumienia, zamykamy go, wywoáuj c metod close. Metoda ta uwalnia zasoby systemu operacyjnego, do tej pory udost pnione w tkowi. Je eli aplikacja otworzy zbyt wiele strumieni, nie zamykaj c ich, zasoby systemu
  • 11. RozdziaÄ 1. Q Strumienie i pliki 19 mog zostaü naruszone. Co wi cej, zamkni cie strumienia wyj cia powoduje opró nienie bufora u ywanego przez ten strumie — wszystkie znaki, przechowywane tymczasowo w buforze, aby mogáy zostaü zapisane w jednym wi kszym pakiecie, zostan natychmiast wysáane. Je eli nie zamkniemy strumienia, ostatni pakiet bajtów mo e nigdy nie dotrzeü do odbiorcy. Bufor mo emy równie opró niü wáasnor cznie, przy u yciu metody flush. Mimo i klasy strumieni udost pniaj konkretne metody wykorzystuj ce funkcje read i write, programi ci Javy rzadko z nich korzystaj , poniewa niecz sto si zdarza, eby programy musiaáy czytaü i zapisywaü sekwencje bajtów. Dane, którymi jeste my zwykle bardziej zain- teresowani, to liczby, áa cuchy znaków i obiekty. Java udost pnia wiele klas strumieni pochodz cych od podstawowych klas InputStream i OutputStream, które pozwalaj operowaü na danych w sposób bardziej dogodny ani eli w przypadku pracy na poziomie pojedynczych bajtów. java.io.InputStream 1.0 Q abstract int read() pobiera jeden bajt i zwraca jego warto ü. Metoda read zwraca –1, gdy natrafi na koniec strumienia. Q int read(byte[] b) wczytuje dane do tablicy i zwraca liczb wczytanych bajtów, a je eli natrafi na koniec strumienia, zwraca –1. Metoda read czyta co najwy ej b.length bajtów. Q int read(byte[] b, int off, int len) wczytuje dane do tablicy bajtów. Zwraca liczb wczytanych bajtów, a je eli natrafi na koniec strumienia, zwraca –1. Parametry: b tablica, w której zapisywane s dane. off indeks tablicy b, pod którym powinien zostaü umieszczony pierwszy wczytany bajt. len maksymalna liczba wczytywanych bajtów. Q long skip(long n) ignoruje n bajtów w strumieniu wej cia. Zwraca faktyczn liczb zignorowanych bajtów (która mo e byü mniejsza ni n, je eli natrafimy na koniec strumienia). Q int available() zwraca liczb bajtów dost pnych bez konieczno ci zablokowania w tku (pami tajmy, e zablokowanie oznacza, e wykonanie aktualnego w tku zostaje wstrzymane). Q void close() zamyka strumie wej cia. Q void mark(int readlimit) ustawia znacznik na aktualnej pozycji strumienia wej cia (nie wszystkie strumienie obsáuguj t mo liwo ü). Je eli ze strumienia zostaáo pobranych wi cej ni readlimit bajtów, strumie ma prawo usun ü znacznik.
  • 12. 20 Java. Techniki zaawansowane Q void reset() wraca do ostatniego znacznika. Pó niejsze wywoáania read b d powtórnie czytaü pobrane ju bajty. Je eli znacznik nie istnieje, strumie nie zostanie zresetowany. Q boolean markSupported() zwraca true, je eli strumie obsáuguje znaczniki. java.io.OutputStream 1.0 Q abstract void write(int n) zapisuje jeden bajt. Q void write(byte[] b) Q void write(byte[] b, int off, int len) zapisuj wszystkie bajty tablicy b lub pewien ich zakres. Parametry: b tablica, z której pobierane s dane. off indeks tablicy b, spod którego powinien zostaü pobrany pierwszy zapisywany bajt. len liczba zapisywanych bajtów. Q void close() opró nia i zamyka strumie wyj cia. Q void flush() opró nia strumie wyj cia, czyli wysyáa do odbiorcy wszystkie dane znajduj ce si w buforze. Zoo peÄne strumieni W przeciwie stwie do j zyka C, który w zupeáno ci zadowala si jednym typem FILE*, Java posiada istne zoo ponad 60 (!) ró nych typów strumieni (patrz rysunki 1.1 i 1.2). Podzielmy gatunki nale ce do zoo klas strumieni zale nie od ich przeznaczenia. Istniej osobne hierarchie klas przetwarzaj cych bajty i znaki. Jak ju o tym wspomnieli my, klasy InputStream i OutputStream pozwalaj pobieraü i wysy- áaü jedynie pojedyncze bajty oraz tablice bajtów. Klasy te stanowi baz hierarchii pokazanej na rysunku 1.1. Do odczytu i zapisu liczb i áa cuchów znakowych u ywamy ich podklas. Na przykáad, DataInputStream i DataOutputStream pozwalaj wczytywaü i zapisywaü wszyst- kie podstawowe typy Javy w postaci binarnej. Istnieje wiele po ytecznych klas strumieni, na przykáad ZipInputStream i ZipOutputStream pozwalaj ce odczytywaü i zapisywaü dane w plikach skompresowanych w formacie ZIP. Z drugiej strony, o czym ju wspominali my, do obsáugi tekstu Unicode u ywamy klas pocho- dz cych od klas abstrakcyjnych Reader i Writer (patrz rysunek 1.2) Podstawowe metody klas Reader i Writer s podobne do tych nale cych do InputStream i OutputStream.
  • 13. RozdziaÄ 1. Q Strumienie i pliki 21 Rysunek 1.1. Hierarchia strumieni wej cia i wyj cia abstract int read() abstract void write(int b) Metoda read zwraca albo kod znaku Unicode (jako liczb z przedziaáu od 0 do 65535), albo –1, je eli natrafi na koniec pliku. Metoda write jest wywoáywana dla podanego kodu znaku Unicode (wi cej informacji na temat kodów Unicode znjadziesz w rozdziale 3. ksi ki Java 2. Podstawy).
  • 14. 22 Java. Techniki zaawansowane Rysunek 1.2. Hierarchia klas Reader i Writer Pocz wszy od Java SE 5.0, wprowadzono cztery dodatkowe interfejsy: Closeable, Flushable, Readable i Appendable (patrz rysunek 1.3). Pierwsze dwa z nich s wyj tkowo proste i zawie- raj odpowiednio metody: void close() throws IOException i void flush() Klasy InputStream, OutputStream, Reader i Writer implementuj interfejs Closeable. Klasy OutputStream i Writer implementuj interfejs Flushable. Interfejs Readable ma tylko jedn metod int read(CharBuffer cb) Klasa CharBuffer ma metody do sekwencyjnego oraz swobodnego odczytu i zapisu. Repre- zentuje ona bufor w pami ci lub map pliku w pami ci. (Patrz punkt „Struktura bufora da- nych” na stronie 89). Interfejs Appendable ma dwie metody umo lwiaj ce dopisywanie pojedynczego znaku b d sekwencji znaków: Appendable append(char c) Appendable append(CharSequence s)
  • 15. RozdziaÄ 1. Q Strumienie i pliki 23 Rysunek 1.3. Interfejsy Closeable, Flushable, Readable i Appendable Interfejs CharSequence opisuje podstawowe wáa ciwo ci sekwencji warto ci typu char. Inter- fejs ten implementuj klasy String, CharBuffer, StringBuilder i StringBuffer. Spo ród klas strumieni jedynie klasa Writer implementuje interfejs Appendable. java.io.Closeable 5.0 Q void close() zamyka obiekt implemetuj cy interfejs Closeable. Mo e wyrzuciü wyj tek IOException. java.io.Flushable 5.0 Q void flush() opró nia bufor danych zwi zany z obiektem implementuj cym interfejs Flushable. java.lang.Readable 5.0 Q int read(CharBuffer cb) próbuje wczytaü tyle warto ci typu char, ile mo e pomie ciü cb. Zwraca liczb wczytanych warto ci lub -1, je li obiekt Readable nie ma ju warto ci do pobrania. java.lang.Appendable 5.0 Q Appendable append(char c)
  • 16. 24 Java. Techniki zaawansowane Q Appendable append(CharSequence cs) dopisuje podany kod znaku lub wszystkie kody podanej sekwencji do obiektu Appendable; zwraca this. java.lang.CharSequence 1.4 Q char charAt(int index) zwraca kod o podanym indeksie. Q int length() zwraca liczb kodów w sekwencji. Q CharSequence subSequence(int startIndex, int endIndex) zwraca sekwencj CharSequence záo on z kodów od startIndex do endIndex - 1. Q String toString() zwraca áa cuch znaków skáadaj cy si z kodów danej sekwencji. þczenie filtrów strumieni Klasy FileInputStream i FileOutputStream obsáuguj strumienie wej cia i wyj cia przypo- rz dkowane okre lonemu plikowi na dysku. W konstruktorze tych klas podajemy nazw pliku lub peán cie k dost pu do niego. Na przykáad FileInputStream fin = new FileInputStream("employee.dat"); spróbuje odszukaü w aktualnym katalogu plik o nazwie employee.dat. Poniewa wszystkie klasy w java.io uznajñ relatywne cie ki dostöpu za rozpoczy- najñce siö od aktualnego katalogu roboczego, powiniene wiedzieè, co to za kata- log. Mo esz pobraè tö informacjö poleceniem System.getProperty("user.dir"). Tak jak klasy abstrakcyjne InputStream i OutputStream, powy sze klasy obsáuguj odczyt i zapis plików na poziomie pojedynczego bajta. Oznacza to, e z obiektu fin mo emy czytaü wyá cznie pojedyncze bajty oraz tablice bajtów. byte b = (byte)fin.read(); W nast pnym podrozdziale przekonamy si , e korzystaj c z DataInputStream, mogliby my wczytywaü typy liczbowe: DataInputStream din = . . .; double p = din.readDouble(); Ale tak jak FileInputStream nie posiada metod czytaj cych typy liczbowe, tak DataInput ´Stream nie posiada metody pozwalaj cej czytaü dane z pliku. Java korzysta ze sprytnego mechanizmu rozdzielaj cego te dwa rodzaje funkcjonalno ci. Niektóre strumienie (takie jak FileInputStream i strumie wej cia zwracany przez metod openStream klasy URL) mog udost pniaü bajty z plików i innych, bardziej egzotycznych loka-
  • 17. RozdziaÄ 1. Q Strumienie i pliki 25 lizacji. Inne strumienie (takie jak DataInputStream i PrintWriter) potrafi tworzyü z bajtów reprezentacj bardziej u ytecznych typów danych. Programista Javy musi poá czyü te dwa mechanizmy w jeden. Dla przykáadu, aby wczytywaü liczby z pliku, powinien utworzyü obiekt typu FileInputStream, a nast pnie przekazaü go konstruktorowi DataInputStream. FileInputStream fin = new FileInputStream("employee.dat"); DataInputStream din = new DataInputStream(fin); double s = din.readDouble(); Wróümy do rysunku 1.1, gdzie przedstawione s klasy FilterInputStream i FilterOutput ´Stream. Ich podklasy mo emy wykorzystaü do rozbudowy obsáugi strumieni zwykáych bajtów. Ró ne funkcjonalno ci mo emy dodawaü poprzez zagnie d anie filtrów. Na przykáad — domy lnie strumienie nie s buforowane. Wobec tego ka de wywoáanie metody read oznacza odwoáanie si do usáug systemu operacyjnego, który odczytuje kolejny bajt. Du o efektyw- niej b dzie daü od systemu operacyjnego caáych bloków danych i umieszczaü je w buforze. Je li chcemy uzyskaü buforowany dost p do pliku, musimy skorzystaü z poni szej, mon- strualnej sekwencji konstruktorów: DataInputStream din = new DataInputStream (new BufferedInputStream (new FileInputStream("employee.dat"))); Zwróümy uwag , e DataInputStream znalazá si na ostatnim miejscu w áa cuchu konstrukto- rów, poniewa chcemy u ywaü metod klasy DataInputStream i chcemy, aby korzystaáy one z buforowanej metody read. Czasami b dziemy zmuszeni utrzymywaü á czno ü ze strumieniami znajduj cymi si po rodku áa cucha. Dla przykáadu, czytaj c dane, musimy cz sto podejrzeü nast pny bajt, aby spraw- dziü, czy jego warto ü zgadza si z naszymi oczekiwaniami. W tym celu Java dostarcza klas PushbackInputStream. PushbackInputStream pbin = new PushbackInputStream (new BufferedInputStream (new FileInputStream("employee.dat"))); Teraz mo emy odczytaü warto ü nast pnego bajta: int b = pbin.read(); i umie ciü go z powrotem w strumieniu, je eli jego warto ü nie odpowiada naszym oczeki- waniom. if (b != '<') pbin.unread(b); Ale wczytywanie i powtórne wstawianie to jedyne metody obsáugiwane przez klas Push ´back-InputStream. Je eli chcemy podejrzeü kolejne bajty, a tak e wczytywaü liczby, potrze- bujemy referencji zarówno do PushbackInputStream, jak i do DataInputStream. DataInputStream din = DataInputStream (pbin = new PushbackInputStream (new BufferedInputStream (new FileInputStream("employee.dat"))));
  • 18. 26 Java. Techniki zaawansowane Oczywi cie, w bibliotekach strumieni innych j zyków programowania takie udogodnienia jak buforowanie i kontrolowanie kolejnych bajtów s wykonywane automatycznie, wi c koniecz- no ü tworzenia ich kombinacji w j zyku Java wydaje si niepotrzebnym zawracaniem gáowy. Jednak mo liwo ü á czenia klas filtrów i tworzenia w ten sposób naprawd u ytecznych sekwencji strumieni daje nam niespotykan elastyczno ü. Na przykáad, korzystaj c z poni - szej sekwencji strumieni, mo emy wczytywaü liczby ze skompresowanego pliku ZIP (patrz rysunek 1.4). ZipInputStream zin = new ZipInputStream(new FileInputStream("employee.zip")); DataInputStream din = new DataInputStream(zin); Rysunek 1.4. Sekwencja filtrowanych strumieni Aby dowiedzieü si wi cej o obsáudze formatu ZIP, zajrzyj do podrozdziaáu po wi conego strumieniom plików ZIP na stronie 48. java.io.FileInputStream 1.0 Q FileInputStream(String name) tworzy nowy obiekt typu FileInputStream, u ywaj c pliku, którego cie ka dost pu znajduje si w áa cuchu nazwa. Q FileInputStream(File file) tworzy nowy obiekt typu FileInputStream, u ywaj c pliku, którego cie k dost pu zawiera parametr name, lub u ywaj c informacji zawartych w obiekcie file (klasa File zostanie omówiona pod koniec tego rozdziaáu). cie ki dost pu s podawane wzgl dem katalogu roboczego skonfigurowanego podczas uruchamiania maszyny wirtualnej Java. java.io.FileOutputStream 1.0 Q FileOutputStream(String name) Q FileOutputStream(String name, boolean append) Q FileOutputStream(File file) tworzy nowy obiekt typu
  • 19. RozdziaÄ 1. Q Strumienie i pliki 27 Q FileOutputStream(File file, boolean append) tworzy nowy strumie wyj ciowy pliku okre lonego za pomoc áa cucha file lub obiektu file (klasa File zostanie omówiona pod koniec tego rozdziaáu). Je eli parametr append ma warto ü true, dane doá czane s na ko cu pliku, a istniej cy plik o tej samej nazwie nie zostanie skasowany. W przeciwnym razie istniej cy plik o tej samej nazwie zostanie skasowany. java.io.BufferedInputStream 1.0 Q BufferedInputStream(InputStream in) tworzy nowy obiekt typu BufferedInputStream, o domy lnym rozmiarze bufora. Strumie buforowany wczytuje znaki ze strumienia danych, nie wymuszaj c za ka dym razem dost pu do urz dzenia. Gdy bufor zostanie opró niony, system prze le do niego nowy blok danych. java.io.BufferedOutputStream 1.0 Q BufferedOutputStream(OutputStream out) tworzy nowy obiekt typu Buffered-OutputStream, o domy lnym rozmiarze bufora. Strumie umieszcza w buforze znaki, które powinny zostaü zapisane, nie wymuszaj c za ka dym razem dost pu do urz dzenia. Gdy bufor zapeáni si lub gdy strumie zostanie opró niony, dane s przesyáane odbiorcy. java.io.PushbackInputStream 1.0 Q PushbackInputStream(InputStream in) tworzy strumie sprawdzaj cy warto ü nast pnego w kolejce bajta. Q PushbackInputStream(InputStream we, int size) tworz strumie umo liwiaj cy podgl d kolejnego bajta wraz z buforem o podanym rozmiarze. Q void unread(int b) wstawia bajt z powrotem do strumienia, dzi ki czemu przy nast pnym wywoáaniu read zostanie on ponownie odczytany. Parametry: b zwracany bajt Strumienie tekstowe Zapisuj c dane, mo emy wybieraü pomi dzy formatem binarnym i tekstowym. Dla przykáadu: je eli liczba caákowita 1234 zostanie zapisana w postaci binarnej, w pliku pojawi si sekwencja bajtów 00 00 04 D2 (w notacji szesnastkowej). W formacie tekstowym liczba ta zostanie zapisana jako áa cuch "1234". Mimo i zapis danych w postaci binarnej jest szybki i efektywny, to uzyskany wynik jest kompletnie nieczytelny dla ludzi. W poni szym podrozdziale skoncen- trujemy si na tekstowym wej ciu-wyj ciu.
  • 20. 28 Java. Techniki zaawansowane Zapisuj c áa cuchy znakowe, musimy uwzgl dniü sposób kodowania znaków. W przypad- ku kodowania UTF-16 áa cuch "1234" zostanie zakodowany jako 00 31 00 32 00 33 00 34 (w notacji szesnastkowej). Jednak e obecnie wi kszo ü rodowisk, w których uruchamiamy programy w j zyku Java, u ywa swojego wáasnego formatu tekstu. W kodzie ISO 8859-1, najcz ciej stosowanym w USA i Europie Zachodniej, nasz przykáadowy áa cuch zostanie zapisany jako 31 32 33 34, bez bajtów o warto ci zero. Klasa OutputStreamWriter zamienia strumie znaków Unicode na strumie bajtów, stosuj c odpowiednie kodowanie znaków. Natomiast klasa InputStreamReader zamienia strumie wej cia, zawieraj cy bajty (reprezentuj ce znaki za pomoc okre lonego kodowania), na obiekt udost pniaj cy znaki Unicode. Poni ej przedstawiamy sposób utworzenia obiektu wej cia, wczytuj cego znaki z konsoli i automatycznie konwertuj cego je na Unicode. InputStreamReader in = new InputStreamReader(System.in); Obiekt wej cia korzysta z domy lnego kodowania lokalnego systemu, na przykáad ISO 8859-1 Mo emy wybraü inny sposób kodowania, podaj c jego nazw w konstruktorze InputStream ´Reader, na przykáad: InputStreamReader in = new InputStreamReader(new FileInputStream("kremlin.dat"), ´"ISO8859_5"); Wi cej informacji na temat kodowania znaków znajdziesz w punkcie „Zbiory znaków” na stronie 35. Poniewa obiekty tekstowego wej cia i wyj cia s tak cz sto doá czane do plików, Java dostar- cza w tym celu dwie wygodne klasy: FileReader i FileWriter. Na przykáad instrukcja FileWriter out = new FileWriter("output.txt"); jest równoznaczna z FileWriter out = new FileWriter(new FileOutputStream("output.txt"); Zapisywanie tekstu W celu zapisania tekstu korzystamy z klasy PrintWriter. Dysponuje ona metodami umo - liwiaj cymi zapis áa cuchów i liczb w formacie tekstowym. Dla wygody programistów ma ona konstruktor umo liwiaj cy poá czenie obiektu klasy PrintWriter z FileWriter. Zatem instrukcja PrintWriter out = new PrintWriter("employee.txt"); stanowi odpowiednik instrukcji PrintWriter out = new PrintWriter(new FileWriter("employee.txt")); Do zapisywania danych za pomoc obiektu klasy PrintWriter u ywamy tych samych metod print i println, których u ywali my dot d z obiektem System.out. Mo emy wykorzystywaü je do zapisu liczb (int, short, long, float, double), znaków, warto ci logicznych, áa cuchów znakowych i obiektów.
  • 21. RozdziaÄ 1. Q Strumienie i pliki 29 Spójrzmy na poni szy kod: String name = "Harry Hacker"; double salary = 75000; out.print(name); out.print(' '); out.println(salary); Rezultatem jego wykonania b dzie wysáanie napisu Harry Hacker 75000.0 do strumienia out. Nast pnie znaki zostan skonwertowane na bajty i zapisane w pliku employee.txt. Metoda println automatycznie dodaje znak ko ca wiersza, odpowiedni dla danego systemu operacyjnego ("rn" w systemie Windows, "n" w Unix). Znak ko ca wiersza mo emy pobraü, stosuj c wywoáanie System.getProperty("line.separator"). Je eli obiekt zapisu znajduje si w trybie automatycznego opró niania, w chwili wywoáania metody println wszystkie znaki w buforze zostan wysáane do odbiorcy (obiekty PrintWriter zawsze s buforowane). Domy lnie automatyczne opró nianie jest wyá czone. Automaty- czne opró nianie mo emy wá czaü i wyá czaü przy u yciu konstruktora PrintWriter(Writer out, boolean autoFlush: PrintWriter out = new PrintWriter(new FileWriter("employee.txt", true); // automatyczne opró nianie Metody print nie wyrzucaj wyj tków. Aby sprawdziü, czy ze strumieniem jest wszystko w porz dku, wywoáujemy metod checkError. Weterani Javy prawdopodobnie zastanawiajñ siö, co siö staäo z klasñ PrintStream i obiektem System.out. W jözyku Java 1.0 klasa PrintStream obcinaäa znaki Unicode do znaków ASCII, po prostu opuszczajñc górny bajt. Takie rozwiñzanie nie pozwalaäo na przenoszenie kodu na inne platformy i w jözyku Java 1.1 zostaäo zastñpione przez kon- cepcjö obiektów odczytu i zapisu. Ze wzglödu na konieczno è zachowania zgodno ci System.in, System.out i System.err wciñ sñ strumieniami, nie obiektami odczytu i zapisu. Ale obecna klasa PrintStream konwertuje znaki Unicode na schemat kodowa- nia lokalnego systemu w ten sam sposób, co klasa PrintWriter. Gdy u ywamy metod print i println, obiekty PrintStream dziaäajñ tak samo jak obiekty PrintWriter, ale w przeciwieþstwie do PrintWriter pozwalajñ wysyäaè bajty za pomocñ metod write(int) i write(byte[]). java.io.PrintWriter 1.1 Q PrintWriter(Writer out) Q PrintWriter(Writer out, boolean autoFlush) tworzy nowy obiekt klasy PrintWriter. Parametry: out obiekt zapisu tekstu. autoFlush true oznacza, e metody println b d opró niaü bufor (domy lnie: false).
  • 22. 30 Java. Techniki zaawansowane Q PrintWriter(OutputStream out) Q PrintWriter(OutputStream out, boolean autoFlush) tworzy nowy obiekt klasy PrintWriter na podstawie istniej cego obiektu typu OutputStream, poprzez utworzenie po rednicz cego obiektu klasy OutputStreamWriter. Q PrintWriter(String filename) Q PrintWriter(File file) tworzy nowy obiekt klasy PrintWriter zapisuj cy dane do pliku poprzez utworzenie po rednicz cego obiektu klasy FileWriter. Q void print(Object obj) drukuje áa cuch zwracany przez metod toString danego obiektu. Parametry: obj drukowany obiekt. Q void print(String p) drukuje áa cuch Unicode. Q void println(String p) drukuje áa cuch zako czony znakiem ko ca wiersza. Je eli automatyczne opró nianie jest wá czone, opró nia bufor strumienia. Q void print(char[] p) drukuje tablic znaków Unicode. Q void print(char c) drukuje znak Unicode. Q void print(int i) Q void print(long l) Q void print(float f) Q void print(double d) Q void print(boolean b) drukuje podan warto ü w formacie tekstowym. Q void printf(String format, Object... args) drukuje podane warto ci wedáug áa cucha formatuj cego. Specyfikacj áa cucha formatuj cego znajdziesz w rozdziale 3. ksi ki Java 2. Podstawy. Q boolean checkError() zwraca true, je eli wyst piá bá d formatowania lub zapisu. Je eli w strumieniu danych wyst pi bá d, strumie zostanie uznany za niepewny (ang. tainted) i wszystkie nast pne wywoáania metody checkError b d zwracaü true.
  • 23. RozdziaÄ 1. Q Strumienie i pliki 31 Wczytywanie tekstu Wiemy ju , e: Q aby zapisaü dane w formacie binarnym, u ywamy klasy DataOutputStream; Q aby zapisaü dane w formacie tekstowym, u ywamy klasy PrintWriter. Na tej podstawie mo na si domy laü, e istnieje równie klasa analogiczna do DataInput ´Stream, która pozwoli nam czytaü dane w formacie tekstowym. Najbli szym odpowiedni- kiem jest w tym przypadku klasa Scanner, któr wykorzystywali my intensywnie w ksi ce Java 2. Podstawy. Niestety, przed wprowadzeniem Java SE 5.0 mo na byáo u yü w tym celu jedynie klasy BufferedReader. Ma ona metod readLine pozwalaj c pobraü wiersz tekstu. Aby j wykorzystaü, musimy najpierw poá czyü obiekt typu BufferedReader ze ródáem wej cia. BufferedReader in = new BufferedReader(new FileReader("employee.txt")); Je eli dalsze wczytywanie nie jest mo liwe, metoda readLine zwraca null. Typowa p tla pobierania danych wygl da wi c nast puj co: String line; while ((line = in.readLine()) != null) { operacje na danych line } Jednak klasa BufferedReader nie udost pnia metod odczytu danych liczbowych. Dlatego do odczytu danych sugerujemy zastosowanie klasy Scanner. Zapis obiektów w formacie tekstowym W tym podrozdziale przeanalizujemy dziaáanie przykáadowego programu, który b dzie zapi- sywaü tablic obiektów typu Employee w pliku tekstowym. Dane ka dego obiektu zostan zapisane w osobnym wierszu. Warto ci pól skáadowych zostan oddzielone od siebie sepa- ratorami. Jako separatora u ywamy pionowej kreski (|) (innym popularnym separatorem jest dwukropek (:), zabawa polega na tym, e ka dy programista u ywa innego separatora). Naturalnie, taki wybór stawia przed nami pytanie, co b dzie, je li znak | znajdzie si w jednym z zapisywanych przez nas áa cuchów? Oto przykáadowy zbiór danych obiektów: Harry Hacker|35500|1989|10|1 Carl Cracker|75000|1987|12|15 Tony Tester|38000|1990|3|15 Zapis tych rekordów jest prosty. Poniewa korzystamy z pliku tekstowego, u ywamy klasy PrintWriter. Po prostu zapisujemy wszystkie pola skáadowe, za ka dym z nich stawiaj c |, albo te , po ostatnim polu, n. Operacje te wykona poni sza metoda writeData, któr dodamy do klasy Employee.
  • 24. 32 Java. Techniki zaawansowane public void writeData(PrintWriter out) throws IOException { GregorianCalendar calendar = new GregorianCalendar(); kalendarz.setTime(hireDay); out.println(name + "|" + salary + "|" + calendar.get(Calendar.YEAR) + "|" + (calendar.get(Calendar.MONTH) + 1) + "|" + calendar.get(Calendar.DAY_OF_MONTH)); } Aby odczytaü te dane, wczytujemy po jednym wierszu tekstu i rozdzielamy pola skáadowe. Do wczytania wierszy u yjemy obiektu klasy Scanner, a metoda String.split pozwoli nam wyodr bniü poszczególne tokeny. public void readData(Scanner in) { String line = in.nextLine(); String[] tokens = line.split("|"); name = tokens[0]; salary = Double.parseDouble(tokens[1]); int y = Integer.parseInt(tokens[2]); int m = Integer.parseInt(tokens[3]); int d = Integer.parseInt(tokens[4]); GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d); hireDay = calendar.getTime(); } Parametrem metody split jest wyra enie regularne opisuj ce separator. Wyra enie regularne omówimy bardziej szczegóáowo pod koniec bie cego rozdziaáu. Poniewa pionowa kreska ma specjalne znaczenie w wyra eniach regularnych, to musimy poprzedziü j znakiem . Ten z kolei musimy poprzedziü jeszcze jednym znakiem — w efekcie uzyskuj c wyra enie postaci "|". Kompletny program zostaá przedstawiony na listingu 1.1. Metoda statyczna void writeData(Employee[] e, PrintWriter out) najpierw zapisuje rozmiar tablicy, a nast pnie ka dy z rekordów. Metoda statyczna Employee[] readData(BufferedReader in) najpierw wczytuje rozmiar tablicy, a nast pnie ka dy z rekordów. Wymaga to zastosowania pewnej sztuczki: int n = in.nextInt(); in.nextLine(); // konsumuje znak nowego wiersza Employee[] employees = new Employee[n]; for (int i = 0; i < n; i++) { employees[i] = new Employee(); employees[i].readData(in); } Wywoáanie metody nextInt wczytuje rozmiar tablicy, ale nie nast puj cy po nim znak nowego wiersza. Musimy zatem go pobraü (wywoáuj c metod nextLine), aby metoda readData mogáa uzyskaü kolejny wiersz.
  • 25. RozdziaÄ 1. Q Strumienie i pliki 33 Listing 1.1. TextFileTest.java import java.io.*; import java.util.*; /** * @version 1.12 2007-06-22 * @author Cay Horstmann */ public class TextFileTest { public static void main(String[] args) { Employee[] staff = new Employee[3]; staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15); staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15); try { // zapisuje wszystkie rekordy pracowników w pliku employee.dat PrintWriter out = new PrintWriter("employee.dat"); writeData(staff, out); out.close(); // wczytuje wszystkie rekordy do nowej tablicy Scanner in = new Scanner(new FileReader("employee.dat")); Employee[] newStaff = readData(in); in.close(); // wy wietla wszystkie wczytane rekordy for (Employee e : newStaff) System.out.println(e); } catch (IOException exception) { exception.printStackTrace(); } } /** * Zapisuje dane wszystkich obiektów klasy Employee * umieszczonych w tablicy * do obiektu klasy PrintWriter * @param employees tablica obiektów klasy Employee * @param out obiekt klasy PrintWriter */ private static void writeData(Employee[] employees, PrintWriter out) throws ´IOException { // zapisuje liczbú obiektów out.println(employees.length); for (Employee e : employees) e.writeData(out); }
  • 26. 34 Java. Techniki zaawansowane /** * Wczytuje tablicú obiektów klasy Employee * @param in obiekt klasy Scanner * @return tablica obiektów klasy Employee */ private static Employee[] readData(Scanner in) { // pobiera rozmiar tablicy int n = in.nextInt(); in.nextLine(); // pobiera znak nowego wiersza Employee[] employees = new Employee[n]; for (int i = 0; i < n; i++) { employees[i] = new Employee(); employees[i].readData(in); } return employees; } } class Employee { public Employee() { } public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public String toString()
  • 27. RozdziaÄ 1. Q Strumienie i pliki 35 { return getClass().getName() + "[name=" + name + ",salary=" + salary + ´",hireDay=" + hireDay + "]"; } /** * Zapisuje dane obiektu klasy Employee * do obiektu klasy PrintWriter * @param out obiekt klasy PrintWriter */ public void writeData(PrintWriter out) { GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(hireDay); out.println(name + "|" + salary + "|" + calendar.get(Calendar.YEAR) + "|" + (calendar.get(Calendar.MONTH) + 1) + "|" + ´calendar.get(Calendar.DAY_OF_MONTH)); } /** * Wczytuje dane obiektu klasy Employee * @param in obiekt klasy Scanner */ public void readData(Scanner in) { String line = in.nextLine(); String[] tokens = line.split("|"); name = tokens[0]; salary = Double.parseDouble(tokens[1]); int y = Integer.parseInt(tokens[2]); int m = Integer.parseInt(tokens[3]); int d = Integer.parseInt(tokens[4]); GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d); hireDay = calendar.getTime(); } private String name; private double salary; private Date hireDay; } Zbiory znaków We wcze niejszych edycjach platformy Java problem znaków narodowych obsáugiwany byá w maáo systematyczny sposób. Sytuacja zmieniáa si z wprowadzeniem pakietu java.nio w Java SE 1.4, który unifikuje konwersje zbiorów znaków, udost pniaj c klas Charset (zwróümy uwag na maá liter s w nazwie klasy). Zbiór znaków stanowi odwzorowanie pomi dzy dwubajtowymi kodami Unicode i sekwen- cjami bajtów stosowanymi w lokalnych kodowaniach znaków. Jednym z najpopularniejszych kodowa znaków jest ISO-8859-1, które koduje za pomoc jednego bajta pierwszych 256 znaków z zestawu Unicode. Coraz wi ksze znaczenie zyskuje równie ISO08859-15, w którym
  • 28. 36 Java. Techniki zaawansowane zast piono cz ü mniej przydatnych znaków kodu ISO-8859-1 akcentowanymi znakami j zyka francuskiego i fi skiego, a przede wszystkim zamiast znaku waluty mi dzynarodowej ¤ umieszczono symbol euro (€) o kodzie 0xA4. Innymi przykáadami kodowa znaków s ko- dowania o zmiennej liczbie bajtów stosowane dla j zyków japo skiego i chi skiego. Klasa Charset u ywa nazw zbiorów znaków zgodnie ze standardem okre lonym przez IANA Character Set Registry (http://www.iana.org/assignments/character-sets). Nazwy te ró ni si nieco od nazw stosowanych w poprzednich wersjach. Na przykáad „oficjaln ” nazw ISO-8859-1 jest teraz "ISO-8859-1" zamiast "ISO8859_1" preferowan w Java SE 1.3 i wcze niejszych wersjach. Opis kodowania ISO 8859 znajdziesz na stronie http://aspell.net/charsets/ iso8859.html. Obiekt klasy Charset uzyskujemy, wywoáuj c metod statyczn forName, której podajemy oficjaln nazw zbioru znaków lub jeden z jej synonimów: Charset cset = Charset.forName("ISO-8859-1"); Du e i maáe litery nie s rozró niane w nazwach zbiorów znaków. Ze wgl du na konieczno ü zachowania zgodno ci z innymi konwencjami nazw nazwa ka dego zbioru znaków mo e mieü wiele synonimów. Na przykáad dla ISO-8859-1 istniej nast - puj ce synonimy: ISO8859-1 ISO_8859_1 ISO8859_1 ISO_8859-1 ISO_8859-1:1987 8859_1 latin1 l1 csISOLatin1 iso-ir-100 cp819 IBM819 IBM-819 819 Metoda aliases zwraca obiekt klasy Set zawieraj cy synonimy. Poni ej przedstawiamy kod umo liwiaj cy przegl danie synonimów: Set<String> aliases = cset.aliases(); for (String alias : aliases) System.out.println(alias); Aby dowiedzieü si , które zbiory znaków s dost pne dla konkretnej implementacji, wywo- áujemy metod statyczn availableCharsets. Poni szy kod pozwala poznaü nazwy wszyst- kich dost pnych zbiorów znaków: Map<String, Charset> charsets = Charset.availableCharsets(); for (String name : charsets.keySet()) System.out.println(name);
  • 29. RozdziaÄ 1. Q Strumienie i pliki 37 W tabeli 1.1 zostaáy przedstawione wszystkie kodowania znaków, które musi obsáugiwaü ka da implementacja platformy Java. W tabeli 1.2 wymienione zostaáy schematy kodowania instalowane domy lnie przez pakiet JDK. Zbiory znaków przedstawione w tabeli 1.3 s insta- lowane tylko w przypadku systemów operacyjnych u ywaj cych j zyków innych ni euro- pejskie. Tabela 1.1. Kodowania znaków wymagane na platformie Java Standardowa nazwa Nazwa tradycyjna Opis obiektu Charset US-ASCII ASCII American Standard Code for Information Exchange ISO-8859-1 ISO8859_1 ISO 8859-1, alfabet Latin 1 UTF-8 UTF8 8-bitowy Unicode Transformation Format UTF-16 UTF-16 16-bitowy Unicode Transformation Format, porz dek bajtów okre lony przez opcjonalny znacznik UTF-16BE UnicodeBigUnmarked 16-bitowy Unicode Transformation Format, porz dek bajtów od najstarszego UTF-16LE UnicodeLittleUnmarked 16-bitowy Unicode Transformation Format, porz dek bajtów od najmáodszego Tabela 1.2. Podstawowe kodowania znaków Standardowa nazwa Nazwa tradycyjna Opis obiektu Charset ISO8859-2 ISO8859_2 ISO 8859-2, alfabet Latin 2 ISO8859-4 ISO8859_4 ISO 8859-4, alfabet Latin 4 ISO8859-5 ISO8859_5 ISO 8859-5, alfabet Latin/Cyrillic ISO8859-7 ISO8859_7 ISO 8859-7, alfabet Latin/Greek ISO8859-9 ISO8859_9 ISO 8859-9, alfabet Latin 5 ISO8859-13 ISO8859_13 ISO 8859-13, alfabet Latin 7 ISO8859-15 ISO8859_15 ISO 8859-15, alfabet Latin 9 windows-1250 Cp1250 Windows, wschodnioeuropejski windows-1251 Cp1251 Windows Cyrillic windows-1252 Cp1252 Windows, Latin 1 windows-1253 Cp1253 Windows, grecki windows-1254 Cp1254 Windows, turecki windows-1257 Cp1257 Windows, baátycki
  • 30. 38 Java. Techniki zaawansowane Tabela 1.3. Rozszerzone kodowania znaków Standardowa nazwa Nazwa tradycyjna Opis obiektu Charset Big5 Big5 Big5, tradycyjny chi ski Big5-HKSCS Big5_HKSCS Big5, tradycyjny chi ski z rozszerzeniami Hongkong EUC-JP EUC_JP JIS X 0201, 0208, 0212, kodowanie EUC, japo ski EUC-KR EUC_KR KS C 5601, kodowanie EUC, korea ski GB18030 GB18030 uproszczony chi ski, standard PRC GBK GBK GBK, uproszczony chi ski ISCII91 ISCII91 ISCII91, indu ISO-2022-JP ISO2022JP JIS X 0201, 0208 w postaci ISO 2022, japo ski ISO-2022-KR ISO2022KR ISO 2022 KR, korea ski ISO8859-3 ISO8859_3 ISO 8859-3, Latin 3 ISO8859-6 ISO8859_6 ISO 8859-6, alfabet áaci ski/arabski ISO8859-8 ISO8859_8 ISO 8859-8, alfabet áaci ski/hebrajski Shift_JIS SJIS Shift_JIS, japo ski TIS-620 TIS620 TIS620, tajski windows-1255 Cp1255 Windows, hebrajski windows-1256 Cp1256 Windows, arabski windows-1258 Cp1258 Windows, wietnamski windows-3lj MS392 Windows, japo ski x-EUC-CN EUC_CN GB2313, kodowanie EUC, uproszczony chi ski x-EUC-JP-LINUX EUC_JP_LINUX JIS X 0201, 0208, kodowanie EUC, japo ski x-EUC-TW EUC_TW CNS11643 (Plane 1-3), kodowanie EUC, tradycyjny chi ski x-MS950-HKSCS MS950_HKSCS Windows, tradycyjny chi ski z rozszerzeniami Hongkong x-mswin-936 MS936 Windows, uproszczony chi ski x-windows-949 MS949 Windows, korea ski x-windows-950 MS950 Windows, tradycyjny chi ski Lokalne schematy kodowania nie mog oczywi cie reprezentowaü wszystkich znaków Unicode. Je li znak nie jest reprezentowany, to zostaje przeksztaácony na znak ?. Dysponuj c zbiorem znaków, mo emy u yü go do konwersji áa cuchów Unicode i sekwencji bajtów. Oto przykáad kodowania áa cucha Unicode:
  • 31. RozdziaÄ 1. Q Strumienie i pliki 39 String str = . . .; ByteBuffer buffer = cset.encode(str); byte[] bytes = buffer.array(); Natomiast aby dokonaü konwersji w kiedunku przeciwnym, potrzebny b dzie bufor. Wykorzy- stamy metod statyczn wrap tablicy ByteBuffer, aby przeksztaáciü tablic bajtów w bufor. W wyniku dziaáania metody decode otrzymujemy obiekt klasy CharBuffer. Wystarczy wywo- áaü jego metod toString, aby uzyskaü áa cuch znaków. byte[] bytes = . . .; ByteBuffer bbuf = ByteBuffer.wrap(bytes, offset, length); CharBuffer cbuf = cset.decode(bbuf); String str = cbuf.toString(); java.nio.charset.Charset 1.4 Q static SortedMap availableCharsets() pobiera wszystkie zbiory znaków dost pne dla maszyny wirtualnej. Zwraca map , której kluczami s nazwy zbiorów znaków, a warto ciami same zbiory. Q static Charset forName(String name) zwraca zbiór znaków o podanej nazwie. Q Set aliases() zwraca zbiór synonimów nazwy danego zbioru znaków. Q ByteBuffer encode(String str) dokonuje konwersji podanego áa cucha na sekwencj bajtów. Q CharBuffer decode(ByteBuffer buffer) dokonuje konwersji sekwencji bajtów. Nierozpoznane bajty s zamieniane na specjalny znak Unicode ('uFFFD'). java.nio.ByteBuffer 1.4 Q byte[] array() zwraca tablic bajtów, któr zarz dza ten bufor. Q static ByteBuffer wrap(byte[] bytes) Q static ByteBuffer wrap(byte[] bytes, int offset, int length) zwraca bufor, który zarz dza podan tablic bajtów lub jej okre lonym zakresem. java.nio.CharBuffer Q char[] array() zwraca tablic kodów, któr zarz dza ten bufor. Q char charAt(int index) zwraca kod o podanym indeksie.
  • 32. 40 Java. Techniki zaawansowane Q String toString() zwraca áa cuch, który tworz kody zarz dzane przez ten bufor. Odczyt i zapis danych binarnych Aby zapisaü liczb , znak, warto ü logiczn lub áa cuch, korzystamy z jednej z poni szych metod interfejsu DataOutput: writeChars writeByte writeInt writeShort writeLong writeFloat writeDouble writeChar writeBoolean writeUTF Na przykáad, writeInt zawsze zapisuje liczb integer jako warto ü czterobajtow , niezale nie od liczby jej cyfr, a writeDouble zawsze zapisuje liczby double jako warto ci o miobajtowe. Rezultat tych dziaáa nie jest czytelny dla czáowieka, ale poniewa wymagana ilo ü bajtów jest taka sama dla ka dej warto ci danego typu, to wczytanie ich z powrotem b dzie szybsze ni parsowanie zapisu tekstowego. Zale nie od platformy u ytkownika, liczby caäkowite i zmiennoprzecinkowe mogñ byè przechowywane w pamiöci na dwa ró ne sposoby. Zaäó my, e pracujesz z cztero- bajtowñ warto ciñ, takñ jak int, na przykäad 1234, czyli 4D2 w zapisie szesnastkowym (1234 = 4×256+13×16+2). Mo e ona zostaè przechowana w ten sposób, e pierwszym z czterech bajtów pamiöci bödzie bajt najbardziej znaczñcy (ang. most significant byte, MSB): 00 00 04 D2. Albo w taki sposób, e bödzie to bajt najmäodszy (ang. least signifi- cant byte, LSB): D2 04 00 00. Pierwszy sposób stosowany jest przez maszyny SPARC, a drugi przez procesory Pentium. Mo e to powodowaè problemy z przenoszeniem nawet najprostszych plików danych pomiödzy ró nymi platformami. W jözyku Java zawsze sto- sowany jest pierwszy sposób, niezale nie od procesora. Dziöki temu pliki danych progra- mów w jözyku Java sñ niezale ne od platformy. Metoda writeUTF zapisuje áa cuchy, u ywaj c zmodyfikowanej wersji 8-bitowego kodu UTF (ang. Unicode Text Format). Zamiast po prostu zastosowaü od razu standardowe kodowanie UTF-8 (przedstawione w tabeli 1.4), znaki áa cucha s najpierw reprezentowane w kodzie UTF-16 (patrz tabela 1.5), a dopiero potem przekodowywane na UTF-8. Wynik takiego kodo- wania ró ni si dla znaków o kodach wi kszych od 0xFFFF. Kodowanie takie stosuje si dla zachowania zgodno ci z maszynami wirtualnymi powstaáymi, gdy Unicode zadowalaá si tylko 16 bitami. Poniewa opisana modyfikacja kodowania UTF-8 stosowana jest wyá cznie na platformie Java, to metody writeUTF powinni my u ywaü tylko do zapisu áa cuchów przetwarzanych przez programy wykonywane przez maszyn wirtualn Java. W pozostaáych przypadkach nale y u ywaü metody writeChars.
  • 33. RozdziaÄ 1. Q Strumienie i pliki 41 Tabela 1.4. Kodowanie UTF-8 Zakres znaków Kodowanie 0...7F 0a6a5a4a3a2a1a0 80...7FF 110a10a9a8a7a6 10a5a4a3a2a1a0 800...FFFF 1110a15a14a13a12 10a11a10a9a8a7a6 10a5a4a3a2a1a0 10000...10FFFF 11110a20a19a18 10a17a16a15a14a13a12 10a11a10a9a8a7a6 10a5a4a3a2a1a0 Tabela 1.5. Kodowanie UTF-16 Zakres znaków Kodowanie 0...FFFF a15a14a13a12a11a10a9a8 a7a6a5a4a3a2a1a0 10000...10FFFF 110110b19b18b17b16a15a14a13a12a11a10 110111a9a8 a7a65a4a3a2a1a0 gdzie b19b18b17b16 = a20a19a18a17a16 - 1 Definicje kodów UTF-8 i UTF-16 znajdziesz w dokumentach, odpowiednio: RFC 2279 (http://ietf.org/rfc/rfc2279.txt) i RFC 2781 (http://ietf.org/rfc/rfc2781.txt). Aby odczytaü dane, korzystamy z poni szych metod interfejsu DataInput: readInt readShort readLong readFloat readDouble readChar readBoolean readUTF Klasa DataInputStream implementuje interfejs DataInput. Aby odczytaü dane binarne z pli- ku, á czymy obiekt klasy DataInputStream ze ródáem bajtów, takim jak na przykáad obiekt klasy FileInputStream: DataInputStream in = new DataInputStream(new FileInputStream("employee.dat")); Podobnie, aby zapisaü dane binarne, u ywamy klasy DataOutputStream implementuj cej interfejs DataOutput: DataOutputStream out = new DataOutputStream(new FileOutputStream("employee.dat")); java.io.DataInput 1.0 Q boolean readBoolean() Q byte readByte() Q char readChar() Q double readDouble() Q float readFloat()
  • 34. 42 Java. Techniki zaawansowane Q int readInt() Q long readLong() Q short readShort() wczytuje warto ü okre lonego typu. Q void readFully(byte[] b) wczytuje bajty do tablicy b, blokuj c w tek, dopóki wszystkie bajty nie zostan wczytane. Parametry: b bufor, do którego zapisywane s dane. Q void readFully(byte[] b, int off, int len) wczytuje bajty do tablicy b, blokuj c w tek, dopóki wszystkie bajty nie zostan wczytane. Parametry: b bufor, do którego zapisywane s dane. off indeks pierwszego bajta. len maksymalna ilo ü odczytanych bajtów. Q String readUTF() wczytuje áa cuch znaków zapisanych w zmodyfikowanym formacie UTF-8. Q int skipBytes(int n) ignoruje n bajtów, blokuj c w tek, dopóki wszystkie bajty nie zostan zignorowane. Parametry: n liczba ignorowanych bajtów. java.io.DataOutput 1.0 Q void writeBoolean(boolean b) Q void writeByte(int b) Q void writeChar(char c) Q void writeDouble(double d) Q void writeFloat(float f) Q void writeInt(int i) Q void writeLong(long l) Q void writeShort(short s) zapisuj warto ü okre lonego typu. Q void writeChars(String s) zapisuje wszystkie znaki podanego áa cucha. Q void writeUTF(String s) zapisuje áa cuch znaków w zmodyfikowanym formacie UTF-8.
  • 35. RozdziaÄ 1. Q Strumienie i pliki 43 Strumienie plików o swobodnym dostÂpie Strumie RandomAccessFile pozwala pobraü lub zapisaü dane w dowolnym miejscu pliku. Do plików dyskowych mo emy uzyskaü swobodny dost p, inaczej ni w przypadku strumieni danych pochodz cych z sieci. Plik o swobodnym dost pie mo emy otworzyü w trybie tylko do odczytu albo zarówno do odczytu, jak i do zapisu. Okre lamy to, u ywaj c jako drugiego argumentu konstruktora áa cucha "r" (odczyt) lub "rw" (odczyt i zapis). RandomAccesFile in = new RandomAccesFile("employee.dat", "r"); RandomAccesFile inOut = new RandomAccesFile("employee.dat", "rw"); Otwarcie istniej cego pliku przy u yciu RandomAccessFile nie powoduje jego skasowania. Plik o swobodnym dost pie posiada wska nik pliku. Wska nik pliku opisuje pozycj nast p- nego bajta, który zostanie wczytany lub zapisany. Metoda seek zmienia poáo enie wska nika, okre laj c numer bajta, na który wskazuje. Argumentem metody seek jest liczba typu long z przedziaáu od 0 do dáugo ci pliku w bajtach. Metoda getFilePointer zwraca aktualne poáo enie wska nika pliku. Klasa RandomAccessFile implementuje zarówno interfejs DataInput, jak i DataOutput. Aby czytaü z pliku o swobodnym dost pie, u ywamy tych samych metod, np. readInt/writeInt lub readChar/writeChar, które omówili my w poprzednim podrozdziale. Prze ledzimy teraz dziaáanie programu, który przechowuje rekordy pracowników w pliku o swobodnym dost pie. Ka dy z rekordów b dzie mieü ten sam rozmiar, co uáatwi nam ich wczytywanie. Zaáó my na przykáad, e chcemy ustawiü wska nik pliku na trzecim rekordzie. Musimy zatem wyznaczyü bajt, na którym nale y ustawiü ten wska nik, a nast pnie mo emy ju wczytaü rekord. long n = 3; in.seek((n - 1) * RECORD_SIZE); Employee e = new Employee(); e.readData(in); Je li zmodyfikujemy rekord i b dziemy chcieli zapisaü go w tym samym miejscu pliku, musimy pami taü, aby przywróciü wska nik pliku na pocz tek tego rekordu: in.seek((n - 1) * RECORD_SIZE); e.writeData(out); Aby okre liü caákowit liczb bajtów w pliku, u ywamy metody length. Caákowit liczb rekordów w pliku ustalamy, dziel c liczb bajtów przez rozmiar rekordu. long nbytes = in.length(); // d ugo è w bajtach int nrecords = (int) (nbytes / RECORD_SIZE); Liczby caákowite i zmiennoprzecinkowe posiadaj reprezentacj binarn o staáej liczbie bajtów. W przypadku áa cuchów znaków sytuacja jest nieco trudniejsza. Stworzymy zatem dwie metody pomocnicze pozwalaj ce zapisywaü i wczytywaü áa cuchy o ustalonym rozmiarze. Metoda writeFixedString zapisuje okre lon liczb kodów, zaczynaj c od pocz tku áa cucha. (Je li jest ich za maáo, to dopeánia áa cuch warto ciami zerowymi).
  • 36. 44 Java. Techniki zaawansowane public static void writeFixedString(String s, int size, DataOutput out) throws IOException { for (int i = 0; i < size; i++) { char ch = 0; if (i < s.length()) ch = s.charAt(i); out.writeChar(ch); } } Metoda readFixedString wczytuje size kodów znaków ze strumienia wej ciowego lub do momentu napotkania warto ci zerowej. Wszystkie pozostaáe warto ci zerowe zostaj pomi- ni te. Dla lepszej efektywno ci metoda u ywa klasy StringBuilder do wczytania áa cucha. public static String readFixedString(int size, DataInput in) throws IOException { StringBuilder b = new StringBuilder(size); int i = 0; boolean more = true; while (more && i < size) { char ch = in.readChar(); i++; if (ch == 0) more = false; else b.append(ch); } in.skipBytes(2 * (size - i)); return b.toString(); } Metody writeFixedString i readFixedString umie cili my w klasie pomocniczej DataIO. Aby zapisaü rekord o staáym rozmiarze, zapisujemy po prostu wszystkie jego pola w formacie binarnym. public void writeData(DataOutput out) throws IOException { DataIO.writeFixedString(name, NAME_SIZE, out); out.writeDouble(salary); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(hireDay); out.writeInt(calendar.get(Calendar.YEAR)); out.writeInt(calendar.get(Calendar.MONTH) + 1); out.writeInt(calendar.get(Calendar.DAY_OF_MONTH)); } Odczyt rekordu jest równie prosty. public void readData(DataInput in) throws IOException { name = DataIO.readFixedString(NAME_SIZE, in); salary = in.readDouble(); int y = in.readInt(); int m = in.readInt();
  • 37. RozdziaÄ 1. Q Strumienie i pliki 45 int d = in.readInt(); GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d); hireDay = calendar.getTime(); } Wyznaczmy jeszcze rozmiar ka dego rekordu. àa cuchy znakowe przechowuj ce nazwiska b d miaáy 40 znaków dáugo ci. W rezultacie ka dy rekord b dzie zajmowaü 100 bajtów: Q 40 znaków = 80 bajtów dla pola name Q 1 double = 8 bajtów dla pola salary Q 3 int = 12 bajtów dla pola date Program przedstawiony na listingu 1.2 zapisuje trzy rekordy w pliku danych, a nast pnie wczytuje je w odwrotnej kolejno ci. Efektywne dziaáanie programu wymaga pliku o swobod- nym dost pie, poniewa najpierw zostanie wczytany ostatni rekord. Listing 1.2. RandomFileTest.java import java.io.*; import java.util.*; /** * @version 1.11 2004-05-11 * @author Cay Horstmann */ public class RandomFileTest { public static void main(String[] args) { Employee[] staff = new Employee[3]; staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15); staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15); try { // zapisuje rekordy wszystkich pracowników w pliku employee.dat DataOutputStream out = new DataOutputStream(new ´FileOutputStream("employee.dat")); for (Employee e : staff) e.writeData(out); out.close(); // wczytuje wszystkie rekordy do nowej tablicy RandomAccessFile in = new RandomAccessFile("employee.dat", "r"); // oblicza rozmiar tablicy int n = (int)(in.length() / Employee.RECORD_SIZE); Employee[] newStaff = new Employee[n]; // wczytuje rekordy pracowników w odwrotnej kolejno ci for (int i = n - 1; i >= 0; i--) { newStaff[i] = new Employee(); in.seek(i * Employee.RECORD_SIZE);
  • 38. 46 Java. Techniki zaawansowane newStaff[i].readData(in); } in.close(); // wy wietla wczytane rekordy for (Employee e : newStaff) System.out.println(e); } catch (IOException e) { e.printStackTrace(); } } } class Employee { public Employee() {} public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } /** Podnosi wynagrodzenie pracownika. @byPercent podwy ka procentowo */ public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary
  • 39. RozdziaÄ 1. Q Strumienie i pliki 47 + ",hireDay=" + hireDay + "]"; } /** Zapisuje dane pracownika @param out obiekt klasy DataOutput */ public void writeData(DataOutput out) throws IOException { DataIO.writeFixedString(name, NAME_SIZE, out); out.writeDouble(salary); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(hireDay); out.writeInt(calendar.get(Calendar.YEAR)); out.writeInt(calendar.get(Calendar.MONTH) + 1); out.writeInt(calendar.get(Calendar.DAY_OF_MONTH)); } /** Wczytuje dane pracownika @param in obiekt klasy DataOutput */ public void readData(DataInput in) throws IOException { name = DataIO.readFixedString(NAME_SIZE, in); salary = in.readDouble(); int y = in.readInt(); int m = in.readInt(); int d = in.readInt(); GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d); hireDay = calendar.getTime(); } public static final int NAME_SIZE = 40; public static final int RECORD_SIZE = 2 * NAME_SIZE + 8 + 4 + 4 + 4; private String name; private double salary; private Date hireDay; } class DataIO { public static String readFixedString(int size, DataInput in) throws IOException { StringBuilder b = new StringBuilder(size); int i = 0; boolean more = true; while (more && i < size) { char ch = in.readChar(); i++; if (ch == 0) more = false; else b.append(ch);
  • 40. 48 Java. Techniki zaawansowane } in.skipBytes(2 * (size - i)); return b.toString(); } public static void writeFixedString(String s, int size, DataOutput out) throws IOException { for (int i = 0; i < size; i++) { char ch = 0; if (i < s.length()) ch = s.charAt(i); out.writeChar(ch); } } } java.io.RandomAccessFile 1.0 Q RandomAccessFile(String file, String mode) Q RandomAccessFile(File file, String mode) Parametry: file plik, który ma zostaü otwarty. tryb "r" dla samego odczytu, "rw" dla odczytu i zapisu, "rws" dla odczytu i zapisu danych wraz z synchronicznym zapisem danych i metadanych dla ka dej aktualizacji, "rwd" dla odczytu i zapisu danych wraz z synchronicznym zapisem tylko samych danych. Q long getFilePointer() zwraca aktualne poáo enie wska nika pliku. Q void seek(long pos) zmienia poáo enie wska nika pliku, przesuwaj c go o pos bajtów od pocz tku pliku. Q long length() zwraca dáugo ü pliku w bajtach. Strumienie plików ZIP Pliki ZIP to archiwa, w których mo na przechowywaü jeden lub wi cej plików w postaci (zazwyczaj) skompresowanej. Ka dy plik ZIP posiada nagáówek zawieraj cy informacje, takie jak nazwa pliku i u yta metoda kompresji. W j zyku Java, aby czytaü z pliku ZIP, korzystamy z klasy ZipInputStream. Odczyt dotyczy okre lonej pozycji w archiwum. Metoda getNext ´Entry zwraca obiekt typu ZipEntry opisuj cy pozycj archiwum. Metoda read klasy Zip
  • 41. RozdziaÄ 1. Q Strumienie i pliki 49 ´InputStream zwraca warto ü –1, gdy napotka koniec pozycji archiwum, a nie koniec caáego pliku ZIP. Aby odczytaü kolejn pozycj archiwum, musimy wtedy wywoáaü metod close ´Entry. Oto typowy kod wczytuj cy zawarto ü pliku ZIP: ZipInputStream zin = ZipInputStream (new FileInputStream(zipname)); ZipEntry entry; while ((entry = zin.getNextEntry()) != null) { analizuj entry; wczytaj zawarto è zin; zin.closeEntry(); } zin.close(); Wczytuj c zawarto ü pozycji pliku ZIP, zwykle zamiast z podstawowej metody read lepiej b dzie skorzystaü z jakiego bardziej kompetentnego filtru strumienia. Dla przykáadu, aby wydobyü z archiwum ZIP plik tekstowy, mo emy skorzystaü z poni szej p tli: Scanner in = new Scanner(zin); while (in.hasNextLine()) operacje na in.nextLine(); Strumieþ wej cia ZIP wyrzuca wyjñtek ZipException, je eli w czasie czytania pliku ZIP nastñpiä bäñd. Zazwyczaj dzieje siö tak, gdy archiwum zostaäo uszkodzone. Aby zapisaü dane do pliku ZIP, u ywamy strumie ZipOutputStream. Dla ka dej pozycji, któr chcemy umie ciü w archiwum ZIP, tworzymy obiekt ZipEntry. Nazw pliku przekazujemy konstruktorowi ZipEntry; konstruktor sam okre la inne parametry, takie jak data pliku i metoda dekompresji. Je li chcemy, mo emy zmieniü ich warto ci. Aby rozpocz ü zapis nowego pliku w archiwum, wywoáujemy metod putNextEntry klasy ZipOutputStream. Nast pnie wysyáamy dane do strumienia ZIP. Po zako czeniu zapisu pliku wywoáujemy metod closeEntry. Wymienione operacje powtarzamy dla wszystkich plików, które chcemy skompresowaü w archiwum. Oto schemat kodu: FileOutputStream fout = new FileOutputStream("test.zip"); ZipOutputStream zout = new ZipOutputStream(fout); dla wszystkich plików { ZipEntry ze = new ZipEntry(nazwapliku); zout.putNextEntry(kze); wy lij dane do zout; zout.closeEntry(); } zout.close(); Pliki JAR (omówione w rozdziale 10. ksiñ ki Java 2. Podstawy sñ po prostu plikami ZIP, zawierajñcymi specjalny rodzaj pliku, tzw. manifest. Do wczytania i zapisania manifestu u ywamy klas JarInputStream i JarOutputStream. Strumienie ZIP s dobrym przykáadem pot gi abstrakcji strumieni. Odczytuj c dane prze- chowywane w skompresowanej postaci, nie musimy zajmowaü si ich dekompresj . ródáo
  • 42. 50 Java. Techniki zaawansowane bajtów formatu ZIP nie musi byü plikiem — dane ZIP mog byü ci gane przez poá czenie sieciowe. Na przykáad za ka dym razem, gdy mechanizm áadowania klas jakiego apletu wczy- tuje plik JAR, tak naprawd wczytuje i dekompresuje dane pochodz ce z sieci. Artykuä dostöpny pod adresem http://www.javaworld.com/javaworld/jw-10-2000/ ´jw-1027-toolbox.html przedstawia sposób modyfikacji archiwum ZIP. Program przedstawiony na listingu 1.3 pozwala u ytkownikowi otworzyü archiwum ZIP. Nast pnie wy wietla pliki przechowywane w tym archiwum w postaci listy, w dolnej cz ci okna. Je eli u ytkownik kliknie dwukrotnie który z plików, jego zawarto ü zostanie wy wie- tlona w obszarze tekstowym, tak jak jest to pokazane na rysunku 1.5. Listing 1.3. ZipTest.java import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; import java.util.zip.*; import javax.swing.*; /** * @version 1.32 2007-06-22 * @author Cay Horstmann */ public class ZipTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { ZipTestFrame frame = new ZipTestFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * Ramka zawierajæca obszar tekstowy wy wietlajæcy zawarto è pliku, * listú pozwalajæcæ na wybór plików w archiwum * oraz menu pozwalajæce wczytaè nowe archiwum. */ class ZipTestFrame extends JFrame { public ZipTestFrame() { setTitle("ZipTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // dodaje menu i opcje Open oraz Exit
  • 43. RozdziaÄ 1. Q Strumienie i pliki 51 Rysunek 1.5. Program ZipTest w dziaäaniu JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); JMenuItem openItem = new JMenuItem("Open"); menu.add(openItem); openItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(new File(".")); int r = chooser.showOpenDialog(ZipTestFrame.this); if (r == JFileChooser.APPROVE_OPTION) { zipname = chooser.getSelectedFile().getPath(); fileCombo.removeAllItems(); scanZipFile(); } } }); JMenuItem exitItem = new JMenuItem("Exit"); menu.add(exitItem); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); menuBar.add(menu); setJMenuBar(menuBar); // dodaje obszar tekstowy i listú fileText = new JTextArea(); fileCombo = new JComboBox(); fileCombo.addActionListener(new ActionListener()
  • 44. 52 Java. Techniki zaawansowane { public void actionPerformed(ActionEvent event) { loadZipFile((String) fileCombo.getSelectedItem()); } }); add(fileCombo, BorderLayout.SOUTH); add(new JScrollPane(fileText), BorderLayout.CENTER); } /** * Skanuje zawarto è archiwum ZIP i wype nia listú. */ public void scanZipFile() { new SwingWorker<Void, String>() { protected Void doInBackground() throws Exception { ZipInputStream zin = new ZipInputStream(new ´FileInputStream(zipname)); ZipEntry entry; while ((entry = zin.getNextEntry()) != null) { publish(entry.getName()); zin.closeEntry(); } zin.close(); return null; } protected void process(List<String> names) { for (String name : names) fileCombo.addItem(name); } }.execute(); } /** * aduje zawarto è pliku z archiwum ZIP * do obszaru tekstowego * @param name nazwa pliku w archiwum */ public void loadZipFile(final String name) { fileCombo.setEnabled(false); fileText.setText(""); new SwingWorker<Void, Void>() { protected Void doInBackground() throws Exception { try {
  • 45. RozdziaÄ 1. Q Strumienie i pliki 53 ZipInputStream zin = new ZipInputStream(new ´FileInputStream(zipname)); ZipEntry entry; // znajduje element archiwum o odpowiedniej nazwie while ((entry = zin.getNextEntry()) != null) { if (entry.getName().equals(name)) { // wczytuje go do obszaru tekstowego Scanner in = new Scanner(zin); while (in.hasNextLine()) { fileText.append(in.nextLine()); fileText.append("n"); } } zin.closeEntry(); } zin.close(); } catch (IOException e) { e.printStackTrace(); } return null; } protected void done() { fileCombo.setEnabled(true); } }.execute(); } public static final int DEFAULT_WIDTH = 400; public static final int DEFAULT_HEIGHT = 300; private JComboBox fileCombo; private JTextArea fileText; private String zipname; } java.util.zip.ZipInputStream 1.1 Q ZipInputStream(InputStream in) tworzy obiekt typu ZipInputStream umo liwiaj cy dekompresj danych z podanego strumienia InputStream. Q ZipEntry getNextEntry() zwraca obiekt typu ZipEntry opisuj cy nast pn pozycj archiwum lub null, je eli archiwum nie ma wi cej pozycji.
  • 46. 54 Java. Techniki zaawansowane Q void closeEntry() zamyka aktualnie otwart pozycj archiwum ZIP. Dzi ki temu mo emy odczytaü nast pn pozycj , wywoáuj c metod getNextEntry(). java.util.zip.ZipOutputStream 1.1 Q ZipOutputStream(OutputStream out) tworzy obiekt typu ZipOutputStream, który umo liwia kompresj i zapis danych w podanym strumieniu OutputStream. Q void putNextEntry(ZipEntry ze) zapisuje informacje podanej pozycji ZipEntry do strumienia i przygotowuje strumie do odbioru danych. Dane mog zostaü zapisane w strumieniu przy u yciu metody write(). Q void closeEntry() zamyka aktualnie otwart pozycj archiwum ZIP. Aby otworzyü nast pn pozycj , wywoáujemy metod putNextEntry. Q void setLevel(int level) okre la domy lny stopie kompresji nast pnych pozycji archiwum o trybie DEFLATED. Domy ln warto ci jest Deflater.DEFAULT_COMPRESSION. Wyrzuca wyj tek IllegalArgumentException, je eli podany stopie jest nieprawidáowy. Parametry: level stopie kompresji, od 0 (NO_COMPRESSION) do 9 (BEST_COMPRESSION). Q void setMethod(int method) okre la domy ln metod kompresji dla danego ZipOutputStream dla wszystkich pozycji archiwum, dla których metoda kompresji nie zostaáa okre lona. Parametry: method metoda kompresji, DEFLATED lub STORED. java.util.zip.ZipEntry 1.1 Q ZipEntry(String name) Parametry: name nazwa elementu. Q long getCrc() zwraca warto ü sumy kontrolnej CRC32 danego elementu. Q String getName() zwraca nazw elementu. Q long getSize() zwraca rozmiar danego elementu po dekompresji lub –1, je eli rozmiar nie jest znany. Q boolean isDirectory() zwraca warto ü logiczn , która okre la, czy dany element archiwum jest katalogiem.
  • 47. RozdziaÄ 1. Q Strumienie i pliki 55 Q void setMethod(int method) Parametry: method metoda kompresji danego elementu, DEFLATED lub STORED. Q void setSize(long rozmiar) okre la rozmiar elementu. Wymagana, je eli metod kompresji jest STORED. Parametry: rozmiar rozmiar nieskompresowanego elementu. Q void setCrc(long crc) okre la sum kontroln CRC32 dla danego elementu. Aby obliczyü t sum u ywamy klasy CRC32. Wymagana, je eli metod kompresji jest STORED. Parametry: crc suma kontrolna elementu. java.util.ZipFile 1.1 Q ZipFile(String name) ten konstruktor tworzy obiekt typu ZipFile, otwarty do odczytu, na podstawie podanego áa cucha. Q ZipFile(File file) tworzy obiekt typu ZipFile, otwarty do odczytu, na podstawie podanego áa cucha lub obiektu typu File. Q Enumeration entries() zwraca obiekt typu Enumeration, wyliczaj cy obiekty ZipEntry opisuj ce elementy archiwum ZipFile. Q ZipEntry getEntry(String name) zwraca element archiwum o podanej nazwie lub null, je eli taki element nie istnieje. Parametry: name nazwa elementu. Q InputStream getInputStream(ZipEntry ze) zwraca obiekt InputStream dla podanego elementu. Parametry: ze element ZipEntry w pliku ZIP. Q String getName() zwraca cie k dost pu do pliku ZIP. Strumienie obiektów i serializacja Korzystanie z rekordów o staáej dáugo ci jest dobrym rozwi zaniem, pod warunkiem e zapi- sujemy dane tego samego typu. Jednak obiekty, które tworzymy w programie zorientowanym obiektowo, rzadko nale do tego samego typu. Dla przykáadu: mo emy u ywaü tablicy o nazwie staff, której nominalnym typem jest Employee, ale która zawiera obiekty b d ce instancjami klas pochodnych, np. klasy Manager.
  • 48. 56 Java. Techniki zaawansowane Z pewno ci mo na zaprojektowaü format danych, który pozwoli przechowywaü takie poli- morficzne kolekcje, ale na szcz cie ten dodatkowy wysiáek nie jest konieczny. J zyk Java obsáuguje bowiem bardzo ogólny mechanizm zwany serializacj obiektów. Pozwala on na wysáanie do strumienia dowolnego obiektu i umo liwia jego pó niejsze wczytanie (w dalszej cz ci tego rozdziaáu wyja nimy, sk d wzi á si termin „serializacja”). Aby zachowaü dane obiektu, musimy najpierw otworzyü strumie ObjectOutputStream: ObjectOutputStream out = new ObjectOutputStream(new ´FileOutputStream("employee.dat")); Teraz, aby zapisaü obiekt, wywoáujemy metod writeObject klasy ObjectOutputStream: Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1); Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); out.writeObject(harry); out.writeObject(boss); Aby z powrotem zaáadowaü obiekty, u ywamy strumienia ObjectInputStream: ObjectInputStream in = new ObjectInputStream(new FIleInputStream("employee.dat")); Nast pnie pobieramy z niego obiekty w tym samym porz dku, w jakim zostaáy zapisane, korzystaj c z metody readObject: Employee p1 = (Employee)in.readObject(); Employee p2 = (Employee)in.readObject(); Je li chcemy zapisywaü i odtwarzaü obiekty za pomoc strumieni obiektów, to konieczne jest wprowadzenie jednej modyfikacji w klasie tych obiektów. Klasa ta musi implementowaü interfejs Serializable: class Employee implements Serializable { . . . } Interfejs Serializable nie posiada metod, nie musimy zatem wprowadzaü adnych innych modyfikacji naszych klas. Pod tym wzgl dem Serializable jest podobny do interfejsu Clone- able, który omówili my w rozdziale 6. ksi ki Java 2. Podstawy. Aby jednak móc klonowaü obiekty, musimy przesáoniü metod clone klasy Object. Aby móc serializowaü, nie nale y robiü nic poza dopisaniem powy szych sáów. Jednak e musimy rozwa yü jeszcze jedn sytuacj . Co si stanie, je eli dany obiekt jest wspóá- dzielony przez kilka innych obiektów jako element ich stanu? Aby zilustrowaü ten problem, zmodyfikujemy troch klas Manager. Zaáó my, e ka dy mened er ma asystenta: class Manager extends Employee { . . . private Employee secretary; } Ka dy obiekt typu Manager przechowuje teraz referencj do obiektu klasy Employee opisuj - cego asystenta, a nie osobn kopi tego obiektu.
  • 49. RozdziaÄ 1. Q Strumienie i pliki 57 Oznacza to, e dwóch mened erów mo e mieü tego samego asystenta, tak jak zostaáo to przed- stawione na rysunku 1.6 i w poni szym kodzie: harry = new Employee("Harry Hacker", . . .); Manager carl = new Manager("Carl Cracker", . . .); carl.setSecretary(harry); Manager tony = new Manager("Tony Tester", . . .); tony.setSecretary(harry); Rysunek 1.6. Dwóch mened erów mo e mieè wspólnego asystenta Teraz zaáó my, e zapisujemy dane pracowników na dysk. Oczywi cie nie mo emy zapisaü i przywróciü adresów obiektów asystentów, poniewa po ponownym zaáadowaniu obiekt asystenta najprawdopodobniej znajdzie si w zupeánie innym miejscu pami ci. Zamiast tego ka dy obiekt zostaje zapisany z numerem seryjnym i st d wáa nie pochodzi okre lenie serializacja. Oto jej algorytm: Q Wszystkim napotkanym referencjom do obiektów nadawane s numery seryjne (patrz rysunek 1.7). Q Je li referencja do obiektu zostaáa napotkana po raz pierwszy, obiekt zostaje zapisany w strumieniu. Q Je eli obiekt zostaá ju zapisany, Java zapisuje, e w danym miejscu znajduje si „ten sam obiekt, co pod numerem seryjnym x”. Wczytuj c obiekty z powrotem, Java odwraca caá procedur . Q Gdy obiekt pojawia si w strumieniu po raz pierwszy, Java tworzy go, inicjuje danymi ze strumienia i zapami tuje zwi zek pomi dzy numerem i referencj do obiektu.
  • 50. 58 Java. Techniki zaawansowane Rysunek 1.7. Przykäad serializacji obiektów Q Gdy natrafi na znacznik „ten sam obiekt, co pod numerem seryjnym x”, sprawdza, gdzie znajduje si obiekt o danym numerze, i nadaje referencji do obiektu adres tego miejsca. W tym rozdziale korzystamy z serializacji, aby zapisaè zbiór obiektów na dysk, a pó - niej z powrotem je wczytaè. Innym bardzo wa nym zastosowaniem serializacji jest przesyäanie obiektów przez sieè na inny komputer. Podobnie jak adresy pamiöci sñ bezu- yteczne dla pliku, tak samo sñ bezu yteczne dla innego rodzaju procesora. Poniewa serializacja zastöpuje adresy pamiöci numerami seryjnymi, mo emy transportowaè zbiory danych z jednej maszyny na drugñ. Omówimy to zastosowanie przy okazji wywoäywania zdalnych metod w rozdziale 5. Listing 1.4 zawiera program zapisuj cy i wczytuj cy sieü powi zanych obiektów klas Em- ployee i Manager (niektóre z nich maj referencj do tego samego asystenta). Zwróü uwag , e po wczytaniu istnieje tylko jeden obiekt ka dego asystenta — gdy pracownik newStaff[1] dostaje podwy k , znajduje to odzwierciedlenie za pomoc pól secretary obiektów klasy Manager. Listing 1.4. ObjectStreamTest.java import java.io.*; import java.util.*; /** * @version 1.10 17 Aug 1998 * @author Cay Horstmann */ class ObjectStreamTest { public static void main(String[] args)
  • 51. RozdziaÄ 1. Q Strumienie i pliki 59 { Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1); Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15); carl.setSecretary(harry); Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15); tony.setSecretary(harry); Employee[] staff = new Employee[3]; staff[0] = carl; staff[1] = harry; staff[2] = tony; try { // zapisuje rekordy wszystkich pracowników w pliku employee.dat ObjectOutputStream out = new ObjectOutputStream(new ´FileOutputStream("employee.dat")); out.writeObject(staff); out.close(); // wczytuje wszystkie rekordy do nowej tablicy ObjectInputStream in = new ObjectInputStream(new ´FileInputStream("employee.dat")); Employee[] newStaff = (Employee[]) in.readObject(); in.close(); // podnosi wynagrodzenie asystenta newStaff[1].raiseSalary(10); // wy wietla wszystkie rekordy for (Employee e : newStaff) System.out.println(e); } catch (Exception e) { e.printStackTrace(); } } } class Employee implements Serializable { public Employee() { } public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public String getName() {
  • 52. 60 Java. Techniki zaawansowane return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ´",hireDay=" + hireDay + "]"; } private String name; private double salary; private Date hireDay; } class Manager extends Employee { /** * Tworzy obiekt klasy Manager nie inicjujæc pola secretary * @param n nazwisko pracownika * @param s wynagrodzenie * @param year rok zatrudnienia * @param month miesiæc zatrudnienia * @param day dzie zatrudnienia */ public Manager(String n, double s, int year, int month, int day) { super(n, s, year, month, day); secretary = null; } /** * Przypisuje asystenta mened erowi. * @param s asystent */ public void setSecretary(Employee s) { secretary = s; }
  • 53. RozdziaÄ 1. Q Strumienie i pliki 61 public String toString() { return super.toString() + "[secretary=" + secretary + "]"; } private Employee secretary; } java.io.ObjectOutputStream 1.1 Q ObjectOutputStream(OutputStream wy) tworzy obiekt ObjectOutputStream, dzi ki któremu mo esz zapisywaü obiekty do podanego strumienia wyj cia. Q void writeObject(Object ob) zapisuje podany obiekt do ObjectOutputStream. Metoda ta zachowuje klas obiektu, sygnatur klasy oraz warto ci wszystkich niestatycznych, nieprzechodnich pól skáadowych tej klasy, a tak e jej nadklas. java.io.ObjectInputStream 1.1 Q ObjectInputStream(InputStream we) tworzy obiekt ObjectInputStream, dzi ki któremu mo esz odczytywaü informacje z podanego strumienia wej cia. Q Object readObject() wczytuje obiekt z ObjectInputStream. Pobiera klas obiektu, sygnatur klasy oraz warto ci wszystkich niestatycznych, nieprzechodnich pól skáadowych tej klasy, a tak e jej nadklas. Przeprowadza deserializacj , pozwalaj c na przyporz dkowanie obiektów referencjom. Format pliku serializacji obiektów Serializacja obiektów powoduje zapisanie danych obiektu w okre lonym formacie. Oczy- wi cie, mo emy u ywaü metod writeObject/readObject, nie wiedz c nawet, która sekwencja bajtów reprezentuje dany obiekt w pliku. Niemniej jednak doszli my do wniosku, e poznanie formatu danych b dzie bardzo pomocne, poniewa daje wgl d w proces obsáugi obiektów przez strumienie. Poniewa poni szy tekst jest peáen technicznych detali, to je li nie jeste zainteresowany implementacj serializacji, mo esz pomin ü lektur tego podrozdziaáu. Ka dy plik zaczyna si dwubajtow „magiczn liczb ”: AC ED po której nast puje numer wersji formatu serializacji obiektów, którym aktualnie jest 00 05 (w tym podrozdziale do opisywania bajtów b dziemy u ywaü notacji szesnastkowej). Pó niej nast puje sekwencja obiektów, w takiej kolejno ci, w jakiej zostaáy one zapisane.
  • 54. 62 Java. Techniki zaawansowane àa cuchy zapisywane s jako 74 dáugo ü (2 bajty) znaki Dla przykáadu, áa cuch "Harry" b dzie wygl daá tak: 74 00 05 Harry Znaki Unicode zapisywane s w zmodyfikowanym formacie UTF-8. Wraz z obiektem musi zostaü zapisana jego klasa. Opis klasy zawiera: Q nazw klasy, Q unikalny numer ID stanowi cy „odcisk” wszystkich danych skáadowych i sygnatur metod, Q zbiór flag opisuj cy metod serializacji, Q opis pól skáadowych. Java tworzy wspomniany „odcisk” klasy, pobieraj c opisy klasy, klasy bazowej, interfejsów, typów pól danych oraz sygnatury metod w postaci kanonicznej, a nast pnie stosuje do nich algorytm SHA (Secure Hash Algorithm). SHA to szybki algorytm, tworz cy „odciski palców” dla du ych bloków danych. Niezale nie od rozmiaru oryginalnych danych, „odciskiem” jest zawsze pakiet 20 bajtów. Jest on two- rzony za pomoc sekwencji operacji binarnych, dzi ki którym mo emy mieü stuprocentow pewno ü, e je eli zachowana informacja zmieni si , zmianie ulegnie równie jej „odcisk palca”. SHA jest ameryka skim standardem, rekomendowanym przez Narodowy Instytut Nauki i Technologii (National Institute of Science and Technology — NIST; aby dowiedzieü si wi cej o SHA, zajrzyj np. do Cryptography and Network Security: Principle and Practice, autorstwa Williama Stallingsa, wydanej przez Prentice Hall). Jednak e Java korzysta jedynie z pierwszych o miu bajtów kodu SHA. Mimo to nadal jest bardzo prawdopodobne, e „odcisk” zmieni si , je eli ulegn zmianie pola skáadowe lub metody. W chwili odczytu danych Java sprawdza, u ywaj c „odcisku” klasy, czy definicja klasy nie ulegáa zmianie. Oczywi cie, w praktyce klasy ulegaj zmianie i mo e si okazaü, e program b dzie musiaá wczytaü starsze wersje obiektów. Zagadnienie to omówimy w punkcie „Wersje” na stronie 71. Oto, w jaki sposób przechowywany jest identyfikator klasy: 72 dáugo ü nazwy klasy (2 bajty) nazwa klasy „odcisk” klasy (8 bajtów) zbiór flag (1 bajt) liczba deskryptorów pól skáadowych (2 bajty) deskryptory pól skáadowych
  • 55. RozdziaÄ 1. Q Strumienie i pliki 63 78 (znacznik ko ca) typ klasy bazowej (70, je li nie istnieje) Bajt flag skáada si z trzybitowych masek, zdefiniowanych w java.io.ObjectStreamCon ´stants: java.io.ObjectStreamConstants: static final byte SC_WRITE_METHOD = 1; // klasa posiada metodú writeObject zapisujæcæ dodatkowe dane static final byte SC_SERIALIZABLE = 2; // klasa implementuje interfejs Serializable static final byte SC_EXTERNALIZABLE = 4; // klasa implementuje interfejs Externalizable Interfejs Externalizable omówimy w dalszej cz ci rozdziaáu. Klasy implementuj ce Exter ´nalizable udost pniaj wáasne metody wczytuj ce i zapisuj ce, które przejmuj obsáug nad swoimi polami skáadowymi. Klasy, które budujemy, implementuj interfejs Serializable i b d mieü bajt flag o warto ci 02. Jednak np. klasa java.util.Date implementuje Externa ´lizable i jej bajt flag ma warto ü 03. Ka dy deskryptor pola skáadowego skáada si z nast puj cych elementów: Q kod typu (1 bajt), Q dáugo ü nazwy pola (2 bajty), Q nazwa pola, Q nazwa klasy (je eli pole skáadowe jest obiektem). Kod typu mo e mieü jedn z nast puj cych warto ci: B byte C char D double F float I int J long L obiekt S short Z boolean [ tablica Je eli kodem typu jest L, zaraz za nazw pola skáadowego znajdzie si nazwa jego typu. àa cuchy nazw klas i pól skáadowych nie zaczynaj si od 74, w przeciwie stwie do typów pól skáadowych. Typy pól skáadowych u ywaj troch innego sposobu kodowania nazw, a dokáa- dniej — formatu u ywanego przez metody macierzyste. Dla przykáadu, pole pensji klasy Employee zostanie zapisane jako: D 00 06 salary
  • 56. 64 Java. Techniki zaawansowane A oto kompletny opis klasy Employee: 72 00 08 Employee E6 D2 86 7D AE AC 18 1B 02 „Odcisk” oraz flagi 00 03 Liczba pól skáadowych D 00 06 salary Typ i nazwa pola skáadowego L 00 07 hireDay Typ i nazwa pola skáadowego 74 00 10 Ljava/util/Date; Nazwa klasy pola skáadowego — String L 00 04 name Typ i nazwa pola skáadowego 74 00 12 Ljava/lang/String; Nazwa klasy pola skáadowego — String 78 Znacznik ko ca 70 Brak nadklasy Opisy te s do ü dáugie. Je eli w pliku jeszcze raz musi si znale ü opis tej samej klasy, zostanie u yta forma skrócona: 71 numer seryjny (4 bajty) Numer seryjny wskazuje na poprzedni opis danej klasy. Schemat numerowania omówimy pó niej. Obiekt jest przechowywany w nast puj cej postaci: 73 opis klasy dane obiektu Dla przykáadu, oto zapis obiektu klasy Employee: 40 E8 6A 00 00 00 00 Warto ü pola salary — double 73 Warto ü pola hireDay — nowy obiekt 71 00 7E 00 08 Istniej ca klasa java.util.Date 77 08 00 00 00 91 1B 4E B1 80 78 Zawarto ü zewn trzna — szczegóáy poni ej 74 00 0C Harry Hacker Warto ü pola name — String Jak widzimy, plik danych zawiera informacje wystarczaj ce do odtworzenia obiektu klasy Employee. Tablice s zapisywane w nast puj cy sposób: 75 opis klasy liczba elementów (4 bajty) elementy Nazwa klasy tablicy jest zachowywana w formacie u ywanym przez metody macierzyste (ró ni si on troch od formatu nazw innych klas). W tym formacie nazwy klas zaczynaj si od L, a ko cz rednikiem. Dla przykáadu, tablica trzech obiektów typu Employee zaczyna si tak:
  • 57. RozdziaÄ 1. Q Strumienie i pliki 65 75 Tablica 72 00 0C [LEmployee; Nowa klasa, dáugo ü áa cucha, nazwa klasy Employee[] FC BF 36 11 C5 91 11 C7 02 „Odcisk” oraz flagi 00 00 Liczba pól skáadowych 78 Znacznik ko ca 70 Brak nadklasy 00 00 00 03 Liczba komórek tablicy Zauwa my, e „odcisk” tablicy obiektów Employee ró ni si od „odcisku” samej klasy Employee. Wszystkie obiekty (á cznie z tablicami i áa cuchami) oraz wszystkie opisy klas w chwili zapisywania do pliku otrzymuj numery seryjne. Numery seryjne zaczynaj si od warto ci 00 7E 00 00. Przekonali my si ju , e peány opis jest wykonywany tylko raz dla ka dej klasy. Nast pne opisy po prostu wskazuj na pierwszy. W poprzednim przykáadzie kolejna referencja klasy Date zostaáa zakodowana w nast puj cy sposób: 71 00 7E 00 08 Ten sam mechanizm jest stosowany dla obiektów. Je eli zapisywana jest referencja obiektu, który zostaá ju wcze niej zapisany, nowa referencja zostanie zachowana w dokáadnie ten sam sposób, jako 71 plus odpowiedni numer seryjny. Z kontekstu zawsze jasno wynika, czy dany numer seryjny dotyczy opisu klasy, czy obiektu. Referencja null jest zapisywana jako 70 Oto plik zapisany przez program ObjectRefTest z poprzedniego podrozdziaáu, wraz z komen- tarzami. Je li chcesz, uruchom program, spójrz na zapis pliku employee.dat w notacji szes- nastkowej i porównaj z poni szymi komentarzami. Zwróü uwag na wiersze zamieszczone pod koniec pliku, zawieraj ce referencje zapisanego wcze niej obiektu. AC ED 00 05 Nagáówek pliku 75 Tablica staff (nr #1) 72 00 0C [LEmployee; Nowa klasa, dáugo ü áa cucha, nazwa klasy Employee[] (nr #0) FC BF 36 11 C5 91 11 C7 02 „Odcisk” oraz flagi 00 00 Liczba pól skáadowych 78 Znacznik ko ca 70 Brak klasy bazowej 00 00 00 03 Liczba elementów tablicy 73 staff[0] — nowy obiekt (nr #7)
  • 58. 66 Java. Techniki zaawansowane 72 00 08 Manager Nowa klasa, dáugo ü áa cucha, nazwa klasy (nr #2) 36 06 AE 13 63 8F 59 B7 02 „Odcisk” oraz flagi 00 01 Liczba pól skáadowych L 00 08 secretary Typ i nazwa pola skáadowego 74 00 0B LEmployee; Nazwa klasy pola skáadowego — String (nr #3) 78 Znacznik ko ca 72 00 09 Employee Nadklasa — nowa klasa, dáugo ü áa cucha, nazwa klasy (nr #4) E6 D2 86 7D AE AC 18 1B 02 „Odcisk” oraz flagi 00 03 Liczba pól skáadowych D 00 06 salary Typ i nazwa pola skáadowego L 00 11 hireDay Typ i nazwa pola skáadowego 74 00 10 Ljava/util/Date; Nazwa klasy pola skáadowego — String (nr #5) L 00 08 name Typ i nazwa pola skáadowego 74 00 12 Ljava/lang/String; Nazwa klasy pola skáadowego — String (nr #6) 78 Znacznik ko ca 70 Brak klasy bazowej 40 F3 88 00 00 00 00 00 Warto ü pola salary — double 73 Warto ü pola hireDay — nowy obiekt (nr #9) 72 00 0E java.util.Date Nowa klasa, dáugo ü áa cucha, nazwa klasy (nr #8) 68 6A 81 01 4B 59 74 19 03 „Odcisk” oraz flagi 00 00 Brak zmiennych skáadowych 78 Znacznik ko ca 70 Brak klasy bazowej 77 08 Zawarto ü zewn trzna, liczba bajtów 00 00 00 83 E9 39 E0 00 Data 78 Znacznik ko ca 74 00 0C Carl Cracker Warto ü pola name — String (nr #10) 73 Warto ü pola secretary — nowy obiekt (nr #11) 71 00 7E 00 04 Istniej ca klasa (u yj nr #4) 40 E8 6A 00 00 00 00 00 Warto ü pola pensja — double
  • 59. RozdziaÄ 1. Q Strumienie i pliki 67 73 Warto ü pola dzienZatrudnienia — nowy obiekt (nr #12) 71 00 7E 00 08 Istniej ca klasa (u yj nr #8) 77 08 Zawarto ü zewn trzna, liczba bajtów 00 00 00 91 1B 4E B1 80 Data 78 Znacznik ko ca 74 00 0C Harry Hacker Warto ü pola name — String (nr #13) 71 00 7E 00 0B staff[1] — istniej cy obiekt (u yj nr #11) 73 obsluga[2] — nowy obiekt (nr #14) 71 00 7E 00 08 Istniej ca klasa (u yj nr #4) 40 E3 88 00 00 00 00 00 Warto ü pola salary — double 73 Warto ü pola hireDay — nowy obiekt (nr #15) 71 00 7E 00 08 Istniej ca klasa (u yj nr #8) 77 08 Zawarto ü zewn trzna, liczba bajtów 00 00 00 94 6D 3E EC 00 00 Data 78 Znacznik ko ca 74 00 0D Tony Tester Warto ü pola name — String (nr #16) 71 00 7E 00 0B Warto ü pola secretary — istniej cy obiekt (u yj nr #11) Zazwyczaj znajomo ü dokáadnego format pliku nie jest wa na (o ile nie próbujesz dokonaü zmian w samym pliku). Powinni my jednak pami taü, e: Q strumie obiektów zapisuje typy i pola skáadowe wszystkich obiektów, Q ka demu obiektowi zostaje przypisany numer seryjny, Q powtarzaj ce si odwoáania do tego samego obiektu s przechowywane jako referencje jego numeru seryjnego. Modyfikowanie domyÊlnego mechanizmu serializacji Niektóre dane nie powinny byü serializowane — np. warto ci typu integer reprezentuj ce uchwyty plików lub okien, czytelne wyá cznie dla metod rodzimych. Gdy wczytamy takie dane ponownie lub przeniesiemy je na inn maszyn , najcz ciej oka si bezu yteczne. Co gorsza, nieprawidáowe warto ci tych zmiennych mog spowodowaü bá dy w dziaáaniu metod rodzi- mych. Dlatego Java obsáuguje prosty mechanizm zapobiegaj cy serializacji takich danych. Wystarczy oznaczyü je sáowem kluczowym transient. Sáowem tym nale y równie oznaczyü pola, których klasy nie s serializowalne. Pola oznaczone jako transient s zawsze pomi- jane w procesie serializacji.
  • 60. 68 Java. Techniki zaawansowane Mechanizm serializacji na platformie Java udost pnia sposób, dzi ki któremu indywidualne klasy mog sprawdzaü prawidáowo ü danych lub w jakikolwiek inny sposób wpáywaü na zachowanie strumienia podczas domy lnych operacji odczytu i zapisu. Klasa implementuj ca interfejs Serializable mo e zdefiniowaü metody o sygnaturach private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException; private void writeObject(ObjectOutputStream out) throws IOException; Dzi ki temu obiekty nie b d automatycznie serializowane — Java wywoáa dla nich powy sze metody. A oto typowy przykáad. Wielu klas nale cych do pakietu java.awt.geom, takich jak na przykáad klasa Point2D.Double, nie da si serializowaü. Przypu ümy zatem, e chcemy serializowaü klas LabeledPoint zawieraj c pola typu String i Point2D.Double. Najpierw musimy oznaczyü pole Point2D.Double sáowem kluczowym transient, aby unikn ü wyj tku NotSerializableEx ´ception. public class LabeledPoint implements Serializable { . . . private String label; private transient Point2D.Double point; } W metodzie writeObject najpierw zapiszemy opis obiektu i pole typu String, wywoáuj c metod defaultWriteObject. Jest to specjalna metoda klasy ObjectOutputStream, która mo e byü wywoáywana jedynie przez metod writeObject klasy implementuj cej interfejs Seria ´lizable. Nast pnie zapiszemy wspóárz dne punktu, u ywaj c standardowych wywoáa klasy DataOutput. private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeDouble(point.getX()); out.writeDouble(point.getY()); } Implementuj c metod readObject, odwrócimy caáy proces: private void readObject(ObjectInputStream in) throws IOException { in.defaultReadObject(); double x = in.readDouble(); double y = in.readDouble(); point = new Point2D.Double(x, y); } Innym przykáadem jest klasa java.util.Date, która dostarcza wáasnych metod readObject i writeObject. Metody te zapisuj dat jako liczb milisekund, które upáyn áy od póánocy 1 sty- cznia 1970 roku, czasu UTC. Klasa Date stosuje skomplikowan reprezentacj wewn trzn ,
  • 61. RozdziaÄ 1. Q Strumienie i pliki 69 która przechowuje zarówno obiekt klasy Calendar, jak i licznik milisekund, co pozwala zoptymalizowaü operacje wyszukiwania. Stan obiektu klasy Calendar jest wtórny i nie musi byü zapisywany. Metody readObject i writeObject odczytuj i zapisuj jedynie dane wáasnej klasy. Nie zajmuj si przechowywaniem i odtwarzaniem danych klasy bazowej b d jakichkolwiek innych infor- macji o klasie. Klasa mo e równie zdefiniowaü wáasny mechanizm zapisywania danych, nie ogl daj c si na serializacj . Aby tego dokonaü, klasa musi zaimplementowaü interfejs Externalizable. Oznacza to implementacj dwóch metod: public void readExternal(ObjectInputSream in) throws IOException, ClassNotFoundException; public void writeExternal(ObjectOutputStream out) throws IOException; W przeciwie stwie do omówionych wcze niej metod readObject i writeObject, te metody s caákowicie odpowiedzialne za zapisanie i odczytanie obiektu, á cznie z danymi klasy bazowej. Mechanizm serializacji zapisuje jedynie klas obiektu w strumieniu. Odtwarzaj c obiekt imple- mentuj cy interfejs Externalizable, strumie obiektów wywoáuje domy lny konstruktor, a na- st pnie metod readExternal. Oto jak mo emy zaimplementowaü te metody w klasie Employee: public void readExternal(ObjectInput s) throws IOException { name = s.readUTF(); salary = s.readDouble(); hireDay = new Date(s.readLong()); } public void writeExternal(ObjectOutput s) throws IOException { s.writeUTF(name); s.writeDouble(salary); s.writeLong(hireDay.getTime()); } Serializacja jest do è powolnym mechanizmem, poniewa maszyna wirtualna musi rozpoznaè strukturö ka dego obiektu. Je eli zale y nam na efektywno ci dziaäania, a jednocze nie chcemy wczytywaè i zapisywaè du ñ liczbö obiektów pewnej klasy, powin- ni my rozwa yè mo liwo è zastosowania interfejsu Externalizable. Artykuä na stronie http://java.sun.com/developer/TechTips/2000/tt0425.html stwierdza, e w przypadku klasy reprezentujñcej pracowników skorzystanie z wäasnych metod zapisu i odczytu byäo o 35 – 40% szybsze ni domy lna serializacja. W przeciwieþstwie do metod writeObject i readObject, które sñ prywatne i mogñ zostaè wywoäane wyäñcznie przez mechanizm serializacji, metody writeExternal i read ´External sñ publiczne. W szczególno ci metoda readExternal potencjalnie mo e byè wykorzystana do modyfikacji stanu istniejñcego obiektu.
  • 62. 70 Java. Techniki zaawansowane Serializacja singletonów i wyliczeÆ Szczególn uwag nale y zwróciü na serializacj obiektów, które z zaáo enia maj byü uni- kalne. Ma to miejsce w przypadku implementacji singletonów i wylicze . Je li u ywamy w programach konstrukcji enum wprowadzonej w Java SE 5.0, to nie musimy przejmowaü si serializacj — wszystko b dzie dziaáaü poprawnie. Zaáó my jednak, e mamy starszy kod, który tworzy typy wyliczeniowe w nast puj cy sposób: public class Orientation { public static final Orientation HORIZONTAL = new Orientation(1); public static final Orientation VERTICAL = new Orientation(2); private Orientation(int v) { value = v; } private int value; } Powy szy sposób zapisu byá powszechnie stosowany, zanim wprowadzono typ wyliczeniowy w j zyku Java. Zwróümy uwag , e konstruktor klasy Orientation jest prywatny. Dzi ki temu powstan jedynie obiekty Orientation.HORIZONTAL i Orientation.VERTICAL. Obiekty tej klasy mo emy porównywaü za pomoc operatora ==: if (orientation == Orientation.HORIZONTAL) . . . Je li taki typ wyliczeniowy implemenuje interfejs Serializable, to domy lny sposób seria- lizacji oka e si w tym przypadku niewáa ciwy. Przypu ümy, e zapisali my warto ü typu Orientation i wczytujemy j ponownie: Orientation original = Orientation.HORIZONTAL; ObjectOutputStream out = . . .; out.write(value); out.close(); ObjectInputStream in = . . .; Orientation saved = (Orientation) in.read(); Oka e si , e porównanie if (saved == Orientation.HORIZONTAL) . . . da wynik negatywny. W rzeczywisto ci bowiem warto ü saved jest zupeánie nowym obiek- tem typu Orientation i nie jest ona równa adnej ze staáych wst pnie zdefiniowanych przez t klas . Mimo e konstruktor klasy jest prywatny, mechanizm serializacji mo e tworzyü zupeánie nowe obiekty tej klasy! Aby rozwi zaü ten problem, musimy zdefiniowaü specjaln metod serializacji o nazwie readResolve. Je li metoda readResolve jest zdefiniowana, zostaje wywoáana po deserializacji obiektu. Musi ona zwróciü obiekt, który nast pnie zwróci metoda readObject. W naszym przy- káadzie metoda readResolve sprawdzi pole value i zwróci odpowiedni staá : protected Object readResolve() throws ObjectStreamException { if (value == 1) return Orientation.HORIZONTAL; if (value == 2) return Orientation.VERTICAL; return null; // to nie powinno siú zdarzyè }
  • 63. RozdziaÄ 1. Q Strumienie i pliki 71 Musimy zatem pami taü o zdefiniowaniu metody readResolve dla wszystkich wylicze kon- struowanych w tradycyjny sposób i wszystkich klas implementuj cych wzorzec singletonu. Wersje Je li u ywamy serializacji do przechowywania obiektów, musimy zastanowiü si , co si z nimi stanie, gdy powstan nowe wersje programu. Czy wersja 1.1 b dzie potrafiáa czytaü starsze pliki? Czy u ytkownicy wersji 1.0 b d mogli wczytywaü pliki tworzone przez now wersj ? Na pierwszy rzut oka wydaje si to niemo liwe. Wraz ze zmian definicji klasy zmienia si kod SHA, a strumie obiektów nie odczyta obiektu o innym „odcisku palca”. Jednak e klasa mo e zaznaczyü, e jest kompatybilna ze swoj wcze niejsz wersj . Aby tego dokonaü, mu- simy pobraü „odcisk palca” wcze niejszej wersji tej klasy. Do tego celu u yjemy serialver, programu b d cego cz ci JDK. Na przykáad, uruchamiaj c serialver Employee otrzymujemy: Employee: static final long serialVersionUID = -876875122904779311L Je eli uruchomimy serialver z opcj -show, program wy wietli okno dialogowe (rysunek 1.8). Rysunek 1.8. Wersja graficzna programu serialver Wszystkie pó niejsze wersje tej klasy musz definiowaü staá serialVersionUID o tym samym „odcisku palca”, co wersja oryginalna. class Employee implements Serializable // wersja 1.1 { . . . public static final long serialVersionUID = -1814239825517340645L; } Klasa posiadaj ca statyczne pole skáadowe o nazwie serialVersionUID nie obliczy wáasnego „odcisku palca”, ale skorzysta z ju istniej cej warto ci. Od momentu, gdy w danej klasie umie cisz powy sz staá , system serializacji b dzie mógá odczytywaü ró ne wersje obiektów tej samej klasy. Je li zmieni si tylko metody danej klasy, sposób odczytu danych nie ulegnie zmianie. Jednak e je eli zmieni si pole skáadowe, mo emy mieü pewne problemy. Dla przykáadu, stary obiekt mo e posiadaü wi cej lub mniej pól skáadowych ni aktualny, albo te typy danych mog si ró niü. W takim wypadku strumie obiektów spróbuje skonwertowaü obiekt na aktualn wersj danej klasy.
  • 64. 72 Java. Techniki zaawansowane Strumie obiektów porównuje pola skáadowe aktualnej wersji klasy z polami skáadowymi wersji znajduj cej si w strumieniu. Oczywi cie, strumie bierze pod uwag wyá cznie niesta- tyczne, nieulotne pola skáadowe. Je eli dwa pola maj te same nazwy, lecz ró ne typy, strumie nawet nie próbuje konwersji — obiekty s niekompatybilne. Je eli obiekt w strumieniu posiada pola skáadowe nieobecne w aktualnej wersji, strumie ignoruje te dodatkowe dane. Je eli aktu- alna wersja posiada pola skáadowe nieobecne w zapisanym obiekcie, dodatkowe zmienne otrzymuj swoje domy lne warto ci (null dla obiektów, 0 dla liczb i false dla warto ci logicznych). Oto przykáad. Zaáó my, e zapisali my na dysku pewn liczb obiektów klasy Employee, u ywaj c przy tym oryginalnej (1.0) wersji klasy. Teraz wprowadzamy now wersj 2.0 klasy Employee, dodaj c do niej pole skáadowe department. Rysunek 1.9 przedstawia, co si dzieje, gdy obiekt wersji 1.0 jest wczytywany przez program korzystaj cy z obiektów 2.0. Pole depart- ment otrzymuje warto ü null. Rysunek 1.10 ilustruje odwrotn sytuacj — program korzy- staj cy z obiektów 1.0 wczytuje obiekt 2.0. Dodatkowe pole department jest ignorowane. Rysunek 1.9. Odczytywanie obiektu o mniejszej liczbie pól Rysunek 1.10. Odczytywanie obiektu o wiökszej liczbie pól Czy ten proces jest bezpieczny? To zale y. Opuszczanie pól skáadowych wydaje si byü bez- bolesne — odbiorca wci posiada dane, którymi potrafi manipulowaü. Nadawanie warto ci null nie jest ju tak bezpieczne. Wiele klas inicjalizuje wszystkie pola skáadowe, nadaj c im w konstruktorach niezerowe warto ci, tak wi c metody mog byü nieprzygotowane do obsáu-
  • 65. RozdziaÄ 1. Q Strumienie i pliki 73 giwania warto ci null. Od projektanta klasy zale y, czy zaimplementuje w metodzie readObject dodatkowy kod poprawiaj cy wyniki wczytywania ró nych wersji danych, czy te doá czy do metod obsáug warto ci null. Serializacja w roli klonowania Istnieje jeszcze jedno, ciekawe zastosowanie mechanizmu serializacji — umo liwia on áatwe klonowanie obiektów klas implementuj cych interfejs Serializable. Aby sklonowaü obiekt, po prostu zapisujemy go w strumieniu, a nast pnie odczytujemy z powrotem. W efekcie otrzy- mujemy nowy obiekt, b d cy dokáadn kopi istniej cego obiektu. Nie musisz zapisywaü tego obiektu do pliku — mo esz skorzystaü z ByteArrayOutputStream i zapisaü dane do tablicy bajtów. Kod z listingu 1.5 udowadnia, e aby otrzymaü metod clone „za darmo”, wystarczy roz- szerzyü klas SerialCloneable. Listing 1.5. SerialCloneTest.java /** @version 1.20 17 Aug 1998 @author Cay Horstmann */ import java.io.*; import java.util.*; public class SerialCloneTest { public static void main(String[] args) { Employee harry = new Employee("Harry Hacker", 35000, 1989, 10, 1); // klonuje obiekt harry Employee harry2 = (Employee) harry.clone(); // modyfikuje obiekt harry harry.raiseSalary(10); // teraz obiekt harry i jego klon sæ ró ne System.out.println(harry); System.out.println(harry2); } } /** Klasa, której metoda clone wykorzystuje serializacjú. */ class SerialCloneable implements Cloneable, Serializable { public Object clone() { try { // zapisuje obiekt w tablicy bajtów ByteArrayOutputStream bout = new ByteArrayOutputStream();
  • 66. 74 Java. Techniki zaawansowane ObjectOutputStream out = new ObjectOutputStream(bout); out.writeObject(this); out.close(); // wczytuje klon obiektu z tablicy bajtów ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream in = new ObjectInputStream(bin); Object ret = in.readObject(); in.close(); return ret; } catch (Exception e) { return null; } } } /** Znana ju klasa Employee, tym razem jako pochodna klasy SerialCloneable. */ class Employee extends SerialCloneable { public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public String toString() { return getClass().getName()
  • 67. RozdziaÄ 1. Q Strumienie i pliki 75 + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; } private String name; private double salary; private Date hireDay; } Nale y jednak byü wiadomym, e opisany sposób klonowania, jakkolwiek sprytny, zwykle oka e si znacznie wolniejszy ni metoda clone jawnie tworz ca nowy obiekt i kopiuj ca lub klonuj ca pola danych. Zarz¾dzanie plikami Potrafimy ju zapisywaü i wczytywaü dane z pliku. Jednak e obsáuga plików to co wi cej ni tylko operacje zapisu i odczytu. Klasa File zawiera metody potrzebne do obsáugi systemu pli- ków na komputerze u ytkownika. Na przykáad, mo emy wykorzystaü klas File, aby spraw- dziü, kiedy nast piáa ostatnia modyfikacja danego pliku, oraz usun ü lub zmieniü nazw tego pliku. Innymi sáowy, klasy strumieni zajmuj si zawarto ci plików, natomiast klasa File interesuje si sposobem przechowywania pliku na dysku. Jak to ma czösto miejsce w jözyku Java, klasa File przyjmuje metodö najmniejszego wspólnego mianownika. Na przykäad, w systemie Windows mo emy sprawdziè (lub zmieniè) flagö „tylko do odczytu” dla danego pliku, ale mimo i mo emy równie sprawdziè, czy plik jest ukryty, nie mo emy ukryè go samemu, je li nie zastosujemy w tym celu odpowiedniej metody macierzystej. Najprostszy konstruktor obiektu File pobiera peán nazw pliku. Je eli nie podamy cie ki dost pu, Java u ywa bie cego katalogu. Na przykáad: File p = new File("test.txt"); tworzy obiekt pliku o podanej nazwie, znajduj cego si w bie cym katalogu (bie cym katalogiem jest katalog, w którym program jest uruchamiany). Poniewa znak w äaþcuchach na platformie Java jest traktowany jako poczñtek sek- wencji specjalnej, musimy pamiötaè, aby w cie kach dostöpu do plików systemu Windows u ywaè sekwencji (np. C:Windowswin.ini). W systemie Windows mo emy równie korzystaè ze znaku / (np. C:/Windows/win.ini), poniewa wiökszo è systemów obsäugi plików Windows interpretuje znaki / jako separatory cie ki dostöpu. Jednak e nie zalecamy tego rozwiñzania — zachowanie funkcji systemu Windows mo e siö zmieniaè, a inne systemy operacyjne mogñ u ywaè jeszcze innych separatorów. Je eli piszemy apli- kacjö przeno nñ, powinni my u ywaè separatora odpowiedniego dla danego systemu operacyjnego. Jego znak jest przechowywany jako staäa File.separator.
  • 68. 76 Java. Techniki zaawansowane Wywoáanie tego konstruktora nie tworzy nowego pliku, je eli plik o danej nazwie nie istnieje. Tworzenie nowego pliku na podstawie obiektu File odbywa si przy u yciu jednego z kon- struktorów klas strumieni lub metody createNewFile klasy File. Metoda createNewFile tworzy nowy plik tylko pod warunkiem, e plik o podanej nazwie nie istnieje i zwraca warto ü logiczn , by poinformowaü, czy operacja si udaáa. Z drugiej strony, gdy utworzymy obiekt typu File, dzi ki metodzie exists mo emy sprawdziü, czy plik o danej nazwie istnieje. Dla przykáadu, poni szy program prawie na pewno wydru- kuje „false”, równocze nie podaj c cie k dost pu do nieistniej cego pliku. import java.io.*; public class Test { public static void main(String args[]) { File f = new File("plikktóryprawdopodobnieistnieje"); System.out.println(f.getAbsolutePath()); System.our.println(f.exists()); } } Klasa File posiada jeszcze dwa inne konstruktory: File(String path, String name) tworz ce obiekt typu File o podanej nazwie i katalogu okre lonym przez parametr path (je eli path ma warto ü null, konstruktor tworzy obiekt File, korzystaj c z katalogu bie cego). Oprócz tego mo emy równie posáu yü si istniej cym ju obiektem File, wywoáuj c kon- struktor: File(File dir, String name) gdzie obiekt File reprezentuje katalog i, tak jak poprzednio, je eli dir ma warto ü null, konstruktor tworzy nowy obiekt w katalogu bie cym. Co ciekawe, obiekt File mo e reprezentowaü zarówno plik, jak i katalog (byü mo e dlatego, e system operacyjny, na którym pracowali projektanci j zyka Java, pozwalaá traktowaü katalogi tak, jakby byáy plikami). Aby sprawdziü, czy obiekt reprezentuje katalog, czy plik, korzystamy z metod isDirectory i isFile. Dziwne — w zorientowanym obiektowo systemie mogli my si spodziewaü osobnej klasy Directory, byü mo e rozszerzaj cej klas File. Aby obiekt reprezentowaá katalog, musimy podaü konstruktorowi nazw tego katalogu: File tempDir = new File(File.separator + "temp"); Je eli katalog nie istnieje, mo emy utworzyü go przy pomocy metody mkdir: tempDir.mkdir(); Je eli dany obiekt reprezentuje katalog, metoda list() zwróci nam tablic nazw plików znaj- duj cych si w tym katalogu. Program zamieszczony na listingu 1.6 korzysta ze wszystkich omówionych metod, aby wydrukowaü struktur dowolnego katalogu przekazanego jako argu- ment wywoáania w wierszu polece (áatwo mo na zmieniü ten program w klas zwracaj c list podkatalogów do dalszego przetwarzania).
  • 69. RozdziaÄ 1. Q Strumienie i pliki 77 Listing 1.6. FindDirectories.java import java.io.*; /** * @version 1.00 05 Sep 1997 * @author Gary Cornell */ public class FindDirectories { public static void main(String[] args) { // je li brak parametru wywo ania, // rozpoczyna od katalogu nadrzúdnego if (args.length == 0) args = new String[] { ".." }; try { File pathName = new File(args[0]); String[] fileNames = pathName.list(); // wy wietla wszystkie pliki w katalogu for (int i = 0; i < fileNames.length; i++) { File f = new File(pathName.getPath(), fileNames[i]); // je li kolejny katalog, // wywo uje rekurencyjnie metodú main if (f.isDirectory()) { System.out.println(f.getCanonicalPath()); main(new String[] { f.getPath() }); } } } catch (IOException e) { e.printStackTrace(); } } } Zamiast otrzymywaü list wszystkich plików w danym katalogu, mo emy u yü obiektu klasy FileNameFilter jako parametru metody list, aby zmniejszyü rozmiar wynikowej listy plików. Na list b d wtedy wpisywane tylko te obiekty, które speániaj warunki postawione przez interfejs FilenameFilter. Aby zaimplementowaü interfejs FilenameFilter, klasa musi zdefiniowaü metod o nazwie accept. Oto przykáad prostej klasy implementuj cej FilenameFilter, która akceptuje wyá cznie pliki o okre lonym rozszerzeniu: public class ExtensionFilter implements FilenameFilter { public ExtensionFilter(String ext)
  • 70. 78 Java. Techniki zaawansowane { extension = "." + ext; } public boolean accept(File dir, String name) { return name.endsWith(extension); } private String extension; } W przypadku programów przeno nych wyspecyfikowanie nazwy pliku znajduj cego si w okre lonym katalogu jest znacznie trudniejsze. Jak ju o tym wspominali my, okazuje si , e nawet w systemie Windows mo esz u ywaü znaku / jako separatora katalogów (mimo i jest to separator systemu Unix), ale inne systemy mog na to nie pozwoliü, dlatego nie pole- camy tego sposobu. Je eli tworzñc obiekt klasy File, u ywasmy znaków / jako separatorów katalogów, metoda getAbsolutePath zwróci cie kö dostöpu równie zawierajñcñ znaki /, co w systemie Windows bödzie wyglñdaè do è dziwnie. W takim przypadku warto skorzystaè z metody getCanonicalPath — zamienia ona znaki / na znaki . Lepszym pomysáem jest wykorzystanie informacji o u ywanym separatorze, przechowywanej przez statyczne pole skáadowe separator klasy File (w rodowisku Windows b dzie to znak , w rodowisku Unix znak /). Na przykáad: File foo = new File("Documents" + File.separator + "data.txt") Oczywi cie, je eli skorzystamy z drugiej wersji konstruktora File: File foo = new File("Documents", "data.txt"); Java automatycznie wstawi odpowiednie separatory. Poni sze notki API opisuj (naszym zdaniem) najwa niejsze spo ród pozostaáych metod klasy File; sposób ich u ycia powinien byü oczywisty. java.io.File 1.0 Q boolean canRead() Q boolean canWrite() Q boolean canExecute() 6 sprawdza, czy na pliku mog byü wykonywane operacje odczytu i zapisu oraz czy plik mo e byü wykonywany.
  • 71. RozdziaÄ 1. Q Strumienie i pliki 79 Q boolean setReadable(boolean state, boolean ownerOnly) 6 Q boolean setWritable(boolean state, boolean ownerOnly) 6 Q boolean setExecutable(boolean state, boolean ownerOnly) 6 nadaje plikowi odpowiedni wáa ciwo ü. Je li parametr ownerOnly ma warto ü true, to wáa ciwo ü ta jest dost pna jedynie dla wáa ciciela pliku. W przeciwnym razie dotyczy wszystkich u ytkowników. Metoda ta zwraca warto ü true, je li operacja nadania wáa ciwo ci powiodáa si . Q static boolean createTempFile(String prefi, String sufix) 1.2 Q static boolean createTempFile(String prefix, String sufix, File directory) 1.2 tworzy tymczasowy plik w domy lnym katalogu tymczasowym systemu operacyjnego lub w katalogu podanym przez u ytkownika, u ywaj c przedrostka i przyrostka do utworzenia tymczasowej nazwy. Parametry: prefix áa cuch przedrostka o dáugo ci co najmniej trzech znaków. sufix opcjonalny przyrostek. Je eli ma warto ü null, u ywane jest rozszerzenie .tmp. directory katalog, w którym plik ma si znajdowaü. Je eli ma warto ü null, plik jest tworzony w bie cym katalogu roboczym. Q boolean delete() próbuje skasowaü plik; zwraca true, je eli plik zostaá skasowany; w przeciwnym wypadku zwraca false. Q void deleteOnExit() da, aby plik zostaá skasowany, gdy zostanie wyá czona maszyna wirtualna. Q boolean exists() zwraca true, je eli dany plik lub katalog istnieje; w przeciwnym wypadku zwraca false. Q String getAbsolutePath() zwraca áa cuch zawieraj cy absolutn cie k dost pu. Wskazówka: zamiast tej funkcji lepiej korzystaü z getCanonicalPath. Q File getCanonicalFile() 1.2 zwraca obiekt klasy File zawieraj cy kanoniczn cie k dost pu do danego pliku. Oznacza to, e usuwane s zb dne katalogi ".", u ywany jest odpowiedni separator katalogów oraz zale na od systemu operacyjnego obsáuga wielkich i maáych liter. Q String getCanonicalPath() zwraca áa cuch zawieraj cy kanoniczn form cie ki dost pu. Oznacza to, e usuwane s zb dne katalogi ".", u ywany jest odpowiedni separator katalogów oraz zale na od systemu operacyjnego obsáuga wielkich i maáych liter.
  • 72. 80 Java. Techniki zaawansowane Q String getName() zwraca áa cuch zawieraj cy nazw pliku danego obiektu File (áa cuch ten nie zawiera cie ki dost pu). Q String getParent() zwraca áa cuch zawieraj cy nazw katalogu, w którym znajduje si „rodzic” danego obiektu File. Je eli obiekt jest plikiem, „rodzicem” jest po prostu katalog, w którym dany plik si znajduje. Je eli obiekt jest katalogiem, jego „rodzicem” jest jego katalog bazowy lub null, je eli katalog bazowy nie istnieje. Q File getParentFile() 1.2 zwraca obiekt klasy File „rodzica” danego pliku. W notce o getParent znajdziesz definicj „rodzica”. Q String getPath() zwraca áa cuch zawieraj cy cie k dost pu do pliku. Q boolean isDirectory() zwraca true, je eli obiekt reprezentuje katalog; w przeciwnym wypadku zwraca false. Q boolean isFile() zwraca true, je eli obiekt reprezentuje plik — pozostaáe opcje to katalog lub urz dzenie. Q boolean isHidden() 1.2 zwraca true, je eli obiekt reprezentuje plik lub katalog ukryty. Q long lastModified() zwraca dat ostatniej modyfikacji pliku (liczba milisekund od póánocy 1 stycznia 1970 GMT) lub 0, je eli plik nie istnieje. Aby zmieniü t warto ü w obiekt Date, u ywamy konstruktora Date(long). Q long length() zwraca dáugo ü pliku w bajtach lub 0, je eli plik nie istnieje. Q String[] list() zwraca tablic áa cuchów, zawieraj cych nazwy plików i katalogów znajduj cych si w danym obiekcie File, lub null, je eli dany obiekt nie reprezentuje katalogu. Q String[] list(FilenameFilter filter) zwraca tablic nazw plików i katalogów, znajduj cych si w danym obiekcie i speániaj cych warunki filtra, lub null, je eli nie ma takich elementów. Parametry: filter u ywany obiekt typu FilenameFilter. Q File[] listFiles() 1.2 zwraca tablic obiektów klasy File, odpowiadaj cych plikom i katalogom znajduj cym si w danym obiekcie klasy File, lub null, je eli dany obiekt nie reprezentuje katalogu.
  • 73. RozdziaÄ 1. Q Strumienie i pliki 81 Q File[] listFiles(FilenameFilter filter) 1.2 zwraca tablic obiektów klasy File, odpowiadaj cych plikom i katalogom znajduj cym si w danym obiekcie klasy File i speániaj cym warunki filtra, lub null, je eli nie ma takich elementów. Parametry: filter u ywany obiekt typu FilenameFilter. Q static File[] listRoots() 1.2 zwraca tablic obiektów klasy File odpowiadaj c dost pnym katalogom najwy szego poziomu (np. w systemie Windows otrzymasz obiekty klasy File reprezentuj ce zainstalowane dyski — zarówno dyski lokalne, jak i mapowane dyski sieciowe; w systemie Unix otrzymasz po prostu "/"). Q boolean createNewFile() 1.2 je eli plik o nazwie podanej przez obiekt pliku nie istnieje, automatycznie tworzy taki plik. Oznacza to, e sprawdzanie nazwy oraz tworzenie nowego pliku nie zostanie zakáócone przez inn dziaáalno ü systemu. Je eli udaáo si utworzyü nowy plik, metoda zwraca true. Q boolean mkdir() tworzy podkatalog, którego nazwa zostaáa podana przez obiekt pliku. Zwraca true, je eli udaáo si utworzyü katalog; w przeciwnym wypadku zwraca false. Q boolean mkdirs() w przeciwie stwie do mkdir, ta metoda tworzy równie wymagane katalogi po rednie. Zwraca false, je eli którykolwiek z wymaganych katalogów nie mógá zostaü utworzony. Q boolean renameTo(File newName) zwraca true, je eli nazwa zostaáa zmieniona; w przeciwnym wypadku zwraca false. Parametry: newName obiekt klasy File okre laj cy now nazw pliku. Q boolean setLastModified(long time) 1.2 okre la dat ostatniej modyfikacji pliku. Zwraca true, je eli zmiana si powiodáa, w przeciwnym wypadku zwraca false. Parametry: time liczba typu long reprezentuj ca ilo ü milisekund, jakie upáyn áy od póánocy 1 stycznia 1970 GMT. Metoda getTime klasy Date pozwala obliczyü t warto ü. Q boolean setReadOnly() 1.2 zmienia tryb pliku na „tylko do odczytu”. Zwraca true, je eli operacja si powiodáa, w przeciwnym wypadku zwraca false. Q URL toURL() 1.2 konwertuje obiekt klasy File na plik URL. Q long getTotalSpace() 6 Q long getFreeSpace() 6
  • 74. 82 Java. Techniki zaawansowane Q long getUsableSpace() 6 zwraca caákowity rozmiar, liczb nieprzydzielonych bajtów i liczb dost pnych bajtów partycji reprezentowanej przez obiekt klasy File. Je li obiekt klasy File nie reprezentuje partycji, metoda ta zwraca warto ü 0. java.io.FilenameFilter 1.0 Q boolean accept(File dir, String name) powinna zostaü tak zdefiniowana, aby zwracaáa true, je eli dany plik speánia warunki filtra. Parametry: dir obiekt typu File reprezentuj cy katalog, w którym znajduje si dany plik. name nazwa danego pliku. Ulepszona obsÄuga wejÊcia i wyjÊcia Java SE 1.4 udost pnia w pakiecie java.nio szereg nowych rozwi za poprawiaj cych obsáug wej cia i wyj cia w programach. Wspomniany pakiet obsáuguje nast puj ce rozwi zania: Q kodery i dekodery zbiorów znaków, Q nieblokuj ce operacje wej cia i wyj cia, Q pliki mapowane w pami ci, Q blokowanie dost pu do plików. Kodowanie i dekodowanie znaków omówili my ju w punkcie „Zbiory znaków” na stronie 35. Nieblokuj ce operacje wej cia i wyj cia zostan omówione w rozdziale 3., poniewa maj one szczególne znaczenie w przypadku komunikacji w sieci. W nast pnych podrozdziaáach zaj- miemy si zatem omówieniem mapowania plików w pami ci oraz blokowaniem plików. Mapowanie plików w pamiÂci Wi kszo ü systemów operacyjnych oferuje mo liwo ü wykorzystania pami ci wirtualnej do stworzenia „mapy” pliku lub jego fragmentu w pami ci. Dost p do pliku odbywa si wtedy znacznie szybciej ni w tradycyjny sposób. Na ko cu tego podrozdziaáu zamie cili my program, który oblicza sum kontroln CRC32 dla pliku, u ywaj c standardowych operacji wej cia i wyj cia, a tak e pliku mapowanego w pami ci. Na jednej i tej samej maszynie otrzymali my wyniki jego dziaáania przedstawione w tabeli 1.6 dla pliku rt.jar (37 MB) znajduj cego si w katalogu jre/lib pakietu JDK.
  • 75. RozdziaÄ 1. Q Strumienie i pliki 83 Tabela 1.6. Czasy wykonywania operacji na pliku Metoda Czas Zwykáy strumie wej ciowy 110 sekund Buforowany strumie wej ciowy 9,9 sekundy Plik o swobodnym dost pie 162 sekundy Mapa pliku w pami ci 7,2 sekundy Jak áatwo zauwa yü, na naszym komputerze mapowanie pliku daáo nieco lepszy wynik ni zastosowanie buforowanego wej cia i znacznie lepszy ni u ycie klasy RandomAccessFile. Oczywi cie dokáadne warto ci pomiarów b d si znacznie ró niü dla innych komputerów, ale áatwo domy liü si , e w przypadku swobodnego dost pu do pliku zastosowanie mapo- wania da zawsze popraw efektywno ci dziaáania programu. Natomiast w przypadku sekwen- cyjnego odczytu plików o umiarkowanej wielko ci zastosowanie mapowania nie ma sensu. Pakiet java.nio znakomicie upraszcza stosowanie mapowania plików. Poni ej podajemy prze- pis na jego zastosowanie. Najpierw musimy uzyskaü kanaá dost pu do pliku. Kanaá jest abstrakcj stworzon dla plików dyskowych, pozwalaj c na korzystanie z takich mo liwo ci systemów operacyjnych jak mapowanie plików w pami ci, blokowanie plików czy szybki transfer danych pomi dzy pli- kami. Kanaá uzyskujemy, wywoáuj c metod getChannel dodan do klas FileInputStream, FileOutputStream i RandomAccessFile. FileInputStream in = new FileInputStream(. . .); FileChannel channel = in.getChannel(); Nast pnie uzyskujemy z kanaáu obiekt klasy MappedByteBuffer, wywoáuj c metod map klasy FileChannel. Okre lamy przy tym interesuj cy nas obszar pliku oraz tryb mapowania. Dost pne s trzy tryby mapowania: Q FileChannel.MapMode.READ_ONLY: otrzymany bufor umo liwia wyá cznie odczyt danych. Jakakolwiek próba zapisu do bufora spowoduje wyrzucenie wyj tku ReadOnlyBufferException. Q FileChannel.MapMode.READ_WRITE: otrzymany bufor umo liwia zapis danych, które w pewnym momencie zostan równie zaktualizowane w pliku dyskowym. Nale y pami taü, e modyfikacje mog nie byü od razu widoczne dla innych programów, które mapuj ten sam plik. Dokáadny sposób dziaáania równolegáego mapowania tego samego pliku przez wiele programów zale y od systemu operacyjnego. Q FileChannel.MapMode.PRIVATE: otrzymany bufor umo liwia zapis danych, ale wprowadzone w ten sposób modyfikacje pozostaj lokalne i nie s propagowane do pliku dyskowego. Gdy mamy ju bufor, mo emy czytaü i zapisywaü dane, stosuj c w tym celu metody klasy ByteBuffer i jej klasy bazowej Buffer.
  • 76. 84 Java. Techniki zaawansowane Bufory obsáuguj zarówno dost p sekwencyjny, jak i swobodny. Pozycja w buforze zmienia si na skutek wykonywania operacji get i put. Wszystkie bajty bufora mo emy przejrzeü sekwencyjnie na przykáad w poni szy sposób: while (buffer.hasRemaining()) { byte b = buffer.get(); . . . } Alternatywnie mo emy równie wykorzystaü dost p swobodny: for (int i = 0; i < buffer.limit(); i++) { byte b = buffer.get(i); . . . } Mo emy tak e czytaü tablice bajtów, stosuj c metody: get(byte[] bytes) get(byte[], int offset, int length) Dost pne s równie poni sze metody: getInt getLong getShort getChar getFloat getDouble umo liwiaj ce odczyt warto ci typów podstawowych zapisanych w pliku w postaci binarnej. Jak ju wyja nili my wcze niej, Java zapisuje dane w postaci binarnej, pocz wszy od najbar- dziej znacz cego bajta. Je li musimy przetworzyü plik, który zawiera dane zapisane od naj- mniej znacz cego bajta, to wystarczy zastosowaü poni sze wywoáanie: buffer.order(ByteOrder.LITTLE_ENDIAN); Aby poznaü bie cy sposób uporz dkowania bajtów w buforze, wywoáujemy: ByteOrder b = buffer.order() Ta para metod nie stosuje konwencji nazw set/get. Aby zapisaü warto ci typów podstawowych w buforze, u ywamy poni szych metod: putInt putLong putShort putChar putFloat putDouble Program przedstawiony na listingu 1.7 oblicza sum kontroln CRC32 pliku. Suma taka jest cz sto u ywana do kontroli naruszenia zawarto ci pliku. Uszkodzenie zawarto ci pliku powo-
  • 77. RozdziaÄ 1. Q Strumienie i pliki 85 duje zwykle zmian warto ci jego sumy kontrolnej. Pakiet java.util.zip zawiera klas CRC32 pozwalaj c wyznaczyü sum kontroln sekwencji bajtów przy zastosowaniu nast puj cej p tli: CRC32 crc = new CRC32(); while (wiúcej bajtów) crc.update(nastúpny bajt) long checksum = crc.getValue(); Listing 1.7. NIOTest.java import java.io.*; import java.nio.*; import java.nio.channels.*; import java.util.zip.*; /** * Program obliczajæcy sumú kontrolnæ CRC pliku. * Uruchamianie: java NIOTest nazwapliku * @version 1.01 2004-05-11 * @author Cay Horstmann */ public class NIOTest { public static long checksumInputStream(String filename) throws IOException { InputStream in = new FileInputStream(filename); CRC32 crc = new CRC32(); int c; while ((c = in.read()) != -1) crc.update(c); return crc.getValue(); } public static long checksumBufferedInputStream(String filename) throws ´IOException { InputStream in = new BufferedInputStream(new FileInputStream(filename)); CRC32 crc = new CRC32(); int c; while ((c = in.read()) != -1) crc.update(c); return crc.getValue(); } public static long checksumRandomAccessFile(String filename) throws IOException { RandomAccessFile file = new RandomAccessFile(filename, "r"); long length = file.length(); CRC32 crc = new CRC32(); for (long p = 0; p < length; p++) { file.seek(p); int c = file.readByte();
  • 78. 86 Java. Techniki zaawansowane crc.update(c); } return crc.getValue(); } public static long checksumMappedFile(String filename) throws IOException { FileInputStream in = new FileInputStream(filename); FileChannel channel = in.getChannel(); CRC32 crc = new CRC32(); int length = (int) channel.size(); MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, ´length); for (int p = 0; p < length; p++) { int c = buffer.get(p); crc.update(c); } return crc.getValue(); } public static void main(String[] args) throws IOException { System.out.println("Input Stream:"); long start = System.currentTimeMillis(); long crcValue = checksumInputStream(args[0]); long end = System.currentTimeMillis(); System.out.println(Long.toHexString(crcValue)); System.out.println((end - start) + " milliseconds"); System.out.println("Buffered Input Stream:"); start = System.currentTimeMillis(); crcValue = checksumBufferedInputStream(args[0]); end = System.currentTimeMillis(); System.out.println(Long.toHexString(crcValue)); System.out.println((end - start) + " milliseconds"); System.out.println("Random Access File:"); start = System.currentTimeMillis(); crcValue = checksumRandomAccessFile(args[0]); end = System.currentTimeMillis(); System.out.println(Long.toHexString(crcValue)); System.out.println((end - start) + " milliseconds"); System.out.println("Mapped File:"); start = System.currentTimeMillis(); crcValue = checksumMappedFile(args[0]); end = System.currentTimeMillis(); System.out.println(Long.toHexString(crcValue)); System.out.println((end - start) + " milliseconds"); } }
  • 79. RozdziaÄ 1. Q Strumienie i pliki 87 Opis dziaäania algorytmu CRC znajdziesz na stronie http://www.relisoft.com/Science/ ´CrcMath.html. Szczegóáy oblicze sumy kontrolnej CRC nie s dla nas istotne. Stosujemy j jedynie jako przykáad pewnej praktycznej operacji na pliku. Program uruchamiamy w nast puj cy sposób: java NIOTest nazwapliku java.io.FileInputStream 1.0 Q FileChannel getChannel() 1.4 zwraca kanaá dost pu do strumienia. java.io.FileOutputStream 1.0 Q FileChannel getChannel() 1.4 zwraca kanaá dost pu do strumienia. java.io.RandomAccessFile 1.0 Q FileChannel getChannel() 1.4 zwraca kanaá dost pu do pliku. java.nio.channels.FileChannel 1.4 Q MappedByteBuffer map(FileChannel.MapMode mode, long position, long size) tworzy w pami ci map fragmentu pliku. Parametry: mode jedna ze staáych READ_ONLY, READ_WRITE lub PRIVATE zdefiniowanych w klasie FileChannel.MapMode position pocz tek mapowanego fragmentu size rozmiar mapowanego fragmentu java.nio.Buffer 1.4 Q boolean hasRemaining() zwraca warto ü true, je li bie ca pozycja bufora nie osi gn áa jeszcze jego ko ca. Q int limit() zwraca pozycj ko cow bufora; jest to pierwsza pozycja, na której nie s ju dost pne kolejne dane bufora.
  • 80. 88 Java. Techniki zaawansowane java.nio.ByteBuffer 1.4 Q byte get() pobiera bajt z bie cej pozycji bufora i przesuwa pozycj do kolejnego bajta. Q byte get(int index) pobiera bajt o podanym indeksie. Q ByteBuffer put(byte b) umieszcza bajt na bie cej pozycji bufora i przesuwa pozycj do kolejnego bajta. Q ByteBuffer put(int index, byte b) umieszcza bajt na podanej pozycji bufora. Zwraca referencj do bufora. Q ByteBuffer get(byte[] destination) Q ByteBuffer get(byte[] destination, int offset, int length) wypeánia tablic bajtów lub jej zakres bajtami z bufora i przesuwa pozycj bufora o liczb wczytanych bajtów. Je li bufor nie zawiera wystarczaj cej liczby bajtów, to nie s one w ogóle wczytywane i zostaje wyrzucony wyj tek BufferUnderflowException. Zwracaj referencj do bufora. Parametry: destination wypeániana tablica bajtów offset pocz tek wypeánianego zakresu length rozmiar wypeánianego zakresu Q ByteBuffer put(byte[] source) Q ByteBuffer put(byte[] source, int offset, int length) umieszcza w buforze wszystkie bajty z tablicy lub jej zakresu i przesuwa pozycj bufora o liczb umieszczonych bajtów. Je li w buforze nie ma wystarczaj cego miejsca, to nie s zapisywane adne bajty i zostaje wyrzucony wyj tek BufferOverflowException. Zwraca referencj do bufora. Parametry: source tablica stanowi ca ródáo bajtów zapisywanych w buforze offset pocz tek zakresu ródáa length rozmiar zakresu ródáa Q Xxx getXxx() Q Xxx getXxx(int index) Q ByteBuffer putXxx(xxx value) Q ByteBuffer putXxx(int index, xxx value) pobiera lub zapisuje warto ü typu podstawowego. Xxx mo e byü typu Int, Long, Short, Char, Float lub Double. Q ByteBuffer order(ByteOrder order) Q ByteOrder order() okre la lub pobiera uporz dkowanie bajtów w buforze. Warto ci parametru order jest staáa BIG_ENDIAN lub LITTLE_ENDIAN zdefiniowana w klasie ByteOrder.
  • 81. RozdziaÄ 1. Q Strumienie i pliki 89 Struktura bufora danych Gdy u ywamy mapowania plików w pami ci, tworzymy pojedynczy bufor zawieraj cy caáy plik lub interesuj cy nas fragment pliku. Buforów mo emy równie u ywaü podczas odczytu i zapisu mniejszych porcji danych. W tym podrozdziale omówimy krótko podstawowe operacje na obiektach typu Buffer. Bufor jest w rzeczywisto ci tablic warto ci tego samego typu. Abstrakcyjna klasa Buffer posiada klasy pochodne ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer i ShortBuffer. Klasa StringBuffer nie jest zwiñzana z omawianñ tutaj hierarchiñ klas. W praktyce najcz ciej u ywane s klasy ByteBuffer i CharBuffer. Na rysunku 1.11 poka- zali my, e bufor jest scharakteryzowany przez: Q pojemno ü, która nigdy nie ulega zmianie; Q pozycj wskazuj c nast pn warto ü do odczytu lub zapisu; Q granic , poza któr odczyt i zapis nie maj sensu; Q opcjonalny znacznik dla powtarzaj cych si operacji odczytu lub zapisu. Rysunek 1.11. Bufor Wymienione warto ci speániaj nast puj cy warunek: 0 d znacznik d pozycja d granica d pojemno ü Podstawowa zasada funkcjonowania bufora brzmi: „najpierw zapis, potem odczyt”. Na pocz tku pozycja bufora jest równa 0, a granic jest jego pojemno ü. Nast pnie bufor jest wypeániany danymi za pomoc metody put. Gdy dane sko cz si lub wypeániony zostanie caáy bufor, pora przej ü do operacji odczytu. Metoda flip przenosi granic bufora do bie cej pozycji, a nast pnie zeruje pozycj . Teraz mo emy wywoáywaü metod get, dopóki metoda remaining zwraca warto ü wi ksz od zera (metoda ta zwraca ró nic granica – pozycja). Po wczytaniu wszystkich warto ci z bufora wywoáujemy metod clear, aby przygotowaü bufor do nast pnego cyklu zapisu. Jak áatwo si domy liü, metoda ta przywraca pozycji warto ü 0, a granicy nadaje warto ü pojemno ci bufora. Je li chcemy ponownie odczytaü bufor, u ywamy metody rewind lub metod mark/reset. Wi cej szczegóáów na ten temat w opisie metod zamieszczonym poni ej.
  • 82. 90 Java. Techniki zaawansowane java.nio.Buffer 1.4 Q Buffer clear() przygotowuje bufor do zapisu, nadaj c pozycji warto ü 0, a granicy warto ü równ pojemno ci bufora; zwraca this. Q Buffer flip() przygotowuje bufor do zapisu, nadaj c granicy warto ü równ pozycji, a nast pnie zeruj c warto ü pozycji; zwraca this. Q Buffer rewind() przygotowuje bufor do ponownego odczytu tych samych warto ci, nadaj c pozycji warto ü 0 i pozostawiaj c warto ü granicy bez zmian; zwraca this. Q Buffer mark() nadaje znacznikowi warto ü pozycji; zwraca this. Q Buffer reset() nadaje pozycji bufora warto ü znacznika, umo liwiaj c w ten sposób ponowny odczyt lub zapis danych; zwraca this. Q int remaining() zwraca liczb warto ci pozostaj cych do odczytu lub zapisu; jest to ró nica pomi dzy warto ci granicy i pozycji. Q int position() zwraca pozycj bufora. Q int capacity() zwraca pojemno è bufora. java.nio.CharBuffer 1.4 Q char get() Q CharBuffer get(char[] destination) Q CharBuffer get(char[] destination, int offset, int length) zwraca jedn warto ü typu char lub zakres warto ci typu char, pocz wszy od bie cej pozycji bufora, która w efekcie zostaje przesuni ta za ostatni wczytan warto ü. Ostatnie dwie wersje zwracaj this. Q CharBuffer put(char c) Q CharBuffer put(char[] source) Q CharBuffer put(char[] source, int offset, int length) Q CharBuffer put(String source) Q CharBuffer put(CharBuffer source) zapisuje w buforze jedn warto ü typu char lub zakres warto ci typu char, pocz wszy od bie cej pozycji bufora, która w efekcie zostaje przesuni ta za ostatni zapisan warto ü. Wszystkie wersje zwracaj this.
  • 83. RozdziaÄ 1. Q Strumienie i pliki 91 Q CharBuffer read(CharBuffer destination) pobiera warto ci typu char z bufora i umieszcza je w buforze destination, do momentu a zostanie osi gni ta granica bufora destination. Zwraca this. Blokowanie plików Rozwa my sytuacj , w której wiele równocze nie wykonywanych programów musi zmo- dyfikowaü ten sam plik. Je li pomi dzy programami nie b dzie mieü miejsca pewien rodzaj komunikacji, to bardzo prawdopodobne jest, e plik zostanie uszkodzony. Blokady plików pozwalaj kontrolowaü dost p do plików lub pewnego zakresu bajtów w pliku. Jednak implementacja blokad plików ró ni si istotnie w poszczególnych systemach opera- cyjnych i dlatego wcze niejsze wersje JDK nie umo liwiaáy stosowania takich blokad. Blokowanie plików nie jest wcale tak powszechne w przypadku typowych aplikacji. Wiele z nich przechowuje swoje dane w bazach danych, które dysponuj mechanizmami pozwala- j cymi na równolegáy dost p. Je li nasz program przechowuje dane w plikach, a problem równolegáego dost pu zaczyna zaprz taü nasz uwag , to cz sto áatwiej b dzie wáa nie sko- rzystaü z bazy danych zamiast projektowaü skomplikowany system blokowania plików. Istniej jednak sytuacje, w których blokowanie plików jest niezb dne. Zaáó my na przykáad, e nasza aplikacja zapisuje preferencje u ytkownika w pliku konfiguracyjnym. Je li uruchomi on dwie instancje aplikacji, to mo e si zdarzyü, e obie b d chciaáy zapisaü dane w pliku konfiguracyjnym w tym samym czasie. W takiej sytuacji pierwsza instancja powinna zablo- kowaü dost p do pliku. Gdy druga instancja natrafi na blokad , mo e zaczekaü na odblo- kowanie pliku lub po prostu pomin ü zapis danych. Aby zablokowaü plik, wywoáujemy metod lock lub tryLock klasy FileChannel: FileLock lock = channel.lock(); lub FileLock lock = channel.tryLock(); Pierwsze wywoáanie blokuje wykonanie programu do momentu, gdy blokada pliku b dzie dost pna. Drugie wywoáanie nie powoduje blokowania, lecz natychmiast zwraca blokad lub warto ü null, je li blokada nie jest dost pna. Plik pozostaje zablokowany do momentu zam- kni cia kanaáu lub wywoáania metody release dla danej blokady. Mo na równie zablokowaü dost p do fragmentu pliku za pomoc wywoáania FileLock lock(long start, long size, boolean exclusive) lub FileLock tryLock(long start, long size, boolean exclusive) Parametrowi flag nadajemy warto ü true, aby zablokowaü dost p do pliku zarówno dla operacji odczytu, jak i zapisu. W przypadku blokady wspóádzielonej parametr flag otrzymuje warto ü false, co umo liwia wielu procesom odczyt pliku, zapobiegaj c jednak uzyskaniu przez którykolwiek z nich wyá cznej blokady pliku. Nie wszystkie systemy operacyjne obsáuguj
  • 84. 92 Java. Techniki zaawansowane jednak blokady wspóádzielone. W takim przypadku mo emy uzyskaü blokad wyá czn , nawet je li dali my jedynie blokady wspóádzielonej. Metoda isShared klasy FileLock pozwala nam dowiedzieü si , któr z blokad otrzymali my. Je li zablokujemy dostöp do koþcowego fragmentu pliku, a rozmiar pliku zwiökszy siö poza granicö zablokowanego fragmentu, to dostöp do dodatkowego obszaru nie bödzie zablokowany. Aby zablokowaè dostöp do wszystkich bajtów, nale y parametrowi size nadaè warto è Long.MAX_VALUE. Nale y pami taü, e mo liwo ci blokad zale w znacznej mierze od konkretnego systemu operacyjnego. Poni ej wymieniamy kilka aspektów tego zagadnienia, na które warto zwróciü szczególn uwag : Q W niektórych systemach blokady plików maj jedynie charakter pomocniczy. Nawet je li aplikacji nie uda si zdobyü blokady, to mo e zapisywaü dane w pliku „zablokowanym” wcze niej przez inn aplikacj . Q W niektórych systemach nie jest mo liwe zablokowanie dost pu do mapy pliku w pami ci. Q Blokady plików s przydzielane na poziomie maszyny wirtualnej Java. Je li zatem dwa programy dziaáaj na tej samej maszynie wirtualnej, to nie mog uzyskaü blokady tego samego pliku. Metody lock i tryLock wyrzuc wyj tek OverlappingFile ´LockException w sytuacji, gdy maszyna wirtualna jest ju w posiadaniu blokady danego pliku. Q W niektórych systemach zamkni cie kanaáu zwalnia wszystkie blokady pliku b d ce w posiadaniu maszyny wirtualnej Java. Dlatego te nale y unikaü wielu kanaáów dost pu do tego samego, zablokowanego pliku. Q Dziaáanie blokad plików w sieciowych systemach plików zale y od konkretnego systemu i dlatego nale y unikaü stosowania blokad w takich systemach. java.nio.channels.FileChannel 1.4 Q FileLock lock() uzyskuje wyá czn blokad pliku. Blokuje dziaáanie programu do momentu uzyskania blokady. Q FileLock tryLock() uzyskuje wyá czn blokad caáego pliku lub zwraca null, je li nie mo e uzyskaü blokady. Q FileLock lock(long position, long size, boolean shared) Q FileLock tryLock(long position, long size, boolean shared) uzyskuje blokad dost pu do fragmentu pliku. Pierwsza wersja blokuje dziaáanie programu do momentu uzyskania blokady, a druga zwraca natychmiast warto ü null, je li nie mo e uzyskaü od razu blokady. Parametry: position pocz tek blokowanego fragmentu size rozmiar blokowanego fragmentu
  • 85. RozdziaÄ 1. Q Strumienie i pliki 93 shared warto ü true dla blokady wspóádzielonej, false dla wyá cznej java.nio.channels.FileLock 1.4 Q void release() zwalnia blokad . WyraÑenia regularne Wyra enia regularne stosujemy do okre lenia wzorców wyst puj cych w áa cuchach znaków. U ywamy ich najcz ciej wtedy, gdy potrzebujemy odnale ü áa cuchy zgodne z pewnym wzorcem. Na przykáad jeden z naszych przykáadowych programów odnajdywaá w pliku HTML wszystkie hiperá cza, wyszukuj c áa cuchy zgodne ze wzorcem <a href= "... ">. Oczywi cie zapis ... nie jest wystarczaj co precyzyjny. Specyfikuj c wzorzec, musimy dokáadnie okre liü znaki, które s dopuszczalne. Dlatego te opis wzorca wymaga zastoso- wania odpowiedniej skáadni. Oto prosty przykáad. Z wyra eniem regularnym [Jj]ava.+ mo e zostaü uzgodniony dowolny áa cuch znaków nast puj cej postaci: Q Pierwsz jego liter jest J lub j. Q Nast pne trzy litery to ava. Q Pozostaáa cz ü áa cucha mo e zawieraü jeden lub wi cej dowolnych znaków. Na przykáad áa cuch "javanese" zostanie dopasowany do naszego wyra enia regularnego, "Core Java" ju nie. Aby posáugiwaü si wyra eniami regularnymi, musimy nieco bli ej poznaü ich skáadni . Na szcz cie na pocz tek wystarczy kilka do ü oczywistych konstrukcji. Q Przez klas znaków rozumiemy zbiór alternatywnych znaków uj ty w nawiasy kwadratowe, na przykáad [Jj], [0-9], [A-Za-z] czy [^0-9]. Znak - oznacza zakres (czyli wszystkie znaki, których kody Unicode le w podanych granicach), a znak ^ oznacza dopeánienie (wszystkie znaki oprócz podanych). Q Istnieje wiele wst pnie zdefiniowanych klas znaków, takich jak d (cyfry) czy p{Sc} (symbol waluty w Unicode). Patrz przykáady w tabelach 1.7 i 1.8. Q Wi kszo ü znaków oznacza sam siebie, tak jak znaki ava w poprzednim przykáadzie. Q Symbol . oznacza dowolny znak (z wyj tkiem, byü mo e, znaków ko ca wiersza, co zale y od stanu odpowiedniego znacznika).
  • 86. 94 Java. Techniki zaawansowane Tabela 1.7. Skäadnia wyra eþ regularnych SkÄadnia ObjaÊnienie Znaki c Znak c. unnnn, xnn, 0n, 0nn, 0nnn Znak o kodzie, którego warto ü zostaáa podana w notacji szesnastkowej lub ósemkowej. t, n, r, f, a, e Znaki steruj ce tabulatora, nowego wiersza, powrotu karetki, ko ca strony, alertu i sekwencji steruj cej. cc Znak steruj cy odpowiadaj cy znakowi c. Klasy znaków [C1C2. . .] Dowolny ze znaków reprezentowanych przez C1C2. . ., gdzie Ci jest znakiem, zakresem znaków (c1-c2) lub klas znaków. [^. . .] Dopeánienie klasy znaków. [. . .&&. . .] Cz ü wspólna (przeci cie) dwóch klas znaków. Wst pnie zdefiniowane klasy znaków . Dowolny znak oprócz ko cz cego wiersz (lub dowolny znak, je li znacznik DOTALL zostaá ustawiony). d Cyfra [0-9]. D Znak, który nie jest cyfr [^0-9]. s Znak odst pu [ tnrfx0B]. S Znak, który nie jest odst pem. w Znak sáowa [a-zA-Z0-9_]. W Znak inny ni znak sáowa. p{nazwa} Klasa znaków o podanej nazwie (patrz tabela 1.8). P{nazwa} Dopeánienie klasy znaków o podanej nazwie. Granice dopasowania ^$ Pocz tek, koniec wej cia (lub pocz tek, koniec wiersza w trybie wielowierszowym). b Granica sáowa. B Granica inna ni sáowa. A Pocz tek wej cia. z Koniec wej cia. Z Koniec wej cia oprócz ostatniego zako czenia wiersza. G Koniec poprzedniego dopasowania. Kwantyfikatory X? Opcjonalnie X.
  • 87. RozdziaÄ 1. Q Strumienie i pliki 95 Tabela 1.7. Skäadnia wyra eþ regularnych — ciñg dalszy SkÄadnia ObjaÊnienie X* X, 0 lub wi cej razy. X+ X, 1 lub wi cej razy. X{n} X{n,} X{n,m} X n razy, co najmniej n razy, pomi dzy n i m razy. Przyrostki kwantyfikatora ? Powoduje dopasowanie najmniejszej liczby wyst pie . + Powoduje dopasowanie najwi kszej liczby wyst pie , nawet kosztem ogólnego powodzenia dopasowania. Operacje na zbiorach XY Dowolny áa cuch z X, po którym nast puje dowolny áa cuch z Y. X|Y Dowolny áa cuch z X lub Y. Grupowanie (X) Grupa. n Dopasowanie n-tej grupy. Sekwencje steruj ce c Znak c (nie mo e byü znakiem alfabetu). Q...E Cytat… dosáownie. (?...) Specjalna konstrukcja — patrz opis klasy Pattern. Tabela 1.8. Wstöpnie zdefiniowane nazwy klas znaków Nazwa klasy znaków ObjaÊnienie Lower Maáe litery ASCII [a-z] Upper Du e litery ASCII [A-Z] Alpha Litery alfabetu ASCII [A-Za-z] Digit Cyfry ASCII [0-9] Alnum Litery alfabetu b d cyfry ASCII [A-Za-z0-9] XDigit Cyfry szesnastkowe [0-9A-Fa-f] Print lub Graph Znaki ASCII posiadaj ce reprezentacj graficzn (na wydruku) [x21-x7E] Punct Znaki, które nie nale do znaków alfanumerycznych, b d cyfry [p{Print}&&P{Alnum}] ASCII Wszystkie znaki ASCII [x00-x7F] Cntrl Znaki steruj ce ASCII [x00-x1F] Blank Spacja lub tabulacja [t] Space Odst p [ tnrf0x0B]
  • 88. 96 Java. Techniki zaawansowane Tabela 1.8. Wstöpnie zdefiniowane nazwy klas znaków — ciñg dalszy Nazwa klasy znaków ObjaÊnienie javaLowerCase Maáa litera, zgodnie z wynikiem metody Character.isLowerCase() javaUpperCase Du a litera, zgodnie z wynikiem metody Character.isUpperCase() javaWhitespace Odst p, zgodnie z wynikiem metody Character.isWhiteSpace() javaMirrored Ekwiwalent wyniku metody Character.isMirrored() InBlok Blok jest nazw bloku znaków Unicode z usuni tymi spacjami, na przykáad BasicLatin lub Mongolian. List nazw bloków znajdziesz na stronach witryny http://www.unicode.org Kategoria lub InKategoria Kategoria jest nazw kategorii znaków Unicode, na przykáad L (litera) czy Sc (symbol waluty). List nazw kategorii znajdziesz na stronach witryny http://www.unicode.org Q speánia rol znaku specjalnego, na przykáad . oznacza znak kropki, a znak lewego uko nika. Q ^ i $ oznaczaj odpowiednio pocz tek i koniec wiersza. Q Je li X i Y s wyra eniami regularnymi, to XY oznacza „dowolne dopasowanie do X, po którym nast puje dowolne dopasowanie do Y”, a X | Y „dowolne dopasowanie do X lub Y”. Q Do wyra enia regularnego X mo emy stosowaü kwantyfikatory X+ (raz lub wi cej), X* (0 lub wi cej) i X? (0 lub 1). Q Domy lnie kwantyfikator dopasowuje najwi ksz mo liw liczb wyst pie , która gwarantuje ogólne powodzenie dopasowania. Zachowanie to mo emy zmodyfikowaü za pomoc przyrostka ? (dopasowanie najmniejszej liczby wyst pie ) i przyrostka + (dopasowanie najwi kszej liczby wyst pie , nawet je li nie gwarantuje ono ogólnego powodzenia dopasowania). Na przykáad áa cuch cab mo e zostaü dopasowany do wyra enia [a-z]*ab, ale nie do [a-z]*+ab. W pierwszym przypadku wyra enie [a-z]* dopasuje jedynie znak c, wobec czego znaki ab zostan dopasowane do reszty wzorca. Jednak wyra enie [a-z]*+ dopasuje znaki cab, wobec czego reszta wzorca pozostanie bez dopasowania. Q Grupy pozwalaj definiowaü podwyra enia. Grupy ujmujemy w znaki nawiasów ( ); na przykáad ([+-]?)([0-9]+). Mo emy nast pnie za daü dopasowania do wszystkich grup lub do wybranej grupy, do której odwoáujemy si przez n, gdzie n jest numerem grupy (numeracja rozpoczyna si od 1). A oto przykáad nieco skomplikowanego, ale potencjalnie u ytecznego wyra enia regularnego, które opisuje liczby caákowite zapisane dziesi tnie lub szesnastkowo: [+-]?[0-9]+|0[Xx][0-9A-Fa-f]+ Niestety, skáadnia wyra e regularnych nie jest caákowicie ustandaryzowana. Istnieje zgodno ü w zakresie podstawowych konstrukcji, ale diabeá tkwi w szczegóáach. Klasy j zyka Java zwi zane z przetwarzaniem wyra e regularnych u ywaj skáadni podobnej do zastosowanej w j zyku Perl. Wszystkie konstrukcje tej skáadni zostaáy przedstawione w tabeli 1.7. Wi cej
  • 89. RozdziaÄ 1. Q Strumienie i pliki 97 informacji na temat skáadni wyra e regularnych znajdziesz w dokumentacji klasy Pattern lub ksi ce Wyra enia regularne autorstwa J.E.F. Friedla (Wydawnictwo Helion, 2001). Najprostsze zastosowanie wyra enia regularnego polega na sprawdzeniu, czy dany áa cuch znaków pasuje do tego wyra enia. Oto w jaki sposób zaprogramowaü taki test w j zyku Java. Najpierw musimy utworzyü obiekt klasy Pattern na podstawie áa cucha opisuj cego wyra- enie regularne. Nast pnie pobraü obiekt klasy Matcher i wywoáaü jego metod matches: Pattern pattern = Pattern.compile(patternString); Matcher matcher = pattern.matcher(input); if (matcher.matches()) . . . Wej cie obiektu Matcher stanowi obiekt dowolnej klasy implementuj cej interfejs Char ´Sequence, na przykáad String, StringBuilder czy CharBuffer. Kompiluj c wzorzec, mo emy skonfigurowaü jeden lub wi cej znaczników, na przykáad: Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE + Pattern.UNICODE_CASE); Obsáugiwanych jest sze ü nast puj cych znaczników: Q CASE_INSENSITIVE — dopasowanie niezale nie od wielko ci liter. Domy lnie dotyczy to tylko znaków US ASCII. Q UNICODE_CASE — zastosowany w poá czeniu z CASE_INSENSITIVE, dotyczy wszystkich znaków Unicode. Q MULTILINE — ^ i $ oznaczaj pocz tek i koniec wiersza, a nie caáego wej cia. Q UNIX_LINES — tylko 'n' jest rozpoznawany jako zako czenie wiersza podczas dopasowywania do ^ i $ w trybie wielowierszowym. Q DOTALL — symbol . oznacza wszystkie znaki, w tym ko ca wiersza. Q CANON_EQ — bierze pod uwag kanoniczny odpowiednik znaków Unicode. Na przykáad znak u, po którym nast puje znak ¨ (diareza), zostanie dopasowany do znaku ü. Je li wyra enie regularne zawiera grupy, obiekt Matcher pozwala ujawniü granice grup. Metody: int start(int groupIndex) int end(int groupIndex) zwracaj indeks pocz tkowy i ko cowy podanej grupy. Dopasowany áa cuch mo emy pobraü, wywoáuj c String group(int groupIndex) Grupa 0 oznacza caáe wej cie; indeks pierwszej grupy równy jest 1. Metoda groupCount zwraca caákowit liczb grup. Grupy zagnie d one s uporz dkowane wedáug nawiasów otwieraj cych. Na przykáad wzo- rzec opisany wyra eniem ((1?[0-9]):([0-5][0-9]))[ap]m
  • 90. 98 Java. Techniki zaawansowane dla danych 11:59am spowoduje, e obiekt klasy Matcher b dzie raportowaü grupy w poni szy sposób: Indeks grupy Pocz tek Koniec àa cuch 0 0 7 11:59am 1 0 5 11:59 2 0 2 11 3 3 5 59 Program przedstawiony na listingu 1.8 umo liwia wprowadzenie wzorca, a nast pnie áa cucha, którego dopasowanie zostanie sprawdzone. Je li áa cuch pasuje do wzorca zawieraj cego grupy, to program wy wietla granice grup w postaci nawiasów, na przykáad: ((11):(59))am Listing 1.8. RegExTest.java import java.util.*; import java.util.regex.*; /** Program testujæcy zgodno è z wyra eniem regularnym. Wprowad wzorzec i dopasowywany a cuch. Je li wzorzec zawiera grupy, program wy wietli ich granice po uzgodnieniu a cucha ze wzorcem. @version 1.01 2004-05-11 @author Cay Horstmann */ public class RegExTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("Enter pattern: "); String patternString = in.nextLine(); Pattern pattern = null; try { pattern = Pattern.compile(patternString); } catch (PatternSyntaxException e) { System.out.println("Pattern syntax error"); System.exit(1); } while (true) { System.out.println("Enter string to match: "); String input = in.nextLine(); if (input == null || input.equals("")) return; Matcher matcher = pattern.matcher(input); if (matcher.matches())
  • 91. RozdziaÄ 1. Q Strumienie i pliki 99 { System.out.println("Match"); int g = matcher.groupCount(); if (g > 0) { for (int i = 0; i < input.length(); i++) { for (int j = 1; j <= g; j++) if (i == matcher.start(j)) System.out.print('('); System.out.print(input.charAt(i)); for (int j = 1; j <= g; j++) if (i + 1 == matcher.end(j)) System.out.print(')'); } System.out.println(); } } else System.out.println("No match"); } } } Zwykle nie chcemy dopasowywaü do wzorca caáego áa cucha wej ciowego, lecz jedynie odna- le ü jeden lub wi cej podáa cuchów. Aby znale ü kolejne dopasowanie, u ywamy metody find klasy Matcher. Je li zwróci ona warto ü true, to stosujemy metody start i end w celu odnale- zienia dopasowanego podáa cucha. while (matcher.find()) { int start = matcher.start(); int end = matcher.end(); String match = input.substring(start, end); . . . } Program przedstawiony na listingu 1.9 wykorzystuje powy szy mechanizm. Odnajduje on wszystkie hiperá cza na stronie internetowej i wy wietla je. Uruchamiaj c program, podajemy adres URL jako parametr w wierszu polece , na przykáad: java HrefMatch http://www.horstmann.com Listing 1.9. HrefMatch.java import java.io.*; import java.net.*; import java.util.regex.*; /** * Program wy wietlajæcy wszystkie adresy URL na stronie WWW * poprzez dopasowanie wyra enia regularnego * opisujæcego znacznik <a href=...> júzyka HTML. * Uruchamianie: java HrefMatch adresURL * @version 1.01 2004-06-04 * @author Cay Horstmann */
  • 92. 100 Java. Techniki zaawansowane public class HrefMatch { public static void main(String[] args) { try { // pobiera URL z wiersza polece lub u ywa domy lnego String urlString; if (args.length > 0) urlString = args[0]; else urlString = "http://java.sun.com"; // otwiera InputStreamReader dla podanego URL InputStreamReader in = new InputStreamReader(new ´URL(urlString).openStream()); // wczytuje zawarto è do obiektu klasy StringBuilder StringBuilder input = new StringBuilder(); int ch; while ((ch = in.read()) != -1) input.append((char) ch); // poszukuje wszystkich wystæpie wzorca String patternString = "<as+hrefs*=s*("[^"]*"|[^s>])s*>"; Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(input); while (matcher.find()) { int start = matcher.start(); int end = matcher.end(); String match = input.substring(start, end); System.out.println(match); } } catch (IOException e) { e.printStackTrace(); } catch (PatternSyntaxException e) { e.printStackTrace(); } } } Metoda replaceAll klasy Matcher zast puje wszystkie wyst pienia wyra enia regularnego podanym áa cuchem. Na przykáad poni szy kod zast pi wszystkie sekwencje cyfr znakiem #: Pattern pattern = Pattern.compile("[0-9]+"); Matcher matcher = pattern.matcher(input); String output = matcher.replaceAll("#"); àa cuch zast puj cy mo e zawieraü referencje grup wzorca: $n zostaje zast pione przez n-t grup . Sekwencja $ pozwala umie ciü znak $ w zast puj cym tek cie. Metoda replaceFirst zast puje jedynie pierwsze wyst pienie wzorca.
  • 93. RozdziaÄ 1. Q Strumienie i pliki 101 Klasa Pattern dysponuje równie metod split, która dzieli áa cuch wej ciowy na tablic áa cuchów, u ywaj c dopasowa wyra enia regularnego jako granic podziaáu. Na przykáad poni szy kod podzieli áa cuch wej ciowy na tokeny na podstawie znaków interpunkcyjnych otoczonych opcjonalnym odst pem. Pattern pattern = Pattern.compile("s*p{Punct}s*"); String[] tokens = pattern.split(input); java.util.regex.Pattern 1.4 Q static Pattern compile(String expression) Q static Pattern compile(String expression, int flags) kompiluje áa cuch wyra enia regularnego, tworz c obiekt wzorca przyspieszaj cy przetwarzanie. Parametry: expression wyra enie regularne flags jeden lub wi cej znaczników CASE_INSENSITIVE, UNICODE_CASE, MULTILINE, UNIX_LINES, DOTALL i CANON_EQ. Q Matcher matcher(CharSequence input) tworzy obiekt pozwalaj cy odnajdywaü dopasowania do wzorca w áa cuchu wej ciowym. Q String[] split(CharSequence input) Q String[] split(CharSequence input, int limit) rozbija áa cuch wej ciowy na tokeny, stosuj c wzorzec do okre lenia granic podziaáu. Zwraca tablic tokenów, które nie zawieraj granic podziaáu. Parametry: input áa cuch rozbijany na tokeny limit maksymalna liczba utworzonych áa cuchów. Je li dopasowanych zostaáo limit - 1 granic podziaáu, to ostatni element zwracanej tablicy zawiera niepodzielon reszt áa cucha wej ciowego. Je li limit jest równy lub mniejszy od 0, to zostanie podzielony caáy áa cuch wej ciowy. Je li limit jest równy 0, to puste áa cuchy ko cz ce dane wej ciowe nie s umieszczane w tablicy java.util.regex.Matcher 1.4 Q boolean matches() zwraca true, je li áa cuch wej ciowy pasuje do wzorca. Q boolean lookingAt() zwraca true, je li pocz tek áa cucha wej ciowego pasuje do wzorca. Q boolean find()
  • 94. 102 Java. Techniki zaawansowane Q boolean find(int start) próbuje odnale ü nast pne dopasowanie i zwraca true, je li próba si powiedzie. Parametry: start indeks, od którego nale y rozpocz ü poszukiwanie Q int start() Q int end() zwraca pozycj pocz tkow dopasowania lub nast pn pozycj za dopasowaniem. Q String group() zwraca bie ce dopasowanie. Q int groupCount() zwraca liczb grup we wzorcu wej ciowym. Q int start(int groupIndex) Q int end(int groupIndex) zwraca pozycj pocz tkow grupy lub nast pn pozycj za grup dla danej grupy bie cego dopasowania. Parametry: groupIndex indeks grupy (warto ci indeksu rozpoczynaj si od 1) lub 0 dla oznaczenia caáego dopasowania Q String group(int groupIndex) zwraca áa cuch dopasowany do podanej grupy. Parametry: groupIndex indeks grupy (warto ci indeksu rozpoczynaj si od 1) lub 0 dla oznaczenia caáego dopasowania Q String replaceAll(String replacement) Q String replaceFirst(String replacement) zwracaj áa cuch powstaáy przez zast pienie podanym áa cuchem wszystkich dopasowa lub tylko pierwszego dopasowania. Parametry: replacement áa cuch zast puj cy mo e zawieraü referencje do grup wzorca postaci $n. Aby umie ciü w áancuchu symbol $, stosujemy sekwencj $. Q Matcher reset() Q Matcher reset(CharSequence input) resetuje stan obiektu Matcher. Druga wersja powoduje przej cie obiektu Matcher do pracy z innymi danymi wej ciowymi. Obie wersje zwracaj this. W tym rozdziale omówili my metody obsáugi plików i katalogów, a tak e metody zapisywania informacji do plików w formacie tekstowym i binarnym i wczytywania informacji z plików w formacie tekstowym i binarnym, jak równie szereg ulepsze , które do obsáugi wej cia i wyj cia wprowadziá pakiet java.nio. W nast pnym rozdziale omówimy mo liwo ci biblioteki j zyka Java zwi zane z przetwarzaniem j zyka XML.