Java niezbędnik programisty spotkanie nr 11 Importy statyczne, wejście/wyjście, wyrażenia regularne, serializacja 1
Importowanie składowych statycznych Opracował Import static java.lang.system.out; //import static java.lang.system.out.println; //tylko statyczne składowe public class Test { public static void main(string[] args) { out.println("witaj świecie!"); Działa dla metod, atrybutów i klas. Klasy można też importować w zwykły sposób: import java.lang.character.subset; Stosować jeżeli składowa jest używana co najmniej 3 razy. Typowe przykłady import static java.util.arrays.sort; import static java.lang.math.*; import static mójpakiet.mójtypwyliczeniowy.*; 2
Pełna nazwa klasy jest konieczna Trzeba podać pełną nazwę klasy, z której są importowane statyczne składowe. Nawet jeśli wcześniej importujemy tą klasę w zwykły sposób: import java.util.arrays; import static java.util.arrays.*; Nawet jeśli tą klasę zadeklarowano w tym samym pakiecie. Nawet jeśli jest to klasa z pakietu java.lang. 3
Składowe o tej samej nazwie Kolejny sposób na przeciążanie metod. import static java.util.arrays.sort; import static java.util.collections.sort; Tak jak w przypadku zwykłych importów, konflikty pojawiają się dopiero w momencie kiedy kompilator rzeczywiście musi zdecydować: import static java.util.arrays.sort; import static w11.sortownica.sort; class Sortownica { public static void sort(float[] t) { out.println("sortownica"); public class Test { public static void main(string[] args) { //sort(new float[] {); Analogicznie w wypadku, gdy oba importy z *. Tu nie ma niejednoznaczności import static w11.sortownica.*; import static java.util.arrays.sort; 4
Przesłanianie Deklaracje lokalne przesłaniają importy statyczne (i z gwiazdką i konkretne). import static java.util.arrays.*; import static w11.sortownica.sort; public class Test { public static void sort(float[] t) { out.println("test"); public static void main(string[] args) { sort(new float[] {); sort(new int[] {); Dla przypomnienia (dla zwykłych importów jest różnica): ok import java.util.*; class Arrays { konflikt import java.util.arrays; class Arrays { 5
Przeglądanie systemu plików Klasa File służy do reprezentowania nazw plików i katalogów w sposób niezależny od systemu operacyjnego. Katalogi można przeglądać. list() zwraca tablicę napisów, a listfiles() tablicę obiektów File. Są też wersja wykonujące filtrowanie przy pomocy wzorca Strategia list(filenamefileter) i listfiles(filefileter/filenamefilter). Można też: sprawdzać rozmiar pliku, zmieniać nazwy, tworzyć/kasować pliki/katalogi, tworzyć pliki tymczasowe o generowanych nazwach, itd. 6
Przykład import java.io.*; import java.util.*; import java.util.regex.*; public class DirList { public static void zawartośćkatalogu() { File path = new File("c:\\"); String[] list; list = path.list(); Arrays.sort(list); for (String s : list) System.out.println(s); 7
Filtrowanie File path = new File("c:\\"); final String wreg = ".*?\\.sys"; String[] list = path.list(new FilenameFilter() { private Pattern pattern = Pattern.compile(wReg); public boolean accept(file dir, String name) { return pattern.matcher( //pozbywamy się ścieżki do katalogu new File(name).getName()).matches(); ); Arrays.sort(list); for(string s : list) System.out.println(s); 8
Strumienie Do obsługi danych binarnych służą: InputStream i OutputStream, a do obsługi danych Unicode Reader i Writer. Źródłem danych dla strumieniu może być: tablica bajtów, napis, plik, gniazdo, strona WWW dostępna w sieci, itd. Nadmiaru klas możemy uniknąć dzięki wzorcu Dekorator strumienie z nowymi możliwościami tworzymy z innych strumieni przy pomocy klasy opakowującej (klasy Filter...). Dodatkowa klasa RandomAccessFile pozwala na przeglądanie pliku zawierającego rekordy o znanym rozmiarze i przeskakiwanie pomiędzy nimi przy pomocy funkcji seek(). 9
Przykład //Odczytujemy plik linia po linii. try { BufferedReader in = new BufferedReader(new FileReader("c:\\kol2.txt")); String s = new String(); while ((s = in.readline())!= null) System.out.println(s); in.close(); catch (IOException e) { e.printstacktrace(); 10
Przykład BufferedReader in = new BufferedReader(new FileReader("c:\\kol2.txt")); String s, s2 = new String(); while ((s = in.readline())!= null) s2 += s + "\n"; in.close(); StringReader in2 = new StringReader(s2); int c; while((c = in2.read())!= -1) System.out.print((char)c); try { DataInputStream in3 = new DataInputStream( new ByteArrayInputStream(s2.getBytes())); //sensownie wypiszą się jedynie znaki ASCII while(true) System.out.print((char)in3.readByte()); catch(eofexception e) { System.err.println("Koniec"); 11
System.out, System.in, System.err System.out i System.err są już opakowane w PrintStream. System.in trzeba samemu opakować. BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String s; //do pierwszej pustej linii lub Ctrl-Z while ((s = in.readline())!= null && s.length()!= 0) System.out.println(s); Przy pomocy statycznych metod System.setIn(InputStream), System.setOut(PrintStream), System.setErr(PrintStream) można dokonać podmiany. 12
nio W Javie 1.4 przebudowano implementację strumieni (teraz są oparte na kanałach i buforach z pakietu java.nio). Od Javy 1.4 dzięki selektorom można tez nasłuchiwać zdarzeń z wielu kanałów naraz. Więcej na Java wybrane technologie. 13
Kompresowanie pojedynczego pliku GZIP Opracował BufferedReader in = new BufferedReader(new FileReader("c:\\kol2.txt")); //kompresja BufferedOutputStream out =new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream("c:\\test.gz"))); int c; while ((c = in.read())!= -1) out.write(c); in.close(); out.close(); //dekompresja BufferedReader in2 = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream("test.gz")))); String s; while ((s = in2.readline())!= null) System.out.println(s); in2.close(); 14
Kompresja wielu plików ZIP public static void main(string[] args) throws IOException { FileOutputStream f = new FileOutputStream("c:\\test.zip"); CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32()); ZipOutputStream zos = new ZipOutputStream(csum); BufferedOutputStream out = new BufferedOutputStream(zos); zos.setcomment("testowe archiwum ZIP"); for (int i = 0; i < args.length; i++) { System.out.println("Zapisywanie " + args[i]); BufferedReader in = new BufferedReader(new FileReader(args[i])); zos.putnextentry(new ZipEntry(args[i])); int c; while ((c = in.read())!= -1) out.write(c); in.close(); out.close(); System.out.println("CRC: " + csum.getchecksum().getvalue()); 15
Kompresja wielu plików ZIP... FileInputStream fi = new FileInputStream("c:\\test.zip"); CheckedInputStream csumi = new CheckedInputStream(fi, new CRC32()); ZipInputStream in2 = new ZipInputStream(csumi); BufferedInputStream bis = new BufferedInputStream(in2); ZipEntry ze; while ((ze = in2.getnextentry())!= null) { System.out.println("Odczytywanie " + ze); int x; while ((x = bis.read())!= -1) System.out.write(x); System.out.println(); System.out.println("CRC: " + csumi.getchecksum().getvalue()); bis.close(); 16
Czy takiego wyjścia się spodziewaliśmy? Opracował Ten przykład został zaczerpnięty z TIJ 3ed. Czy wynik jest zgodny z naszymi oczekiwaniami? Zapisywanie c:\a\a.txt Zapisywanie c:\a\b.txt Zapisywanie c:\a\c.txt Zapisywanie c:\a\c\d.txt CRC: 1742864089 Odczytywanie c:\a\a.txt Odczytywanie c:\a\b.txt Odczytywanie c:\a\c.txt Odczytywanie c:\a\c\d.txt» ala ma kota» i ola ma kota» nawet ula ma kota» ale ela nie ma CRC: 1742864089 17
Czego brakowało? FileOutputStream f = new FileOutputStream("c:\\test.zip"); CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32()); ZipOutputStream zos = new ZipOutputStream(csum); BufferedOutputStream out = new BufferedOutputStream(zos); zos.setcomment("testowe archiwum ZIP"); for (int i = 0; i < args.length; i++) { System.out.println("Zapisywanie " + args[i]); BufferedReader in = new BufferedReader(new FileReader(args[i])); //zamyka poprzedni wpis i pozycjonuje strumień na początku następnego zos.putnextentry(new ZipEntry(args[i])); int c; while ((c = in.read())!= -1) out.write(c); out.flush();//potrzebne mimo putnextentry() in.close(); out.close(); 18
Serializacja Obiekty, które implementują interfejs Serializable można zamienić na strumień bajtów. zapisywanie/odczytywanie z plików (za wszystko odpowiada programista, zapytania wymagają wczytania wszystkich obiektów) wysyłanie przez sieć (również pomiędzy różnymi systemami operacyjnymi, kodowaniem bajtów, itp.) JavaBeans, RMI, EJB (uwaga na semantykę przekazywania parametrów) Klasy opakowujące, tablice i kolekcje implementują Serializable. Serializable nie wymusza dodawania żadnych metod. Do serializacji/deserializacji służą klasy ObjectOutputStream i ObjectInputStream, które zawierają odpowiednio metody writeobject( ) i readobject( ) Serializowany jest cały graf obiektów. 19
Przykład public class Ser implements Serializable { Random r = new Random(); public Ser dowiązanie; public String s; protected char c; private int[] tab; public Ser(char c, String s) { this.c = c; this.s = s; int i = r.nextint(10); tab = new int[i]; for (i = 0; i<tab.length; i++) tab[i] = r.nextint(9); public Ser() { System.out.println("Domyślny konstruktor"); public String tostring() { return "{c=" + c + "; s=" + s + "; " + "tab=" + tab + "; dowiązanie=" + dowiązanie + "";... 20
Przykład public static void main(string[] args) throws ClassNotFoundException, IOException { Ser s = new Ser('a', "aaa"); s.dowiązanie = new Ser('b', "bbb"); System.out.println(s); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("c:\\ser.out")); out.writeobject("tu leży Ser\n"); out.writeobject(s); out.close(); ObjectInputStream in = new ObjectInputStream( new FileInputStream("c:\\ser.out")); String napis = (String) in.readobject(); Ser s2 = (Ser) in.readobject(); System.out.println(napis + "s2 = " + s2); System.out.println("s == s2: " + (s == s2)); 21
Wynik {c=a; s=aaa; tab=[i@130c19b; dowiązanie={c=b; s=bbb; tab=[i@1f6a7b9; dowiązanie=null Tu leży Ser s2 = {c=a; s=aaa; tab=[i@66848c; dowiązanie={c=b; s=bbb; tab=[i@8813f2; dowiązanie=null s == s2: false 22
Co w trawie piszczy Żeby deserializacja się udała odczytywana klasa musi być widoczna w CLASSPATH maszyny wirtualnej. Jak widać obiekt został utworzony bez wywołania konstruktora. Modyfikator transient zabrania serializacji danego atrybutu (przy deserializacji będzie wypełniony domyślną wartością). Dodając do naszej klasy prywatne (pewnie dlatego nie są w interfejsie) metody (które będą wykryte dzięki mechanizmowi odzwierciedleń): private void writeobject(objectoutputstream stream) throws IOException private void readobject(objectinputstream stream) throws IOException, ClassNotFoundException można ręcznie wykonać serializację składowych transient. Jeżeli chcemy skorzystać ze standardowej serializacji zwykłych składowych trzeba jako pierwszą operację w tych metodach wywołać magiczne metody: stream.defaultwriteobject( ) i stream.defaultreadobject( ). Można też w samemu obsłużyć serializację implementując interfejs Externalizable (wraz z metodami writeobject( ) i readobject( )) 23
Kopiowanie obiektów Serializacji można użyć do robienia głębokich kopii obiektów. Co jeśli serializacja obiektu X doprowadzi do serializacji obiektów A i B, które oba miały referencję do obiektu C? Czy po deserializacji powstaną dwie kopie C? Wszytko będzie dobrze i to nawet jak A i B serilizowaliśmy sami (ale do tego samego strumienia). W przypadku innych strumieni otrzymamy kopie. Uwaga na zmiany definicji zapisanej klasy (nowa wersja). 24