Toky dat, soubory, serializace Streams
1
Obsah 1. 2. 3. 4. 5. 6.
Úvod Hierarchie dat Soubory a streamy (datové toky) Třída File Sekvenční přístup k textovým souborům Serializace
2
1. Úvod • Soubory – Dlouhodobé uložení velkého množství dat – Perzistentní data existující po ukončení aplikace – Soubory jsou uloženy na zařízeních druhotné paměti • Magnetické disky • Optické disky • Magnetické pásky
– Sekvenční a náhodný přístup k souborům
3
2. Hierarchie dat • Hierarchie dat – Datové položky v počítači vytváří hierarchii • bity, znaky, položky.
• Záznamy - Records – Jsou složeny z několika položek – Implementace jako třídy v Javě
• Soubor je tvořen skupinou záznamů stejné struktury
4
Hierarchie dat
Judy
Judy
01001010
1
Sally
Black
Tom
Blue
Judy
Green
Iris
Orange
Randy
Red
Green
File
Record
Field
Byte (ASCII character J)
Bit
5
3. Soubory a streamy (datové toky) • Java pohlíží na soubor jako na stream (datový tok) bytů – Soubor má na konci označení end-of-file marker nebo specifický počet bytů (byte number) – Soubor je jako stream bytů asociovaný s objektem • Java také asociuje streamy se zařízeními – System.in, System.out, and System.err
– Streamy mohou být přesměrovány
• Práce se třídou File je v balíčku (java.io) – FileInputStream pro
bytově orientovaný vstup ze souboru – FileOutputStream pro bytově orientovaný výstup do souboru – FileReader pro znakově orientovaný vstup ze souboru – FileWriter pro znakově orientovaný výstup do souboru 6
Soubory a streamy (datové toky) • FileReader – podtřída třídy InputStreamReader • FileWriter – podtřída třídy OutputStreamWriter • Využití pro práci se znaku ve znakové sadě Unicode (16 bitová) • FileInputStream je podtřídou InputStream • FileOutputStream je podtřídou OutputStream • bytově orientované streamy, čtení, zápis 8 bitových (byte) informací 7
Pohled Javy na soubor n bytů
0
1
2
3
4
5
6
7
8
9
...
...
n-1
end-of-file marker
8
Soubory a streamy • Bufrování – Zlepšuje provádění vstupů/ výstupů – Kopíruje každý výstup oblasti paměti nazvané called buffer – Celý buffer se uloží na disk najednou • Jeden delší přístup na disk zabere méně času než několik kratších přístupů
– BufferedOutputStream bufruje výstup souboru – BufferedInputStream bufruje vstup souboru
9
Třída File • Třída File – Poskytuje užitečné informace o souborech a adresářích – Neotvírá a nezpracovává soubory – užitečné metody v následující tabulce
10
Osnova import java. java.io.* io.*; .*; public class Copy{ public static void main(String args[]) args[]) throws IOException { File inputFile = new File(“ File(“nazevA.txt” nazevA.txt”); File outputFile = new File(“ File(“nazevB.txt” nazevB.txt”); FileReader in = new FileReader(inputFile); FileReader(inputFile); FileWriter out = new FileWriter(outputFile); FileWriter(outputFile); int c;
Použití File Streamů na kopírování souborů s celočíselnými informacemi
while ((c = in.read()) in.read()) != -1) out.write(c); out.write(c); in.close(); in.close(); out.close(); out.close(); } }
11
Metody třídy File Metoda
Popis
boolean canRead()
Vrací true, je-li soubor čitelný danou aplikací.
boolean canWrite()
Vrací true, pokud do souboru může zapisovat daná aplikace.
boolean exists()
Vrací true, je-li jméno, zadané jako argument je soubor, nebo adresář ve specifikované cestě.
boolean isFile()
Vrací true, je-li argument funkce soubor.
boolean isDirectory()
Vrací true, pokud je argument funkce adresář.
boolean isAbsolute()
Vrací true, pokud argument funkce je absolutní cesta k souboru/ adresáři.
String getAbsolutePath()
Vrací řetězec s absolutní cestou k adresáři nebo souboru.
12
Metody třídy File Metoda
Popis
String getName()
Vrací řetězec se jménem souboru nebo adresáře.
String getPath()
Vrací řetězec s cestou k souboru nebo adresáři.
String getParent()
Vrací řetězec s rodičovským adresářem souboru, nebo adresáře (adresář, ve kterém se soubor nebo adresář nacházejí).
long length()
Vrací velikost souboru v bytech – pro adresář je to 0.
long lastModified()
Vrací platformově závislý formát času, poslední aktualizace. Hodnotu možno porovnat s jinou hodnotou téže funkce.
String[] list()
Vrací pole řetězců reprezentující obsah adresáře.
13
// Priklad vyuziti tridy File import java.io.File; java.io.File;
Osnova
public class FileDemonstration { // zobrazi informace o souboru, který specifikuje uzivatel public void analyzePath( analyzePath( String path ) { // vytvori File object na zaklade uzivatelskeho vstupu File name = new File( path ); if ( name.exists() name.exists() ) // pokud existuje, zobrazi informace { // zobrazeni informaci file ( directory) System.out.printf( System.out.printf( "%s%s "%s%s\ s%s\n%s\ n%s\n%s\ n%s\n%s\ n%s\n%s%s\ n%s%s\n%s%s\ n%s%s\n%s%s\ n%s%s\n%s%s\ n%s%s\n%s%s", n%s%s", name.getName(), name.getName(), " existuje existuje", uje", ( name.isFile() name.isFile() ? "is soubor" soubor" : „není není soubor" soubor" ), ( name.isDirectory() name.isDirectory() ? „je adresar" adresar" : „není není adresar" adresar" ), ( name.isAbsolute() name.isAbsolute() ? „je absolutni absolutni cesta" cesta" : "is not absolute path" ), „Poslední Poslední modifikace: modifikace: ", name.lastModified(), name.lastModified(), „delka: delka: ", name.length(), name.length(), "Path: ", name.getPath(), name.getPath(), "Absolute path: ", name.getAbsolutePath(), name.getAbsolutePath(), "Parent: ", name.getParent() name.getParent() );
14
if ( name name. .isDirectory() isDirectory() ) // adresarove informace { String directory[] directory[] = name.list(); name.list(); System. System.out. out.println( println( "\ "\n\nDirectory contents: contents:\n" );
Osnova
for ( String directoryName : directory ) System. System.out. out.printf( printf( "%s\ "%s\n", directoryName ); } // end else } // end outer if else // not file or directory, directory, output error message { System. System.out. out.printf( printf( "%s %s", path, path, "does "does not exist." exist." ); } // end else } // end method analyzePath } // end class FileDemonstration
15
// Test tridy FileDemonstration import java. java.util.Scanner; util.Scanner;
Osnova
public class FileDemonstrationTest { public static void main( main( String args[] args[] ) { Scanner input = new Scanner( System.in System.in ); FileDemonstration application = new FileDemonstration(); FileDemonstration(); System. here: : " ); System.out. out.print( print( "Enter "Enter file or directory name here application. application.analyzePath( analyzePath( input. input.nextLine() nextLine() ); } // end main } // end class FileDemonstrationTest
16
Enter file or directory name here: stream_07 stream_07 exists is not a file is a directory is not absolute path Last modified: 1145030448000 Length: 0 Path: stream_07 Absolute path: D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Stream\ Stream\stream_07 Parent: null Directory contents:
Osnova
CreateTextFile.class CreateTextFile.java CreateTextFileTest.class CreateTextFileTest.java Enter file or directory name here: d:\ d:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Prvni\ Prvni\vyjimky\ vyjimky\Vyjimka1.java Vyjimka1.java exists is a file is not a directory is absolute path Last modified: 1143185258000 Length: 737 Path: d:\ d:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Prvni\ Prvni\vyjimky\ vyjimky\Vyjimka1.java Absolute path: d:\ d:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Prvni\ Prvni\vyjimky\ vyjimky\Vyjimka1.java Parent: d:\ d:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Prvni\ Prvni\vyjimky
17
Vytvoření souboru se sekvenčním přístupem • Soubory v Javě – Java nevytváří žádnou strukturu v souboru – Programátor vytváří strukturu souboru podle aplikace – Následující program používá jednoduchou strukturu záznamů (rekordů)
18
// Trida reprezentujici jeden zaznam informaci. informaci. package stream_gen; stream_gen;
Osnova
public class AccountRecord { private int account; private String firstName; firstName; private String lastName; lastName; private double balance; public AccountRecord() AccountRecord() { this( 0, "", "", 0.0 ); } // initializace initializace zaznamu public AccountRecord( AccountRecord( int acct, String first, String last, double bal ) { setAccount( setAccount( acct ); setFirstName( setFirstName( first ); setLastName( setLastName( last ); setBalance( setBalance( bal ); } // nastaveni cisla uctu public void setAccount( setAccount( int acct ) { account = acct; }
19
// ziskani cisla uctu public int getAccount() getAccount() { return account; account; }
Osnova
public void setFirstName( setFirstName( String first ) { firstName = first; first; } public String getFirstName() getFirstName() { return firstName; firstName; } public void setLastName( setLastName( String last ) { lastName = last; last; } public String getLastName() getLastName() { return lastName; lastName; }
20
Osnova public void setBalance( setBalance( double bal ) { balance = bal; }
public double getBalance() getBalance() { return balance; } }
21
// Zapis dat do textoveho souboru pomoci tridy Formatter. Formatter. import java. java.io. io.FileNotFoundException; FileNotFoundException; import java. java.lang. lang.SecurityException; SecurityException; import java. java.util. util.Formatter; Formatter; import java. java.util. util.FormatterClosedException; FormatterClosedException; import java. java.util. util.NoSuchElementException; NoSuchElementException; import java. java.util.Scanner; util.Scanner;
Osnova
import stream_gen. stream_gen.AccountRecord _gen.AccountRecord; AccountRecord; public class CreateTextFile { private Formatter output; output; // objekt pouzity pro vystup textu do souboru // otevirani souboru public void openFile() openFile() { try { output = new Formatter( Formatter( "clients "clients. clients.txt" txt" ); } catch ( SecurityException securityException ) { System. System.err. err.println( println( "You do not have write access to this file." file." ); System.exit( System.exit( 1 ); }
22
catch ( FileNotFoundException filesNotFoundException ) { System. System.err. err.println( println( "Error "Error creating file." file." ); System.exit( System.exit( 1 ); } // end catch } // end method openFile
Osnova
// pridavani zaznamu do souboru public void addRecords() addRecords() { // objekt pro zapis do souboru AccountRecord record = new AccountRecord(); AccountRecord(); Scanner input = new Scanner( System.in System.in ); System. System.out. out.printf( printf( "%s\ "%s\n%s\ n%s\n%s\ n%s\n%s\ n%s\n\n", "To terminate input, type the endend-ofof-file indicator ", "when you are prompted to enter input.", "On UNIX/Linux/Mac OS X type ctrl> d then press Enter", Enter", "On Windows type ctrl> z then press Enter" Enter" ); System. System.out. out.printf( printf( "%s\ "%s\n%s", "Enter account number (> 0), first name, name, last name and balance.", "? " );
23
while ( input. input.hasNext() hasNext() ) // smycka, smycka, dokud není není indikator konce { try { // priprava dat pro zapis record. record.setAccount( setAccount( input. input.nextInt() nextInt() ); // read account number record. record.setFirstName( setFirstName( input. input.next() next() ); // read first name record. record.setLastName( setLastName( input. input.next() next() ); // read last name record. record.setBalance( setBalance( input. input.nextDouble() nextDouble() ); // read balance if ( record. record.getAccount() getAccount() > 0 ) { // zapis noveho zaznamu output. output.format( format( "%d %s %s %.2f\ %.2f\n", record. record.getAccount(), getAccount(), record. record.getFirstName(), getFirstName(), record. record.getLastName(), getLastName(), record. record.getBalance() getBalance() ); } // end if else { System. System.out. out.println( println( "Account number must be greater than 0." ); } // end else } // end try catch ( FormatterClosedException formatterClosedException ) { System. System.err. err.println( println( "Error "Error writing to file." file." ); return; return; } // end catch
Osnova
24
catch ( NoSuchElementException elementException ) { System. System.err. err.println( println( "Invalid input. Please try again." again." ); input.nextLine input.nextLine(); nextLine(); // vstup n neplatny, neplatny, uzivatel zkousi znova } // end catch
Osnova
System. System.out. out.printf( printf( "%s %s\ %s\n%s", "Enter "Enter account number (>0),", "first name, name, last name and balance.", "? " ); } // end while } // end method addRecords // close file public void closeFile() closeFile() { if ( output != null ) output. output.close(); close(); } // end method closeFile } // end class CreateTextFile
25
// Test tridy CreateTextFile
Osnova
public class CreateTextFileTest { public static void main( main( String args[] args[] ) { CreateTextFile application = new CreateTextFile(); CreateTextFile(); application. application.openFile(); openFile(); application. application.addRecords(); addRecords(); application. application.closeFile(); closeFile(); } // end main } // end class CreateTextFileTest
26
D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Stream>java stream_07.CreateTextFileTest To terminate input, type the endend-ofof-file indicator when you are prompted to enter input. On UNIX/Linux/Mac OS X type d then press Enter On Windows type z then press Enter
Osnova
Enter account number (> 0), first name, last name and balance. ? 22, Josef, Kadlec, Kadlec, 250 Invalid input. Please try again. Enter account number (>0), first name, last name and balance. ? 22 Josef Kadlec 250 Enter account number (>0), first name, last name and balance. ? 30 Jindrich Brychta 500 Enter account number (>0), first name, last name and balance. ? 44 Alena Konickova 4040 Enter account number (>0), first name, last name and balance. ? 55 Iveta Blazkova 5600 Enter account number (>0), first name, last name and balance. ? 66 Alois Junek 2000 Enter account number (>0), first name, last name and balance. ? 101 Boleslav Zahradka 8400 Enter account number (>0), first name, last name and balance. ? 77 Renata Modra 4100 Enter account number (>0), first name, last name and balance. ? ^Z
27
Čtení dat ze souboru se sekvenčním přístupem • Data jsou uložena v souborech – Dle potřeby jsou data načtena ze souborů – Přístup k sekvenčnímu souboru • Data musí být čtena ve stejném formátu jako byla zapisována
28
// Program cte textovy soubor a zobrazi kazdy zaznam. zaznam. import java. java.io. io.File; File; import java. java.io. io.FileNotFoundException; FileNotFoundException; import java. java.lang. lang.IllegalStateException; IllegalStateException; import java. java.util. util.NoSuchElementException; NoSuchElementException; import java. java.util.Scanner; util.Scanner;
Osnova
import stream_gen. stream_gen.AccountRecord _gen.AccountRecord; AccountRecord; public class ReadTextFile { private Scanner input; input; // enable user to open file public void openFile() openFile() { try { input = new Scanner( new File( File( "clients "clients. clients.txt" txt" ) ); } // end try catch ( FileNotFoundException fileNotFoundException ) { System. System.err. err.println( println( "Error "Error opening file." file." ); System.exit( System.exit( 1 ); } // end catch } // end method openFile
29
// cteni zaznamu ze souboru public void readRecords() readRecords() { // objekt pro zobrazeni na obrazovku AccountRecord record = new AccountRecord(); AccountRecord();
Osnova
System. System.out. out.printf( printf( "%"%-10s%10s%-12s%12s%-12s%10s\ 12s%10s\n", "Account "Account", Account", "First Name", Name", "Last "Last Name", Name", "Balance" ); try // cte zaznamy ze souboru za pouziti objektu tridy Scanner { while ( input. input.hasNext() hasNext() ) { record. record.setAccount( setAccount( input. input.nextInt() nextInt() ); // read account number record. record.setFirstName( setFirstName( input. input.next() next() ); // read first name record. record.setLastName( setLastName( input. input.next() next() ); // read last name record. record.setBalance( setBalance( input. input.nextDouble() nextDouble() ); // read balance // zobrazi obsah zaznamu System. System.out. out.printf( printf( "%"%-10d%10d%-12s%12s%-12s%10.2f\ 12s%10.2f\n", record. record.getAccount(), getAccount(), record. record.getFirstName(), getFirstName(), record. record.getLastName(), getLastName(), record. record.getBalance() getBalance() ); } // end while } // end try catch ( NoSuchElementException elementException ) { System. System.err. err.println( println( "File "File improperly formed." formed." ); input. input.close(); close(); System.exit( System.exit( 1 ); } // end catch
30
catch ( IllegalStateException stateException ) { System. System.err. err.println( println( "Error "Error reading from file." file." ); System.exit( System.exit( 1 ); } // end catch } // end method readRecords
Osnova
// close file and terminate application public void closeFile() closeFile() { if ( input != null ) input. input.close(); close(); // close file } // end method closeFile } // end class ReadTextFile
31
// Program testujici tridu ReadTextFile. ReadTextFile. public class ReadTextFileTest { public static void main( main( String args[] args[] ) { ReadTextFile application = new ReadTextFile(); ReadTextFile();
Osnova
application. application.openFile(); openFile(); application. application.readRecords(); readRecords(); application. application.closeFile(); closeFile(); } // end main } // end class ReadTextFileTest
32
Osnova D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Stream>java Account First Name Last Name 22 Josef Kadlec 30 Jindrich Brychta 44 Alena Konickova 55 Iveta Blazkova 66 Alois Junek 101 Boleslav Zahradka 77 Renata Modra
stream_11.ReadTextFileTest Balance 250,00 500,00 4040,00 5600,00 2000,00 8400,00 4100,00
33
Využití sekvenčních souborů • Zjišťování nulového, záporného a kladného stavu na účtech uložených v sekvenčním souboru
34
// Definuje vyctovy typ pro ziskavani informaci o uctech. uctech. package stream_13; stream_13; public enum MenuOption { // deklarace obsahu vyctoveho typu ZERO_BALANCE( 1 ), CREDIT_BALANCE( 2 ), DEBIT_BALANCE( 3 ), END( 4 );
Osnova Výčtový typ deklarace
private final int value; value; // aktualni volba z nabidky MenuOption( MenuOption( int valueOption ) { value = valueOption; valueOption; } public int getValue() getValue() { return value; value; } // end method getValue } // end enum MenuOption
35
// Program cte soubor sekvencne a zobrazuje obsah podle // typu uzivatelova dotazu na ucty // (credit (credit balance, debit balance or zero balance). import import import import import
Osnova
java. java.io. io.File; File; java. java.io. io.FileNotFoundException; FileNotFoundException; java. java.lang. lang.IllegalStateException; IllegalStateException; java. java.util. util.NoSuchElementException; NoSuchElementException; java. java.util.Scanner; util.Scanner;
import stream_gen. stream_gen.AccountRecord _gen.AccountRecord; AccountRecord; public class CreditInquiry { private MenuOption accountType; accountType; private Scanner input; private MenuOption choices[] choices[] = { MenuOption.ZERO_BALANCE, MenuOption.ZERO_BALANCE, MenuOption.CREDIT_BALANCE, MenuOption.CREDIT_BALANCE, MenuOption.DEBIT_BALANCE, MenuOption.DEBIT_BALANCE, MenuOption.END MenuOption.END }; // cte zaznamy ze souboru a zobrazi pouze zaznamy pozadovaneho typu private void readRecords() readRecords() { AccountRecord record = new AccountRecord(); AccountRecord();
36
try // cte zaznamy { // otevre soubor a cte od zacatku input = new Scanner( new File( File( "clients "clients. clients.txt" txt" ) );
Osnova
while ( input.hasNext input.hasNext() hasNext() ) { record. record.setAccount( setAccount( input. input.nextInt() nextInt() ); // read account number record. record.setFirstName( setFirstName( input. input.next() next() ); // read first name record. record.setLastName( setLastName( input. input.next() next() ); // read last name record. record.setBalance( setBalance( input. input.nextDouble() nextDouble() ); // read balance // if proper acount type, display record shouldDisplay( ( record. if ( shouldDisplay record.getBalance() getBalance() ) ) System. System.out. out.printf( printf( "%"%-10d%10d%-12s%12s%-12s%10.2f\ 12s%10.2f\n", record. record.getAccount(), getAccount(), record. record.getFirstName(), getFirstName(), record. record.getLastName(), getLastName(), record. record.getBalance() getBalance() ); } // end while } // end try catch ( NoSuchElementException elementException ) { System. System.err. err.println( println( "File "File improperly formed." formed." ); input. input.close(); close(); System.exit( System.exit( 1 ); } // end catch catch ( IllegalStateException stateException ) { System. System.err. err.println( println( "Error "Error reading from file." file." ); System.exit( System.exit( 1 ); } // end catch
37
catch ( FileNotFoundException fileNotFoundException ) { System.err.println( System.err.println( "File cannot be found." ); System.exit( System.exit( 1 ); } // end catch finally { if ( input != null ) input.close(); input.close(); // close the Scanner and the file } // end finally } // end method readRecords
Osnova
// use record type to determine if record should be displayed private boolean shouldDisplay( shouldDisplay( double balance ) { if ( ( accountType == MenuOption.CREDIT_BALANCE ) && ( balance < 0 ) ) return true; else if ( ( accountType == MenuOption.DEBIT_BALANCE ) && ( balance > 0 ) ) return true; else if ( ( accountType == MenuOption.ZERO_BALANCE ) && ( balance == 0 ) ) return true; return false; } // end method shouldDisplay
38
// ziskani pozadavku od uzivatele private MenuOption getRequest() getRequest() { Scanner textIn = new Scanner( System.in System.in ); int request = 1;
Osnova
// zobrazi moznosti vyberu System. System.out. out.printf( printf( "\ "\n%s\ n%s\n%s\ n%s\n%s\ n%s\n%s\ n%s\n%s\ n%s\n", "Enter request", request", " 1 - List accounts with zero balances", balances", " 2 - List accounts with credit balances", balances", " 3 - List accounts with debit balances", balances", " 4 - End of run" ); try { do // input user request { System. System.out. out.print( print( "\ "\n? " ); request = textIn. textIn.nextInt(); nextInt(); } while ( ( request < 1 ) || ( request > 4 ) ); } // end try catch ( NoSuchElementException elementException ) { System. System.err. err.println( println( "Invalid input." ); System.exit( System.exit( 1 ); } // end catch return choices[ choices[ request - 1 ]; // return enum value for option } // end method getRequest
39
public void processRequests() processRequests() { // ziska pozadavek uzivatele (e.g., zero, zero, credit or debit balance) accountType = getRequest(); getRequest();
Osnova
while ( accountType != MenuOption.END MenuOption.END ) { switch ( accountType ) { case ZERO_BALANCE: System. System.out. out.println( println( "\ "\nAccounts with zero balances: balances:\n" ); break; break; case CREDIT_BALANCE: System. System.out. out.println( println( "\ "\nAccounts with credit balances: balances:\n" ); break; break; case DEBIT_BALANCE: System. balances: :\n" ); System.out. out.println( println( "\ "\nAccounts with debit balances break; break; } // end switch readRecords(); readRecords(); accountType = getRequest(); getRequest(); } // end while } // end method processRequests } // end class CreditInquiry
40
// Program testujici tridu CreditInquiry. CreditInquiry. package stream_13; stream_13; public class CreditInquiryTest { public static void main( main( String args[] args[] ) { CreditInquiry application = new CreditInquiry(); CreditInquiry(); application. application.processRequests(); processRequests(); } // end main } // end class CreditInquiryTest
Osnova
41
Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run
Osnova
? 3 Accounts with debit balances: 22 30 44 55 66 101 77
Josef Jindrich Alena Iveta Alois Boleslav Renata
Kadlec Brychta Konickova Blazkova Junek Zahradka Modra
250,00 500,00 4040,00 5600,00 2000,00 8400,00 4100,00
Enter request
42
Serializace • Serializace je proces transformující objekt v operační paměti na stream bytů. • Deserializace je opačný proces – rekonstruující objekt do operační paměti ze streamu bytů do stejného stavu, ve kterém byl objekt serializován. • Použití serializace: – má-li serializovaný stream bytů jako cíl uložení pevný disk, potom serializace podporuje perzistenci objektů – serializace je užitečná pro síťové aplikace (aplikace v jednom uzlu může objekt serializovat a aplikace v jiném uzlu ho deserializuje) 43
Serializace
• syntaxe a chování při serializaci zůstávají stejné bez ohledu na použitý typ serializace • tranzientní & perzistentní objekt - význam
44
Základy serializace • Serializovat se mohou pouze objekty (instance tříd) – primitivní typy int, double … nemohou být serializovány a deserializovány – pouze Integer, Double … • při serializaci třída, nebo její předchůdci musí implementovat rozhraní java.io.Serializable • Objekt je serializovaný zapsáním do objektu ObjectOutputStream • Objekt je deserializovaný přečtením z objektu ObjectInputStream 45
Segmant kódu – ilustrace serializace Date dnes = new Date(); FileOutputStream fos = new FileOutputStream(“save.ser”); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.write(today);
46
Segment kódu – ilustrace deserializace FileInputStream fis = new FileInputStream(“save.ser”); ObjectInputStream ois = new ObjectInputStream(fis); Date today = (Date) ois.readObject();
47
Příklady použití class Alpha implements Serializable { .. } class MyTreeModel extends DefaultTreeModel implements Serializable { //neni nutne }
• Když má serializovatelný objekt odkaz na neserializovatelný objekt, celý graf obejktů je serializovatelný: class Emp implements Serializable { private List clients; }
48
Neserializovaná nadtřída se serializovatelnou podtřídou • Podtřída neserializovatelné nadtřídy je také serializovatelná pokud: – podtřída implementuje rozhraní Serializable – nadtřída nemá žádný konstruktor s argumenty, který je přístupný pro deserializaci - viz třída Object
49
Vstupní a výstupní toky (streamy) třídy ObjectOutputStream, ObjectInputStream
• Dvě nejdůležitější metody serializace jsou: – writeObject( Object obj ) ze třídy ObjectOutputStream – ObjectOutputStream může být konstruovaný z FileOutputStream pro serializaci na souborech pevného disku – readObject() ze třídy ObjectInputStream – ObjectInputStream může být konstruovaný ze třídy FileInputStream pro deserializaci ze souborů na pevném disku 50
import java. java.util.*; util.*; import java. java.io.*; io.*; class SerialTest { public static void main( main( String[ String[ ] args ) throws IOException, IOException, ClassNotFoundException { //** naplneni objektu Vector celymi cilsy Vector Integer>orig = new Vector< Vector(); Integer>(); // serializovatelny for ( int i = 0; i < 5; i++ ) orig. orig.addElement( addElement( new Integer( Integer( i + 1 ) ); //** serialize the Vector out String fileName = "save "save. save.ser"; ser"; ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream( FileOutputStream( fileName ) ); oos. oos.writeObject( writeObject( orig ); oos.close oos.close(); close(); //** serialize the Vector in ObjectInputStream ois = new ObjectInputStream( ObjectInputStream( new FileInputStream( FileInputStream( fileName ) ); Vector restore = (Vector (Vector) Vector) ois. ois.readObject(); readObject(); ois. ois.close(); close(); //** tisk prvku pro potvrzeni deserializace Enumeration e = restore. restore.elements(); elements(); while ( e.hasMoreElements e.hasMoreElements() hasMoreElements() ) System. System.out. out.println( println( e.nextElement e.nextElement() nextElement() ); } }
Osnova Jednoduchý příklad
51
Serializace polí • array libovolných typů je serializovatelné pomocí writeObject a deserializovatelné pomocí readObject String file = “array.ser”; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); int[] array = new int[10]; for (int I = 0; i< 10; i++) array[i] = I; oos.writeObject(array)
52
Deserializace polí String file = “array.ser”; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); int[ ] a = (int[ ]) ois.readObject();
53
Serializace objektů • Objekt Alpha obsahuje tři datové atributy typu int – atribut x inicializovaný na 9, je nestatický, perzistentní; x zůstane po deserializaci 9 – atribut y je nastavený na -555 a je tranzientní, po deserializaci bude mít hodnotu 0 – atribut z je inicializovaný na 1 a je statický, proto z nebude nijak ovlivněno serializací a deserializací
54
import java. java.io.*; io.*; class Alpha implements Serializable { int x = 9; transient int y = -555; static int z = 1; } class SerialTestForTransientAndStatic { public static void main( main( String[ String[ ] args ) throws IOException, IOException, ClassNotFoundException { String fileName = "save "save. save.ser"; ser"; ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream( FileOutputStream( fileName ) ); //*** serializace an Alpha objektu: x == 9, y == -555, z == 1 oos.writeObject oos.writeObject( writeObject( new Alpha() Alpha() ); oos.close oos.close(); close(); //*** zvyseni statickeho z na 2 Alpha.z++; Alpha.z++; ObjectInputStream ois = new ObjectInputStream( ObjectInputStream( new FileInputStream( FileInputStream( fileName ) ); //*** deserializace objektu Alpha Alpha a2 = (Alpha (Alpha) Alpha) ois. ois.readObject(); readObject(); ois. ois.close(); close(); System. System.out. out.println( println( //*** 9 (x je nonstatic, " a2.x == " + a2.x + nonstatic, nontransient) nontransient) " a2.y == " + a2.y + //*** 0 (y je nonstatic, nonstatic, transient) transient) " Alpha.z Alpha.z == " + Alpha.z Alpha.z ); //*** 2 (z je static) } }
Osnova
55
Kastomizace serializace • Serializace může být kastomizovaná, aby vyhověla konkrétním požadavkům tříd • Taková třída implementuje rozhraní Serializable a pak ještě dvě další metody: private void writeObject(ObjectOutputStream os) throws IOException; private void readObject(ObjectInputStream os) throws IOException, ClassNotFoundException;
• obě metody jsou private a tzv. callback metody
56
Kastomizace serializace • Kastomizovaná serializace typicky rozšíří standardní (defaultní) serializaci private void writeObject(ObjectOutputStream os) throws IOException { os.defaultWriteObject(); // default serializace // … kastomizovana serializace } private void readObject(ObjectInputStream os) throws IOException, ClassNotFoundException { os.defaultReadObject(); // default deserialization // … kastomizovana deserializace }
57
Příklad kastomizace • Třída Emp má následující atributy: – String id – tranzientní, reprezentuje zaměstnancův id kód, kód je generovaný z výstupu metody toString() a náhodně generovaného hexadecimálního celého čísla. – Vyvolání defaultní metody toString() je založeno na hash kódu objektu (využívá se metoda hashCode třídy Object) – metoda toString() třídy Object: getClass().getName()+@+ Integer.toHexString(hashCode()) hashCode() – vrací hashCode daného objektu jako celé číslo 58
import java. java.io.*; io.*; import java. java.util.*; util.*; class Emp implements Serializable { private transient String id; private int ran; private String name; name; private static Random r = new Random(); Random(); public Emp( Emp( String NAME ) { makeId( ran = r.nextInt r.nextInt() nextInt() ); setName( setName( NAME ); } public String getId() getId() { return id; } public String getName() getName() { return name; name; } public void setName( setName( String NAME ) { name = NAME; } private void makeId( int n ) { id = toString() toString() + ""-" + Integer. Integer.toHexString( toHexString( n ); } private void writeObject( writeObject( ObjectOutputStream os ) throws IOException { os.defaultWriteObject os.defaultWriteObject(); defaultWriteObject(); // default serialization } private void readObject( readObject( ObjectInputStream is ) throws IOException, IOException, ClassNotFoundException { is. is.defaultReadObject(); defaultReadObject(); // default deserialization makeId( ran ); //*** reconstruct the deserialized object's object's id } }
Osnova Zaměstnanec serializace
59
class EmpSerial { public static void main( main( String[ String[ ] args ) throws IOException, IOException, ClassNotFoundException { String file = "save "save. save.ser"; ser"; Vector v = new Vector(); Vector(); ObjectOutputStream os = new ObjectOutputStream( new FileOutputStream( FileOutputStream( file ) ); System. System.out. out.println( println( "Preserialized "Preserialized Emps:" Emps:" ); for ( int i = 0; i < 4; i++ ) { Emp t = null; null; v.addElement v.addElement( addElement( t = new Emp( Emp( "fred "fred" fred" + i ) ); System. System.out. out.println( println( t.getId t.getId() getId() ); } os.writeObject os.writeObject( writeObject( v ); os.close os.close(); close(); ObjectInputStream is = new ObjectInputStream( ObjectInputStream( new FileInputStream( FileInputStream( file ) ); Vector x = (Vector (Vector) Vector) is. is.readObject(); readObject(); is. is.close(); close(); System. System.out. out.println( println( "\ "\nDeserialized Emps:" Emps:" ); Enumeration e = x.elements x.elements(); elements(); while ( e.hasMoreElements e.hasMoreElements() hasMoreElements() ) System. System.out. out.println( println( ((Emp ((Emp) e.nextElement()). Emp) e.nextElement nextElement()).getId ()).getId() getId() ); } }
Osnova
60
Možné problémy při serializaci • situace, kdy je jeden objekt serializovaný vícekrát a deserializovaný vícekrát
61
import java. java.io.*; io.*; class C implements Serializable { public C( int n ) { x = n; } public void set( int n ) { x = n; } public int get() get() { return x; }; int x; } class SerialCaution { public static void main( main( String[ String[ ] args ) throws Exception { C c1 = new C( 1 ); //*** serializace stejneho objektu C dvakrat ObjectOutputStream os = new ObjectOutputStream( new FileOutputStream( FileOutputStream( "c.ser "c.ser" ser" ) ); os.writeObject //*** zapis objektu jednou os.writeObject( writeObject( c1 ); os.writeObject //*** opetny zapis stejneho objektu os.writeObject( writeObject( c1 ); os.close os.close(); close(); //*** deserializace stejneho objektu C dvakrat ObjectInputStream is = new ObjectInputStream( ObjectInputStream( new FileInputStream( FileInputStream( "c.ser "c.ser" ser" ) ); C t1 = (C) is. is.readObject(); readObject(); //*** prirazeni k referenci t1 C t2 = (C) is. is.readObject(); readObject(); //*** prirazeni k referenci t2 is. is.close(); close(); if ( t1 == t2 ) //*** Vse v poradku: poradku: true System. System.out. out.println( println( "t1 and t2 refer to the same object." object." ); } }
Osnova jedná se o stejný objekt na který ukazují dvě reference
62
Seriálové číslo verze • Další problém může vzniknout změnou třídy mezi serializací a deserializací objektu • Když je objekt serializovaný, je s ním zapsáno i 64 bitové (long integer) číslo známé jako jednotný identifikátor sériové verze. Toto číslo identifikuje třídu objektu • Čísla musí být stejná
63
Osnova FileOutputStream out = new FileOutputStream(” FileOutputStream(”theTime. theTime.ser” ser”); ObjectOutputStream s = new ObjectOutputStream(out); ObjectOutputStream(out); s.writeObject(“ s.writeObject(“Today” Today”); s.writeObject(new Date()); s.flush(); s.flush();
ObjectOutputStream – musí musí být konstruovaný nad jiným streamem V př příkladě kladě se ObjectOutputStream konstruuje nad FileOutputStream a tí tím inicializuje objekt na soubor s ná názvem theTime. theTime.ser. ser.
Příklad získání času v milisekundách vytvořením objektu Date a později jeho serializací
Řetě etězec Today a objekt Date jsou zapsá zapsáni do objektu ObjectOutputStream pomocí pomocí metody writeObject() writeObject() ObjectOutputStream implementuje rozhraní rozhraní DataOutput, DataOutput, které které definuje metody: writeInt, writeInt, writeFloat ... metoda writeObject vyhodí vyhodí výjimku: NotSerializableException
64
Osnova FileInputStream in = new FileInputStream(“ FileInputStream(“theTime. theTime.ser” ser”); ObjectInputStream s = new ObjectInputStream(in); ObjectInputStream(in); String today = (String) s.readObject(); s.readObject(); Date date = (Date) s.readObject(); s.readObject();
Serializace objektů čtení z ObjectInputStream
metoda readObject deserializuje další další objekt ve streamu a prochá prochází jeho referencemi rekurzivně rekurzivně na další další objekt, až až deserializuje všechny dostupné dostupné objekty ObjectInputStream implementuje rozhraní rozhraní DataInput, DataInput, které které definuje další další metody pro nač načtení tení primitivní primitivních typů typů: readInt, readInt, readFloat
65
Vytvoření souboru se sekvenčním přístupem s použitím serializace objektů • Definování třídy AccountRecordSerializable která umožní serializaci a deserializaci pomocí tříd ObjectOutputStream a ObjectInputStream • Nejdůležitější činností je zápis objektu reprezentujícího svůj stav do serializované formy vhodné pro jeho rekonstrukci při čtení • Čtení a zápis objektu se nazývá objektovou serializací
66
// A class that represents one record of information. information. package stream_gen; stream_gen; // packaged for reuse
Osnova
import java. java.io. io.Serializable; Serializable; public class AccountRecordSerializable implements Serializable { private int account; account; private String firstName; firstName; private String lastName; lastName; private double balance; // nono-argument constructor calls other constructor with default values public AccountRecordSerializable() AccountRecordSerializable() { this( this( 0, "", "", 0.0 ); } // end nono-argument AccountRecordSerializable constructor // fourfour-argument constructor initializes a record public AccountRecordSerializable( AccountRecordSerializable( int acct, acct, String first, first, String last, last, double bal ) { setAccount( setAccount( acct ); setFirstName( setFirstName( first ); setLastName( setLastName( last ); setBalance( setBalance( bal ); } // end fourfour-argument AccountRecordSerializable constructor
67
// set account number public void setAccount( setAccount( int acct ) { account = acct; acct; } // end method setAccount
Osnova
// get account number public int getAccount() getAccount() { return account; account; } // end method getAccount // set first name public void setFirstName( setFirstName( String first ) { firstName = first; first; } // end method setFirstName // get first name public String getFirstName() getFirstName() { return firstName; firstName; } // end method getFirstName // set last name public void setLastName( setLastName( String last ) { lastName = last; last; } // end method setLastName
68
// get last name public String getLastName() getLastName() { return lastName; lastName; } // end method getLastName
Osnova
// set balance public void setBalance( setBalance( double bal ) { balance = bal; } // end method setBalance // get balance public double getBalance() getBalance() { return balance; } // end method getBalance } // end class AccountRecordSerializable
69
// Writing objects sequentially to a file with class ObjectOutputStream. import java. java.io. io.FileOutputStream; FileOutputStream; import java. java.io. io.IOException; IOException; import java. java.io. io.ObjectOutputStream; ObjectOutputStream; import java. java.util. util.NoSuchElementException; NoSuchElementException; import java. java.util.Scanner; util.Scanner;
Osnova
import stream_gen. stream_gen.AccountRecordSerializable _gen.AccountRecordSerializable; AccountRecordSerializable; public class CreateSequentialFile { private ObjectOutputStream output; output; // outputs data to file // allow user to specify file name public void openFile() openFile() { try // open file { output = new ObjectOutputStream( new FileOutputStream( FileOutputStream( "clients "clients. clients.ser" ser" ) ); } // end try catch ( IOException ioException ) { System. System.err. err.println( println( "Error "Error opening file." file." ); } // end catch } // end method openFile
70
// add records to file public void addRecords() addRecords() { AccountRecordSerializable record; // object to be written to file int accountNumber = 0; // account number for record object String firstName; firstName; // first name for record object String lastName; lastName; // last name for record object double balance; // balance for record object
Osnova
Scanner input = new Scanner( System.in ); System.out.printf( System.out.printf( "%s "%s\n%s\ n%s\n%s\ n%s\n%s\ n%s\n\n", "To terminate input, type the endend-ofof-file indicator ", "when you are prompted to enter input.", "On UNIX/Linux/Mac OS X type d then press Enter", Enter", "On Windows type z then press Enter" ); System.out.printf( System.out.printf( "%s "%s\n%s", n%s", "Enter account number (> 0), first name, last name and balance.", "? " ); while ( input.hasNext() input.hasNext() ) // loop until endend-ofof-file indicator { try // output values to file { accountNumber = input.nextInt(); input.nextInt(); // read account number firstName = input.next(); input.next(); // read first name lastName = input.next(); input.next(); // read last name balance = input.nextDouble(); input.nextDouble(); // read balance
71
if ( accountNumber > 0 ) { // create new record record = new AccountRecordSerializable( AccountRecordSerializable( accountNumber, accountNumber, firstName, firstName, lastName, lastName, balance ); output. output.writeObject( writeObject( record ); // output record } // end if else { System. System.out. out.println( println( "Account number must be greater than 0." ); } // end else } // end try catch ( IOException ioException ) { System. System.err. err.println( println( "Error "Error writing to file." file." ); return; return; } // end catch catch ( NoSuchElementException elementException ) { System. System.err. err.println( println( "Invalid input. Please try again." again." ); input.nextLine input.nextLine(); nextLine(); // discard input so user can try again } // end catch System. System.out. out.printf( printf( "%s %s\ %s\n%s", "Enter "Enter account number (>0),", "first name, name, last name and balance.", "? " ); } // end while } // end method addRecords
Osnova
72
// close file and terminate application public void closeFile() closeFile() { try // close file { if ( output != null ) output. output.close(); close(); } // end try catch ( IOException ioException ) { System. System.err. err.println( println( "Error "Error closing file." file." ); System.exit( System.exit( 1 ); } // end catch } // end method closeFile } // end class CreateSequentialFile
Osnova
73
// Testing class CreateSequentialFile. CreateSequentialFile. package stream_18; stream_18; public class CreateSequentialFileTest { public static void main( main( String args[] args[] ) { CreateSequentialFile application = new CreateSequentialFile(); CreateSequentialFile();
Osnova
application. application.openFile(); openFile(); application. application.addRecords(); addRecords(); application. application.closeFile(); closeFile(); } // end main } // end class CreateSequentialFileTest
74
Enter account number (>0), first name, last name and balance. ? 59 Kamil Strihavka 200 Enter account number (>0), first name, last name and balance. ? 61 Vlasta Kahovcova 430 Enter account number (>0), first name, last name and balance. ? ^Z
Osnova
75
// This program reads a file of objects sequentially // and displays each record. record. import java. java.io. io.EOFException; EOFException; import java. java.io. io.FileInputStream; FileInputStream; import java. java.io. io.IOException; IOException; import java. java.io. io.ObjectInputStream; ObjectInputStream;
Osnova Deserializace objektů
import stream_gen. stream_gen.AccountRecordSerializable _gen.AccountRecordSerializable; AccountRecordSerializable; public class ReadSequentialFile { private ObjectInputStream input; // enable user to select file to open public void openFile() openFile() { try // open file { input = new ObjectInputStream( ObjectInputStream( new FileInputStream( FileInputStream( "clients "clients. clients.ser" ser" ) ); } // end try catch ( IOException ioException ) { System. System.err. err.println( println( "Error "Error opening file." file." ); } // end catch } // end method openFile
76
// read record from file public void readRecords() readRecords() { AccountRecordSerializable record; record; System. System.out. out.printf( printf( "%"%-10s%10s%-12s%12s%-12s%10s\ 12s%10s\n", "Account "Account", Account", "First Name", Name", "Last "Last Name", Name", "Balance" );
Osnova
try // input the values from the file { while ( true ) { record = ( AccountRecordSerializable ) input. input.readObject(); readObject(); // display record contents System. System.out. out.printf( printf( "%"%-10d%10d%-12s%12s%-12s%10.2f\ 12s%10.2f\n", record. record.getAccount(), getAccount(), record. record.getFirstName(), getFirstName(), record. record.getLastName(), getLastName(), record. record.getBalance() getBalance() ); } // end while } // end try catch ( EOFException endOfFileException ) { return; return; // end of file was reached } // end catch catch ( ClassNotFoundException classNotFoundException ) { System. System.err. err.println( println( "Unable "Unable to create object." object." ); } // end catch
77
catch ( IOException ioException ) { System. System.err. err.println( println( "Error "Error during reading from file." file." ); } // end catch } // end method readRecords
Osnova
// close file and terminate application public void closeFile() closeFile() { try // close file and exit { if ( input != null ) input. input.close(); close(); System.exit( System.exit( 0 ); } // end try catch ( IOException ioException ) { System. file." ." ); System.err. err.println( println( "Error "Error closing file System.exit( System.exit( 1 ); } // end catch } // end method closeFile } // end class ReadSequentialFile
78
// This program test class ReadSequentialFile. ReadSequentialFile. package stream_20; stream_20; public class ReadSequentialFileTest { public static void main( main( String args[] args[] ) { ReadSequentialFile application = new ReadSequentialFile(); ReadSequentialFile();
Osnova
application. application.openFile(); openFile(); application. application.readRecords(); readRecords(); application. application.closeFile(); closeFile(); } // end main } // end class ReadSequentialFileTest
79
Osnova D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Stream>java stream_20.ReadSequentialFileTest Account First Name Last Name Balance 59 Kamil Strihavka 200,00 61 Vlasta Kahovcova 430,00
80