Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský 1 Soubory a proudy Rudolf Pecinovský
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský2 Koncepce čtení a ukládání dat
Copyright © 2008, Rudolf Pecinovský 3 Terminologie ►Soubor Entita (objekt) operačního systému sloužící jako obálka pro úschovu dat ►Datový proud Objekt programu sloužící ke zprostředkování přenosu dat mezi zdrojem a cílem ● Program může být zdrojem, cílem, či obojím ►Serializovatelnost Schopnost objektu být odeslán datovým proudem a zrekonstruovat se po přijetí zpět na plnohodnotný objekt ►Persistence Schopnost objektu uchovat svůj stav mezi dvěma seancemi
Copyright © 2008, Rudolf Pecinovský 4 Koncepce ►Java odděluje práci se soubory od práce s jejich obsahem a do jisté míry i od vlastní serializace ►Práce se soubory: ● Vytvoření, přesun, přejmenování, odstranění, … ● Zjištění názvu, velikosti, přístupových práv, … ►Práce s obsahem ● Otevření proudu ● Čtení a zápis dat ● Spláchnutí (flush) a zavření proudu ►Serializace ● Ukládání objektů a jejich rekonstrukce po opětném načtení včetně případných odkazů ● Řešení problémů s citlivými či zbytečně ukládanými daty ● Řešení problémů verzemi tříd ● Řešení problémů s objekty kontrolujícími vytváření svých instanci
Copyright © 2008, Rudolf Pecinovský 5 Historie ►Na počátku byla v balíčku java.io definována práce se soubory a s proudy bajtů ►Koncepce proudů založena na vzoru Dekorátor ►Při práci s proudy znaků v národních abecedách problémy => ve verzi 1.2 zavedeny znakové proudy ►Pro zvýšení efektivity nízkoúrovňových blokových operací byl ve verzi 1.4 přidán balíček java.nio ● Po přidání toho balíčku byly původní proudy z java.io upraveny tak, aby jej využívaly => nevyžadujeme-li zvýšenou efektivitu a škálovatelnost, není třeba měnit kód
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský6 Práce se soubory – třída java.io.File
Copyright © 2008, Rudolf Pecinovský 7 Charakteristika třídy File 1/2 ►Neměnný hodnotový typ ►Abstraktní reprezentace cesty k souboru či složce ● Nereprezentuje soubor, ale jeho název ►Díky abstrakci umožňuje definovat práci se soubory a složkami nezávisle na platformě a jejích specifikách ● Je schopna se přizpůsobit různým oddělovačům souborů na cestě ( / versus \ ) a cest v seznamu ( : versus ; ) ● Je schopna jednotně zacházet s různými přístupy ke kořenovým složkám ● Je schopna se vypořádat s různými interpretacemi vlivu velikosti znaků na vlastní cestu ● Umí správně reagovat na zadání relativní i absolutní cesty
Copyright © 2008, Rudolf Pecinovský 8 Charakteristika třídy File 2/2 ►Instance třídy File reprezentuje jak soubor, tak složku ● O koho se konkrétně jedná zjistíme až dotazem ►Instance může reprezentovat i neexistující soubor ● Zda existuje zjistíme dotazem ►Dotazy na reálné soubory a jejich vlastnosti jsou hlídány bezpečnostním správcem (security manager)
Copyright © 2008, Rudolf Pecinovský 9 Konstruktory a tovární metody ►Konstruktory ● File(String pathname) ● File(String parent, String child) ● File(File parent, String child) ● File(URI uri) ►Statické tovární metody ● createTempFile(String prefix, String suffix) ● createTempFile(String prefix, String suffix, File directory) ● File[] listRoots() ►Převody ● URI toURI() ● URL toURL() Deprecated – NEPOUŽÍVAT, Místo toho vytvořit URI a ten pak převést metodou toURL()
Copyright © 2008, Rudolf Pecinovský 10 Relativní – absolutní – kanonická cesta ►Cesty (String) ● String getName() ● String getParent() ● String getPath() ● String toString() ● String getAbsolutePath() ● String getCanonicalPath() ►Soubory ● File getParentFile() ● File getAbsoluteFile() ● File getCanonicalFile() ►Dotazy ● boolean isAbsolute() ►Kanonická cesta zaručuje, že instance reprezentující shodnou kanonickou cestu odkazují na týž soubor
Copyright © 2008, Rudolf Pecinovský 11 Soubory × složky ►Dotazy ● boolean isDirectory() ● boolean isFile() ● boolean isHidden() ►Obsah složky – názvy ● String[] list() ● String[] list(FilenameFilter filter) ►Obsah složky – soubory ● File[] listFiles() ● File[] listFiles(FileFilter filter) ● File[] listFiles(FilenameFilter filter ) ►Rozhraní FileFilter ● boolean accept(File pathname) ►Rozhraní FileNameFilter ● boolean accept(File dir, String name)
Copyright © 2008, Rudolf Pecinovský 12 Vlastnosti souboru ►Povolené čtení ● boolean canRead() ● boolean setReadable(boolean readable) ● boolean setReadable(boolean readable, boolean ownerOnly) ►Povolený zápis ● boolean canWrite() ● boolean setReadOnly() ● boolean setWritable(boolean writable) ● boolean setWritable(boolean writable, boolean ownerOnly) ►Povolené spuštění programu v souboru ● boolean canExecute() ● boolean setExecutable(boolean executable) ● boolean setExecutable(boolean execut, boolean ownerOnly) ►Čas poslední modifikace ● long lastModified() ● boolean setLastModified(long time) ►Další vlastnosti ● long length() ● boolean isHidden()
Copyright © 2008, Rudolf Pecinovský 13 Práce se soubory na disku ►Zjištění existence souboru ● boolean exists() ►Vytvoření souboru ● boolean mkdir() Vytvoří novou podsložku v existující rodičovské složce ● boolean mkdirs() Vytvoří novou podsložku včetně potřebných rodičovských složek ● boolean createNewFile() ►Mazání souboru ● boolean delete() Smaže soubor ihned ● void deleteOnExit() Smaže soubor při ukončení programu ►Přejmenování souboru ● boolean renameTo(File dest)
Copyright © 2008, Rudolf Pecinovský 14 Zjišťování souboru ve složce třídy ►Je-li soubor ve stejné složce jako class-soubor dané třídy, mohu jej vyhledat prostřednictvím class-objektu ● URL getResource(String name) ● InputStream getResourceAsStream(String name) ►Odvozujeme-li umístění souboru od složky s kořenovým balíčkem, využijeme ClassLoader – ten nabízí stejně metody, jen začíná v kořenovém balíčku ►Zadáním prázdného řetězce obdržíme URL složky s danou třídou, resp. s kořenovým balíčkem ►Tímto způsobem můžeme přistupovat i k obsahu souborů, které jsou součástí JAR ● V JARu nemůžeme pracovat se soubory, ale pouze s jejich obsahem ● Soubory v JARu můžeme číst, ale nemůžeme do nich zapisovat ● U názvů souborů v JARu VŽDY ZÁLEŽÍ na velikosti písmen
Copyright © 2008, Rudolf Pecinovský 15 Třída Soubor 1/5 import java.io.File; import java.net.URL; import java.net.URI; import java.net.URISyntaxException; /******************************************************************** * Knihovní třída Soubor} definuje metody usnadňující získání * instancí třídy File} reprezentujících požadovaný soubor. * Rudolf PECINOVSKÝ */ public class Soubor { //== KONSTRUKTORY A TOVÁRNÍ METODY ================================== /** Soukromý konstruktor bránící vytvoření instancí. */ private Soubor() {}
Copyright © 2008, Rudolf Pecinovský 16 Soubor. getFile(String, Object) 2/5 /********************************************************************* * Vrátí soubor se zadaným názvem zadaným vůči složce, * v níž je umístěn class-soubor zadané instance. * název Název požadovaného souboru instance Instance třídy, v jejíž složce se soubor nachází, * resp. odkud začíná jeho relativní adresa Instance třídy File} * reprezentující požadovaný soubor */ public static File getFile(String název, Object instance) { Class classObjekt = instance.getClass(); return getFile(název, classObjekt); }
Copyright © 2008, Rudolf Pecinovský 17 Soubor.getFile(String, Class ) 3/5 /********************************************************************* * Vrátí soubor se zadaným názvem zadaným vůči složce, * v níž je umístěn zadaný class-soubor. * název Název požadovaného souboru cls Class-objekt třídy, v jejíž složce se soubor nachází, * resp. odkud začíná jeho relativní adresa Instance třídy File} * reprezentující požadovaný soubor */ public static File getFile(String název, Class cls) { URL url = cls.getResource(název); if (url == null) { throw new IllegalArgumentException( String.format( "\nNebyl nalezen soubor \"%s\" ve složce \"%s\"", název, cls.getResource("") ) ); } return url2file(url); }
Copyright © 2008, Rudolf Pecinovský 18 Soubor.getFile(String) 4/5 /********************************************************************* * Vrátí soubor se zadaným názvem * zadaným vůči složce s kořenovým balíčkem. * název Název požadovaného souboru Instance třídy File} * reprezentující požadovaný soubor */ public static File getFile(String název) { ClassLoader clsLdr = Soubor.class.getClassLoader(); URL url = clsLdr.getResource(název); if (url == null) { throw new IllegalArgumentException( String.format( "\nNebyl nalezen soubor \"%s\" ve složce \"%s\"", název, clsLdr.getResource("") ) ); } return url2file(url); }
Copyright © 2008, Rudolf Pecinovský 19 Soubor.url2file(URL) 5/5 public static File url2file(URL url) { URI uri; try { uri = url.toURI(); }catch( URISyntaxException use ) { throw new RuntimeException( "\nZadané URL nelze konvertovat na URI", use); } File file; try { file = new File(uri); }catch( Exception e ) { String jar = uri.toString().startsWith("jar:") ? "\nHledaný soubor se nachází v souboru JAR" : ""; String s = "\nURI: " + uri + "\nnelze konvertovat na File; " + jar; throw new RuntimeException( s, e); } return file; }
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský20 Třída javax.swing.JFilechooser
Copyright © 2008, Rudolf Pecinovský 21 javax.swing.JFileChooser ►Umožňuje ● Vybrat více souborů současně ● Zadat, zda se má vybírat soubor, složka, či oboje ● Definovat filtry pro zobrazení vybraných typů souborů ● Zabudovat do dialogového okna náhled či jinou komponentu ● Začlenit okno jako komponentu do větších celků
Copyright © 2008, Rudolf Pecinovský 22 Postup pro otevření souboru1/4 1.Vytvoření instance třídy JFileChooser ● Vytvořenou instanci je výhodné používat vícekrát, neboť její vytvoření trvá relativně dlouho (zjišťují se dostupné diskové jednotky) ● Konstruktory: JFileChooser() JFileChooser(File currentDirectory) JFileChooser(String currentDirectoryPath) ●Bez parametru nebo s parametrem null začáíná v uživatelově implicitním adresáři 2.Nastavení počáteční složky pro vyhledávání pomocí setCurrentDirectory(File) 3.Přednastavení jména souboru pomocí metody setSelectedFile(File) 4.Potřebujeme-li umožnit vybrat více souborů současně, zavoláme setMultipleSelectionEnabled(boolean)
Copyright © 2008, Rudolf Pecinovský 23 Postup pro otevření souboru2/4 5.Nastavit filtry (masky) pro výběr souborů (např. pouze soubory *.txt) jako instance třídy javax.swing.filechooser.FileFilter ● Třída vyžaduje implementaci abstraktních metod ● boolean accept(File f) Vrací true, má-li se daný soubor nabízet ● String getDescription() Vrací popis filtru, podle nějž jej uživatel vybírá ● Java 6.0 zavedla předdefinovanou třídu FileNameExtensionFilter ●Té se zadá popis následovaný jednotlivými příponami FileNameExtensionFilter(String desc, String... ext) ● Filtrů může být více, přidávají se voláním void addChoosableFileFilter(FileFilter)
Copyright © 2008, Rudolf Pecinovský 24 Postup pro otevření souboru3/4 6.Zavoláním setFileSelectionMode(int) zadat, zda se mají vybírat adresáře a/nebo soubory ● JFileChooser.FILES_ONLY ● JFileChooser.DIRECTORIES_ONLY ● JFileChooser.FILES_AND_DIRECTORIES 7.Zobrazit dialogové okno pro výběr souborů či složek zavoláním metody ● showOpenDialog(Component parent) ● showSaveDialog(Component parent) ● showDialog(Component parent, String approveButtonText) Chceme-li sami zadat text na potvrzovacím tlačítku
Copyright © 2008, Rudolf Pecinovský 25 Postup pro otevření souboru4/4 8.Předchozí metody vracejí jednu ze dvou konstant ● JFileChooser.APPROVE_OPTION ● JFileChooser.CANCEL_OPTION 9.Vybraný(-né) soubor(y) získáme zavoláním ● File getSelectedFile() ● File[] getSelectedFiles() ►Existuje ještě plejáda dalších možností použití
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský26 Návrhový vzor Adaptér
Copyright © 2008, Rudolf Pecinovský 27 Motivace ►Občas potřebujeme, aby třída měla jiné rozhraní než to, které má ● Třída neimplementuje požadované rozhraní, nicméně poskytuje požadovanou funkčnost ● Příklad: používáme třídu z jedné knihovny, jejíž instance bychom mohli použít jako parametry metod jiné knihovny, ale tato jiná knihovna vyžaduje parametry implementující nějaké specifické rozhraní, které naše třída nezná ►Dopředu víme, že z hlediska požadované funkčnosti stačí implementovat pouze část daného rozhraní nicméně překladač vyžaduje kompletní implementaci ● Iterátor neumožňující odstraňovat prvky z kontejneru ● Kontejnery s předem zadaným, nezměnitelným obsahem ● Posluchači některých událostí při tvorbě GUI
Copyright © 2008, Rudolf Pecinovský 28 Adaptér jako rodič adaptované třídy ►Třída Adaptér definuje implicitní implementace všech metod požadovaných rozhraním IPožadované přičemž implicitní verze typicky: ● Vyhazuje UnsupportedOperationException ● Nedělá nic ►Potomci pak mohou definovat pouze ty metody, které se jim „hodí do krámu“ ►Pro klienta potomek implementuje vše
Copyright © 2008, Rudolf Pecinovský 29 Příklad – adaptér jako předek public interface IPosuvný extends IKreslený { //== DEKLAROVANÉ METODY ================================= public Pozice getPozice(); public void setPozice( Pozice pozice ); public void setPozice( int x, int y ); //== VNOŘENÉ TŘÍDY ====================================== public static class Adaptér extends IKreslený.Adaptér implements IPosuvný { public Pozice getPozice(){ throw new UnsupportedOperationException(); } public void setPozice( int x, int y ) { throw new UnsupportedOperationException(); } public void setPozice( Pozice pozice ) { setPozice( pozice.x, pozice.y ); } Metoda s definovatelnou implementací používající vzor Šablonová metoda Metody, o jejichž implementaci se uživatel může rozhodnout podle potřeby Adaptér je definován jako třída vnořená do rozhraní, na něž bude své potomky adaptovat
Copyright © 2008, Rudolf Pecinovský 30 Adaptovaný objekt jako atribut1/2 ►Adaptér definuje atribut s odkazem na adaptovaný objekt public class Adaptér implements IPožadované { Existující adaptovaný; public Adaptér(Existující exist) { adaptovaný = exist; }
Copyright © 2008, Rudolf Pecinovský 31 Adaptovaný objekt jako atribut2/2 ►Všechna volání metod „přehrává“ na volání ekvivalentních metod adaptovaného objektu public class Adaptér implements IPožadované { Existující adaptovaný; public Adaptér(Existující exist) { adaptovaný = exist; } public void metoda(Parametr parametr) { Požadovaný požadovaný = uprav(parametr); adaptovaný.jehoMetoda(požadovaný); }
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský32 Návrhový vzor Dekorátor
Copyright © 2008, Rudolf Pecinovský 33 Motivace ►V knihovně potřebujeme nabízet třídy‘ poskytující různé kombinace funkcionalit ►Při nutnosti nabízet téměř všechny možné kombinace roste počet potřebných tříd kombinatoricky = neúnosně ►S každou další přidanou funkcionalitou musíme přidat násobek dosud existujících tříd ►S každou přidanou základní třídou musíme přidat i třídy pro možné kombinace přidaných funkcionalit
Copyright © 2008, Rudolf Pecinovský 34 Řešení ►Využít postupu aplikovaného u návrhového vzoru Adaptér a zabalit objekt do objektu poskytujícího novou funkcionalitu, který tak původní objekt ozdobí (dekoruje) novou funkcionalitou ►Pro každou novou funkcionalitu pak bude stačit přidat pouze jedinou obalovou (dekorující) třídu ►Přidávané funkcionality je možné kumulovat, tj. zabalený objekt lze znovu zabalit do objektu přidávajícího další funkcionalitu ● Výsledný objekt lze vytvářet postupně: AAuto benzin = new Benzin(); AAuto abs = new ABS( benzin ); AAuto auto = new Automat(abs); ● Nebo najednou AAuto auto = new Automat( new ABS(new Benzin()) );
Copyright © 2008, Rudolf Pecinovský 35 Využití adaptéru ►Pro všechny dekorující třídy lze definovat společného rodiče, který volání neupravených metod převede na volání odpovídajících metod dekorovaného objektu ►Každá nově přidaná základní třída automaticky získává všechny v úvahu přicházející funkcionality
Copyright © 2008, Rudolf Pecinovský 36 Společný rodič dekorujících tříd public class Dekorovaný extends AAuto { AAuto dekorovaný; public Dekorovaný(AAuto auto) { dekorovaný = auto; } public void zrychliNa(int rychlost, int doba) { dekorovaný.zrychliNa( rychlost, doba ); } public void zpomalNa(int rychlost, int dráha) { dekorovaný.zpomalNa( rychlost, dráha ); } public void zabrzdiNa(int dráha) { dekorovaný.zabrzdiNa( dráha ); }
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský37 Čtení a zápis dat pomocí proudů
Copyright © 2008, Rudolf Pecinovský 38 Charakteristika ►Koncepce převzatá z C/C++ (1984) ►Datový proud je objekt zabezpečující tok dat mezi zdrojem a cílem ►Alespoň na jednom konci proudu musí být program ►Proudy umožňují jednotnou práci s daty nezávisle na jejich zdroji, cíli a a doprovodných akcích (používání vyrovnávací paměti, filtrování nezajímavých dat, šifrování, bezpečnostní kontroly, formátování …)
Copyright © 2008, Rudolf Pecinovský 39 Skupiny proudů ►V každé skupině jsou pak proudy lišící se podle zdroje/cíle ● Z/do souboru ● Z/do pole/objektu ● Z/do programu ● … java.io BajtovéZnakové Vstupní InputStreamReader Výstupní OutputStreamWriter
Copyright © 2008, Rudolf Pecinovský 40 Další možnosti ►Proudy mohou poskytovat dodatečnou funkčnost ● Používání vyrovnávací paměti (buffer) ● Čtení s možností vrácení přečtených znaků ● Převod dat do binárního tvaru a zpět ● Ukládání a načítání objektů (serializace) ● Komprimace a dekomprimace dat ● Šifrování a dešifrování ● … ►Proudy poskytující další funkčnost jsou definovány aplikací návrhového vzoru Dekorátor
Copyright © 2008, Rudolf Pecinovský 41 Přehled vstupních bajtových proudů ► InputStream ● ByteArrayInputStream – proud pro čtení z pole bajtů ● FileInputStream – proud pro čtení ze souboru ● FilterInputStream – univerzální rodič pro vstupní bajtové proudy s dodatečnou funkčností (společných rodič některých dekorátorů) ● BufferedInputStream – proud pro čtení s vyrovnávací pamětí ● DataInputStream – proud pro čtení hodnot primitivních typů a řetězců ● LineNumberInputStream ● PushBackInputStream – vstupní proud umožňující návrat a opětovné čtení ● ObjectInputStream – proud pro čtení serializovaných objektů ● PipedInputStream – proud pro čtení z „datovodu“ (trubky – pipe), do nějž zapisuje jiná část programu nebo jiný program. ● SequenceInputStream – zřetězení vstupních proudů ● StringBufferInputStream
Copyright © 2008, Rudolf Pecinovský 42 Přehled vstupních znakových proudů ► Reader ● BufferedReader – vstupní znakový proud využívající vyrovnávací paměť ● LineNumberReader – proud, který navíc umí pracovat s čísly řádků ● CharArrayReader – proud pro čtení z pole znaků ● FilterReader – univerzální rodič pro vstupní znakové proudy s dodatečnou funkčností (společných rodič některých dekorátorů) ● PushBackReader –proud umožňující návrat a opětovné čtení ● InputStreamReader – převaděč bajtového proudu na znakový ● FileReader – proud pro čtení ze souboru ● PipedReader – proud pro čtení z „datovodu“ (trubky – pipe), do nějž zapsal jiný modul či program ● StringReader – proud pro čtení z textového řetězce (stringu)
Copyright © 2008, Rudolf Pecinovský 43 Přehled výstupních bajtových proudů ► OutputStream ● ByteArrayOutputStream – proud pro zápis do pole bajtů ● FileOutputStream – proud pro zápis do souboru ● FilterOutputStream – univerzální rodič pro výstupní bajtové proudy s dodatečnou funkčností (společných rodič některých dekorátorů) ● BufferedOutputStream – proud využívající vyrovnávací paměť ● DataOutputStream – proud pro zápis hodnot primitivních typů a řetězců ● PrintStream – výstupní bajtový proud určený k tisku na výstupní zařízení ● ObjectOutputStream – výstupní bajtový proud schopný serializovat (uložit, odeslat, …) instance objektových typů ● PipedOutputStream – výstupní bajtový proud odesílající data prostřednictvím „datovodu“ (trubky – pipe) do jiného modulu či programu
Copyright © 2008, Rudolf Pecinovský 44 Přehled výstupních znakových proudů ► Writer ● BufferedWriter – výstupní znakový proud využívající vyrovnávací paměť ● CharArrayWriter – znakový proud zapisující do pole znaků ● FilterWriter – univerzální rodič pro výstupní znakové proudy s dodatečnou funkčností (společných rodič budoucích dekorátorů) ● OutputStreamWriter – proud převádějící bajtový proud na znakový ● FileWriter – výstupní znakový proud zapisující do souboru ● PipedWriter – výstupní znakový proud odesílající data prostřednictvím „datovodu“ (trubky – pipe) do jiného modulu či programu ● PrintWriter – znakový proud určený k tisku na výstupní zařízení ● StringWriter – znakový proud zapisující do textového řetězce
Copyright © 2008, Rudolf Pecinovský 45 Pomocná rozhraní ► FileFilter Filtrování souborů podle vlastností (název, délka, soubor/složka, …) ► FilenameFilter Filtrování souborů podle názvu ► DataInput Proudy schopné číst řetězce a hodnoty primitivních typů ► ObjectInput Proudy schopné číst (deserializovat) instance objektových typů ► Serializable Značkovací rozhraní implementované třídami, jejichž instance lze serializovat a deserializovat ► Flushable Objekty, které je možné „spláchnout“ příkazem flush() ► Closeable Objekty, které je možné zavřít příkazem close() ► DataOutput Proudy shopné odesílat řetězce a hodnoty primitivních typů ► ObjectOutput Proudy schopné odesílat (serializovat) instance objektových typů ► Externalizable Rozhraní pro alternativní způsob serializace/deserializace
Copyright © 2008, Rudolf Pecinovský 46 Společné charakteristiky ►Konstruktor proud současně také otevírá => instance proudu má smysl vytvářet až v okamžiku, kdy daný proud doopravdy potřebuji (jinak proud zbytečně blokuje zdroje) ►Při ukončení práce musím proud zavřít ● Všechny proudy implementují rozhraní Closeable, takže je pro ně možné vytvořit společnou zavírací metodu, které bude jednotným způsobem řešit případné problémy ►Chceme-li mít při použití výstupních proudů zaručeno, že data doopravdy odešla na výstupní zařízení (a neflákají se ve vyrovnávací paměti), musíme data spláchnout zavoláním metody flush() ● Všechny výstupní proudy implementují rozhraní Flushable, takže je pro ně možné vytvořit společnou zavírací metodu, které bude jednotným způsobem řešit případné problémy
Copyright © 2008, Rudolf Pecinovský 47 Metody vstupních proudů1/3 ►Stream: int available() ● Vrátí počet bajtů, které ještě mohou být přečteny nebo přeskočeny. ►Reader: boolean ready() ● Je-li možno přečíst další znaky bez čekání, vrátí true. Vrátí-li false, ještě to neznamená, že se při dalším čtení zablokuje, protože se mezi tím mohou nějaké znaky na vstupu objevit. Nicméně v okamžiku testování na vstupu zrovna nic není. ►Stream: int read() Reader: int read() ● Přečte ze vstupu další bajt, resp. znak a vrátí jej jako celé číslo v rozsahu 0 – 255 (bajt), resp. 0 – 0xFFFF (char). ● Metoda čeká na to, dokud ● není k dispozici čtený bajt, resp. znak – pak jej vrátí, ● není dosaženo konce souboru (pak v obou případech vrátí -1 ) nebo ● není detekována nějaká chyba (pak vyvolá výjimku IOException )
Copyright © 2008, Rudolf Pecinovský 48 Metody vstupních proudů2/3 ►Stream: int read(byte[] b) Reader: int read(char[] cbuf) ● Přečte ze vstupu další bajty, resp. znaky do zadaného vektoru a vrátí počet přečtených bajtů, resp. znaků. ● Metoda čeká na to, dokud ● nejsou nějaké bajty (znaky) k dispozici nebo ● dokud není dosaženo konce souboru nebo ● dokud není detekována nějaká chyba (pak vyvolá IOException ). ● Pokud nic nepřečte, protože bylo dosaženo konce souboru vrátí -1. ►Stream: int read(byte[] b, int off, int len) Reader: int read(char[] cbuf, int off, int len) ● Obdobně jako předchozí, jenom čte do předem definované části zadaného pole ►void close() ● Uzavře proud a uvolní alokované zdroje. Uzavření dříve uzavřeného proudu neudělá nic.
Copyright © 2008, Rudolf Pecinovský 49 Metody vstupních proudů3/3 ► long skip(long n) ● Přeskočí na vstupu zadaný počet bajtů (znaků). Vrátí skutečný počet přeskočených bajtů (znaků). ► boolean markSupported() ● Vrátí informaci o tom, zda daný proud podporuje umísťování značek. ► void mark(int readlimit) ● Označí zadanou pozici v proudu, aby se k ní mohl později vrátit. Parametr readlimit označuje, kolik bajtů (znaků) chce program mít možnost přečíst, než se k této zarážce vrátí a začne od ní číst znovu. ► void reset() ● Vrátí se v proudu k nastavené značce, aby odtud při příštím volání read začal znovu číst. Některé proudy umožňují volat reset bez předchozího volání mark a začínají pak číst celý proud znovu od počátku.
Copyright © 2008, Rudolf Pecinovský 50 Metody výstupních proudů1/2 ► void write(int b) ● Zapíše zadaný bajt, resp. znak na výstup. Z celého čísla, které je předáváno jako parametr, se na vstup pošle pouze příslušný počet nižších bajtů (stream 1, writer 2). Ostatní bajty jsou ignorovány. ►Stream: void write(byte[] b) Writer: void write(char[] cbuf) ● Pošle na výstup obsah zadaného vektoru. ►Stream: void write(byte[] b, int off, int len) Writer: void write(char[] cbuf, int off, int len) ● Pošle na výstup obsah len bajtů, resp. znaků ze zadaného vektoru od pozice off. ►Writer: void write(String s) Writer: void write(String s, int off, int len) ● Pošle na výstup zadaný řetězec, resp. jeho zadanou část.
Copyright © 2008, Rudolf Pecinovský 51 Metody výstupních proudů2/2 ► flush() ● Spláchne zadaný proud, tj. zabezpečí, aby se zapsaná data fyzicky dostala k požadovanému cíli. ● Je-li proud napojen na další proud, zabezpečí, aby se i tento proud spláchnul. ● Po úspěšně provedené operaci flush máme jistotu, že data jsou tam, kde mají být (pro případ následného zhroucení aplikace). ► close() ● Zavře daný proud a uvolní všechny alokované zdroje.
Copyright © 2008, Rudolf Pecinovský 52 XXX – Důležité metody dekorujících proudů
Copyright © 2008, Rudolf Pecinovský 53 Společné zásady ►Při čtení z disku a zápisu na disk je vhodné použít proudy s vyrovnávací pamětí, které práci velmi výrazně zrychlí ►Proudy by neměly zůstávat dlouho zbytečně otevřené, protože blokují zdroje OS ►Řada operací s proudy vyhazuje kontrolované výjimky. Tyto výjimky by neměly zůstat neošetřené. Neočekáváme-li, že k nim někdy dojde, měli bychom je ošetřit převodem na nekontrolované. ►Při práci s konzolou nastávají občas problémy s jazykovým nastavením – je třeba s nimi počítat
Copyright © 2008, Rudolf Pecinovský 54 Příklad: Převod kódování public class Převod_2 { //== KONSTANTNÍ ATRIBUTY TŘÍDY ======================================= private static final Charset ASCII_SET = Charset.forName("ASCII"); private static final PrintStream out = System.out; private static final PrintStream err = System.err; //== PROMĚNNÉ ATRIBUTY TŘÍDY ========================================= /** Kódová stránka zdrojových souborů. */ public static Charset sourceCP; /** Kódová stránka cílových souborů. */ public static Charset destCP; /** Kódová stránka výpisů na standardní výstup. */ public static Charset consoleCP; /** Složka se zdrojovými soubory. */ public static File sourceDir; /** Složka s cílovými soubory. */ public static File destDir;
Copyright © 2008, Rudolf Pecinovský 55 Příklad: převeďSoubor(File, File) private boolean převeďSoubor( File input, File output ) { println( out, "Převádíme soubor: " + input + "\n na soubor: " + output); LineNumberReader lnrd = otevřiSoubor( input ); OutputStream os = null; try { os = new BufferedOutputStream( new FileOutputStream( output ) ); }catch( IOException e ) { problém(e, "Nepodařilo se otevřít výstupní soubor " + output); }//try writer boolean odháčkovat = destCP.equals(ASCII_SET); try { String řádek; while( (řádek = lnrd.readLine()) != null ) { os.write( řádek.getBytes( destCP ) ); os.write( '\n' ); } }catch( IOException e ) { problém( e, "Při převodu ze souboru " + input + " do souboru " + output + " došlo k chybě" ); }finally { zavřiSoubory(lnrd, input, os, output); } return true; }
Copyright © 2008, Rudolf Pecinovský 56 Příklad: zavřiSoubory /********************************************************************* * Zavře zadaný vstupní a výstupní proud a v případě problémů * vypíše příslušnou zprávu. * Souborové parametry slouží pouze k vypsání případné chybové zprávy. is Zavíraný vstupní proud input Soubor z nějž čte vstupní proud os Zavíraný výstupní proud output Soubor, kam zapisuje výstupní proud */ private void zavřiSoubory( Closeable is, File input, Closeable os, File output) { try { is.close(); } catch (IOException e) { problém(e, "Nepodařilo se zavřít vstupní soubor " + input); } try { os.close(); } catch (IOException e) { problém(e, "Nepodařilo se zavřít výstupní soubor " + output); }
Copyright © 2008, Rudolf Pecinovský 57 Příklad: Převod kódování1/5 /********************************************************************* * Otevře zadaný soubor jako vstupní proud v potřebném kódování. * file soubor s parametry (měl by mít příponu.args). */ private LineNumberReader otevřiSoubor(File input) { LineNumberReader lnrd = null; try { FileInputStream fis = new FileInputStream( input ); Reader reader = new InputStreamReader( fis, sourceCP ); lnrd = new LineNumberReader( reader ); }catch( IOException e ) { problém( e, "Nepodařilo se otevřít vstupní soubor " + "\n" + input ); } return lnrd; }
Copyright © 2008, Rudolf Pecinovský 58 Příklad: kopírujSoubor(File, File) 1/2 private void kopírujSoubor( File input, File output ) { println( out, "Kopíruju soubor: " + input + "\n na soubor: " + output ); InputStream is = null; try { is = new BufferedInputStream( new FileInputStream( input ) ); }catch( IOException e ) { problém( e, "Nepodařilo se otevřít vstupní soubor " + "\n" + input ); }//try reader OutputStream os = null; try { os = new BufferedOutputStream( new FileOutputStream( output ) ); }catch( IOException e ) { problém( e, "Nepodařilo se otevřít výstupní soubor " + output ); }//try writer //... Pokračování na dalším snímku
Copyright © 2008, Rudolf Pecinovský 59 Příklad: kopírujSoubor(File, File) 2/2 try { int bajt; while( (bajt = is.read()) >= 0 ) { os.write( bajt ); } }catch( IOException e ) { problém( e, "Při kopírování ze souboru " + input + " do souboru " + output + " došlo k chybě" ); } finally { zavřiSoubory(is, input, os, output); }
Copyright © 2008, Rudolf Pecinovský 60 Příklad: převeďSložku( File, File) private boolean převeďSložku( File inputDir, File outputDir ) { if( !outputDir.exists() ) { outputDir.mkdirs(); } String[] seznam = inputDir.list(); //Převede jednotlivé soubory ve složce for( String název : seznam ) { File inFile = new File( inputDir, název ); File outFile = new File( outputDir, název ); if( inFile.isFile() ) { převeďSoubor( inFile, outFile ); } else if( inFile.isDirectory() ) { převeďSložku( inFile, outFile); } else { problém( new IllegalStateException(), " Převod selhal" ); } return true; }
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský61 Zásady správného programování ►Maximalizovat přehlednost, dodržovat konvence ►Programovat proti rozhraní, ne proti implementaci ►Důsledné zapouzdřování a skrývání implementace ►Upřednostňovat skládání před dědičností ►Jedna entita jeden úkol ►Návrh řízený odpovědnostmi ►Minimalizovat vzájemnou provázanost ►Vyhýbat se duplicitám v kódu ►Nepodřizovat návrh snahám o maximální efektivitu
Copyright © 2008, Rudolf Pecinovský 62 Maximalizovat přehlednost, dodržovat konvence ►Zákaznící si na programech vysoce cení celkové náklady (TCO = Total Cost od Ownership) ►TCO bývá pro mnohé důležitější než souhrn dostupných funkcí; funkce je možné doplnit, ale program s drahým provozem a drahými modifikacemi je prostě drahý ►Při návrhu programu je třeba dbát na jeho snadnou modifikovatelnost a spravovatelnost ►Vynikající programátor, v jehož kódu se nikdo jiný nevyzná, bývá pro tým většinou spíše ztrátou ►Pozor na programátory z MFF; jsou sice často skvělí, ale stejně často jsou to příliš velcí sólisté => potřebují schopného a znalého manažera
Copyright © 2008, Rudolf Pecinovský 63 Obecné konvence ►Identifikátory ● Smysluplné názvy ● Velikost písmen – při dodržování zásad poskytujete čtenáři informaci navíc ● Dodržovat konvence pro přístupové metody vlastností ►Odsazování kódu ● Vnořovat vnitřky bloků ● Zarovnávat závorky ● Odsazovat pokračovací řádky ►Celková úprava ● Dodržovat max. 80 znaků na řádek ● Nezhušťovat program, přehledný je lepší než stručný ● Vhodný překladač optimalizuje lépe než programátor ►Dokumentační komentáře psát poctivě
Copyright © 2008, Rudolf Pecinovský 64 Programovat proti rozhraní, ne proti implementaci ►Rozhraním se v tuto chvíli myslí jak interface, tak obecné rozhraní dané třídy/metody ►Proměnné, prostřednictvím nichž komunikuje modul s okolím, nemají být instancemi konkrétní třídy, ale instancemi rozhraní ● Příklady: Kalkulačka, kontejnery, ● Návrhový vzor Most, Zástupce, Šablonová metoda, … ►I u objektů, které jsou instancemi třídy, je třeba nezneužívat znalost implementace ►Komunikace prostřednictvím rozhraní uvolňuje ruce při příštích modifikacích ►Rozhraní by mělo být optimalizováno vzhledem k budoucím uživatelům
Copyright © 2008, Rudolf Pecinovský 65 Zapouzdřování ►Zapouzdřením se rozumí sdružení dat a je obhospodařujícího kódu na jedno místo ►Třída by neměla obsahovat metody, které pracují s cizími daty, ani atributy, s nimiž pracují cizí metody ►Výjimkou jsou třídy, jejichž hlavním účelem je poskytnout data sdílená celou skupinou tříd ►Jednotlivé zprávy posílané objektu by měly být pokud možno na sobě nezávislé, metody by měly požadovat jen nezbytně nutné parametry
Copyright © 2008, Rudolf Pecinovský 66 Skrývání implementace ►Zapouzdřování úzce souvisí se skrýváním implementace ►Autor třídy by si měl důkladně rozmyslet, které atributy a metody budou určeny k veřejnému použití, a které budou pouze služební, a proto soukromé ►Atributy by měly být zásadně soukromé a okolí by s nimi mělo komunikovat pouze prostřednictvím přístupových metod ►Jedinou výjimkou z předchozího pravidla mohou být nezměnitelné konstanty, a i u těch to musí být odůvodněné ►Na skrývání implementace je třeba dbát nejen uvnitř třídy, ale i mezi balíčky
Copyright © 2008, Rudolf Pecinovský 67 Oddělovat částí kódu, jež se asi budou měnit ►Aby změna jedné části kódu minimalizoval potřebu změn v ostatních částech, je třeba tu část kódu, která se bude pravděpodobně měnit, oddělit od zbytku ►Nejlepší způsob je vložit mezi měněný kód a zbytek světa rozhraní ( interface ), které bude daný kód implementovat ►Jakákoliv změna (kromě změny rozhraní) se pak daleko snáze provádí, protože není třeba mít neustále na paměti zbytek programu.
Copyright © 2008, Rudolf Pecinovský 68 Upřednostňovat skládání před dědičností ►Dědičnost narušuje zapouzdření i skrývání implementace ►Dědičnost bychom měli použít opravdu jen tehdy, kdy je její použití opodstatněné ►Kdykoliv je to jen trochu možné, měli bychom dát před dědičností přednost skládání ● Potenciální rodič je definován jako atribut potenciálního potomka ● Toto řešení bychom měli použít vždy, když by potomek nebyl schopen v jakékoliv situaci zastoupit svého potenciálního rodiče ►Špatné použití dědičnosti ● Letadlo – cyklista – čísla – geometrické tvary
Copyright © 2008, Rudolf Pecinovský 69 Jedna entita jeden úkol ►Programy by měly být maximálně soudržné, tj. žádná entita (balíček – třída – metoda – atribut) by neměla mít na starosti několik věcí současně ►Příklad: jízdenkový automat ►Metoda, která má více než 10 příkazů, „smrdí“ tím, že dělá několik věcí současně (Kent Beck: TDD) ● Student, který přijde ke zkoušce s programem, jehož metody mají více než 35 příkazů, vyletí od zkoušky, aniž bych se díval na jeho program (Cay Horstmann: Big Java) ►Velké entity vedou k nestabilitě programu a duplikaci kódu
Copyright © 2008, Rudolf Pecinovský 70 Návrh řízený odpovědnostmi ►RDD (Responsibility Driven Design) doporučuje, aby každý úkol měla na starosti pouze jedna entita ►Tento přístup velmi usnadňuje modifikaci programu – při změně či vylepšení funkce stačí upravit entitu, která je za danou funkci zodpovědná
Copyright © 2008, Rudolf Pecinovský 71 Minimalizovat vzájemnou provázanost ►Každá entita si má při plnění svých úloh vystačit pokud možno sama a minimálně se obracet na jiné entity => je třeba minimalizovat počet vazeb mezi entitami ►Začneme-li upravovat funkcionalitu entity, na které někdo závisí, je třeba zkontrolovat, nakolik je třeba upravit i závislou entitu ►Provázanosti se nelze zbavit, ale je třeba ji maximálně minimalizovat
Copyright © 2008, Rudolf Pecinovský 72 Vyhýbat se duplicitám v kódu ►Řeší-li se v programu několik věcí na různých místech velmi podobně nebo dokonce stejně, bylo by vhodné všechna řešení sloučit a na toto sloučené řešení se pak jenom odvolávat ►Stejné pravidlo platí jak pro data (konstanty), tak pro kód ►Řešení ● Společný rodič ● Služebník ● Metoda řešící společné části kódu ● Pojmenované konstanty místo literálů ►Výhody: ● Menší náchylnost k chybám, protože nevznikají chyby kopírováním ● Snazší modifikovatelnost, protože změnu je třeba provést jen na jednom místě
Copyright © 2008, Rudolf Pecinovský 73 Nepodřizovat zbytečně návrh efektivitě ►Nejčastější příčinou zkrachování projektu je předčasná snahy po optimalizaci (C.A.Hoare) ►Ve většině případů dokáže optimalizující překladač a virtuální stroj dosáhnout lepších výsledků než programátor ►„Ručně optimalizované“ programy mívají často takovou podobu, že už je překladač a VM nemohou dále optimalizovat, takže běží pomaleji, než kdyby optimalizovány nebyly ►Daleko větší přínos rychlosti většinou přinese zásadní změna algoritmu ►Zrychlení programu koupí rychlejšího stroje přijde často levněji než optimalizace programu
Copyright © 2008, Rudolf Pecinovský 74 Zkratky pro důležité zásady ►KISS – Keep It Small, Simple (Stupid) ● Nepokoušet se hned o maximální funkčnost, naprogramovat v prvním kole jenom nezbytně nutnou funkčnost, kterou budeme v dalších kolech doplňovat a vylepšovat ►TDD – Test Driven Development ● Nejdříve je třeba navrhnout testy, teprve pak můžeme navrhovat program, který bude tyto testy plnit ►POJO – Plain Old Java Object ►LSP – Liskov Substitution Principle ● Potomek musí být vždy schopen plnohodnotně vystupovat v roli předka ►DRY – Don‘t Repeat Yourself ● Maximálně se vyhýbat
Vaše jistota na trhu IT Copyright © 2008, Rudolf Pecinovský75 Děkuji za pozornost Rudolf Pecinovský mail: ICQ:
Copyright © 2008, Rudolf Pecinovský 76
Copyright © 2008, Rudolf Pecinovský 77 Pgm Používaná písma a objekty ► Pgm Příliš žluťoučký kůň úpěl ďábelské ódy (Demi) ● Pgm Příliš žluťoučký kůň úpěl ďábelské ódy (Medium) ● Pgm Příliš žluťoučký kůň úpěl ďábelské ódy (Cond) ►Příliš žluťoučký kůň úpěl ďábelské ódy (Heavy) ● Příliš žluťoučký kůň úpěl ďábelské ódy (Franklin Gothic Book) ● Příliš žluťoučký kůň úpěl ďábelské ódy (Comic Sans MS) ● Příliš žluťoučký kůň úpěl ďábelské ódy (Consolas) Program Keyword Opakování Příliš žluťoučký kůň úpěl ďábelské ódy
Copyright © 2008, Rudolf Pecinovský 78 Konvence syntaktických definic Syntaktická definice = pravidla zápisu konstrukce jazyka Název–Název definované součásti součást –Název součásti definované jinde program –Text, který se přímo použije (opíše) [ ]–Obsah hranatých závorek je volitelný { | }–Výběr z několika možností oddělených | –Druhou možností je psaní možností pod sebe …–Předchozí položka se může opakovat ¶–Definice pokračuje na dalším řádku kde bude navíc odsazená Příklad:Identifikátor:písmeno [ { písmeno | číslice } ] … Číslice: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 Originál: P02_Prvni_kod.ppt#23. Konvence syntaktických definicP02_Prvni_kod.ppt#23. Konvence syntaktických definic