Lecture 7 - streams Advanced Java Programming 1 dr hab. Szymon Grabowski dr inż. Wojciech Bieniecki...

Preview:

Citation preview

Lecture 7 - streams

AdvancedJava

Programming

1

dr hab. Szymon Grabowskidr inż. Wojciech Bienieckiwbieniec@kis.p.lodz.plhttp://wbieniec.kis.p.lodz.pl

What is a streamProgramming of I/O operationsin Java is to use streams that are class in the package java.io

Table: The ancestors of the class hierarchy stream - abstract classes

Input Output

Byte Streams InputStream OutputStream

Character sterams

Reader Writer

The data stream is a logical concept.

Means a structure similar to a queue to which the data can be added and from which data can be downloaded.

Basic operations on the stream: read and write data.

3

Basic classes for reading and writing sequences of bytes are FileInputStream and FileOutputStream.

FileInputStream inp = new FileInputStream("1.dat");FileOutputStream outp = new FileOutputStream("2.dat");

Read a byte (0..255), write a byte: int read(); write(int);

Read value –1 signals the end of file.

The (main) hierarchy of input / output classes starts withabstract classes InputStream and OutputStream.

Intro to I/O

When we create a file object (eg. of any of the two just mentioned classes), the file is open.

44

Don’t forgetexceptionhandling!

Byte-by-byte copy example

5

We already know that read() is abstract.The methods

read(b) == read(b, 0, b.length)

If b.length == 0 (or param len == 0 in the latter method), then the methods return 0.Nothing to read (EOF)? The methods return –1.

read(...) methods from InputStream

int read(byte[ ] b),int read(byte[ ] b, int off, int len)

are non-abstract, but they internally invoke read().

Moral: any subclass of InputStreammust implement read().

Binding the stream with the source or receiver

Table: Subject classes (associated with a particular source / receiver)

Source/receiverCharacter streams Byte streams

Memory CharArrayReaderCharArrayWriter

ByteArrayInputStreamByteArrayOutputStream

StringReaderStringWriter

(not used because of improper byte-to-character conversion)

Pipeline (allows exchange data between two threads)

PipedReader PipedWriter

PipedInputStreamPipedOutputStream

File FileReader FileWriter

FileInputStreamFileOutputStream

7

FileInputStream and FileOutputStream can read / write only bytes.

Fortunately, there are classes DataInputStream, DataOutputStream, but...their constructors obtain an abstract stream(InputStream, OutputStream, respectively).

How to work with files?

The solution is typical for Java I/O: combining filters.

FileInputStream fin = new FileInputStream("numbers.dat");DataInputStream din = new DataInputStream(fin);

double x = din.readDouble();

Combining stream functionalities

Would be useful to have classes for reading / writing int, longs, doubles...

8

DataInputStream din = new DataInputStream(new BufferedInputStream(new FileInputStream("numbers.dat")));

The Decorator pattern lets us add functionality to an object at runtime.

Actually this is the Decorator design pattern

The idea. Object x from class X contains object y from Y. Object x will be called a decorator.

x forwards method calls to y.x conforms to the interface of y, which allowsthe decorator to be used as if x were an instance of Y.(But typically x has its own capabilities, not existing in y.)

9

FileOutputStream(String name)FileOutputStream(String name, boolean append)FileOutputStream(File file)FileOutputStream(File file, boolean append)

append == true data are added at the end of the file(otherwise an existing file is deleted and start from scratch)

FileOutputStream constructors

Transforming classesIn I/O operations data transformation can be performed.

Type of processingCharacter streams Byte streams

Bufferinf BufferedReaderBufferedWriter

BufferedInputStreamBufferedOutputStream

Filtering – these classes define the methods for final filtersFinal filters are: DataInputStream & DataOutputStream, BufferedInputStream BufferedOutputStream. LineNumberInputStream, PushbackInputStream, PrintStream. You can create custom filters

FilterReaderFilterWriter

FilterInputStreamFilterOutputStream

Java has many classes specialized in automatic processing specific types of streams.

These classes implement certain types of process streams, regardless of the source / receiver

Transforming classesType of processing

Character streams Byte streams

Byte-to-character conversion

InputStreamReaderOutputStreamWriter

Concatenate SequenceInputStream

Object serialization ObjectInputStream ObjectOutputStream

Data conversion DataInputStream DataOutputStream

Line counting LineNumberReader LineNumberInputStream

Previewing PushbackReader PushbackInputStream

Printing PrintWriter PrintStream

To apply processing classes you must:Create an object associated with a physical source.receiverCreate an object that is overlayed on the physical stream

12

Use FileReader, FileWriter classes.

FileReader: converts from the default codepage to Unicode. FileWriter: the other way around...

import java.io.*;public class ReadByReader { public static void main(String[] args) { StringBuffer cont = new StringBuffer(); try { FileReader inp = new FileReader(args[0]); int c; while ((c=inp.read()) != -1) cont.append((char)c); inp.close(); } catch(Exception e) { System.exit(1); } String s = cont.toString(); System.out.println(s); }}

Text I/O

13

If the characters in the file to read adhere to non-standard encoding, don’t use FileReader.

FileInputStream fis = new FileInputStream("test.txt");InputStreamReader isr = new InputStreamReader(fis, "UTF8");

Non-standard character encoding

Use its parent class, InputStreamReader, instead.OutputStreamWriter for writing, of course.

14

On slide 8, the method read(), from InputStreamReader (not FileReader), was used.

Reading a text (character) file

It reads a single char (a variant reading a number of chars into an array also exists).

For a greater flexibility, use Scanner.

15

For writing to a text file, use PrintWriter

BufferingBuffering reduces the number of physical references to external devices.

For example, reading large text files it should be avoided by reading the FileReader class.

Using BufferedReader class will improve the effectiveness of the program. But BufferedReader class is a processing claass, therefore can not directly

take the physical data source. This source is served to the FileReader object which is wrapped into a BufferedReader

FileReader fr = new FileReader("plik.txt");BufferedReader br = new BufferedReader(fr);String line;while ((line - br. readLine()) != null){// processinf a line}

Buffering find a word in a file

class Search{ public boolean hasAnyWord(String fname, Hashtable wordtab) { boolean result = false; try{ String line; FileReader fr = new FileReader(fname); BufferedReader br = new BufferedReader(fr);

br.close(); } catch (IOException e){ System.err.println(e); } return result; }}

search: while ((line = br.readLine()) != null ){ StringTokenizer st = new StringTokenizer(line, " ,.:;()\t\r\n"); while (st.hasMoreTokens()){ String word = st.nextToken(); if (wordtab.get(word) != null){ result = true; break search; } } }

Buffering find a word in a file

public class Szukaj{ public static void main(String[] args){/* argumenty: nazwa_pliku slowo1 slowo2 ... */ if (args.length < 2) System.exit(1);

Search srch = new Search(); boolean result = srch.hasAnyWord(args[0], words); String msg = " nie zawiera zadnego ze slow:"; if (result) msg = " zawiera ktores ze slow:"; System.out.println("Plik "+args[0]+msg); Enumeration en = words.keys();// uzyskujemy wszystkie klucze tablicy while (en.hasMoreElements()){ // ... i przebiegamy je po kolei String word = (String) en.nextElement(); System.out.println(word); } }}

Object dummy = new Object(); Hashtable words = new Hashtable(); for (int i = 1; i<args.length; i++) words.put(args[i], dummy);

encodingava uses Unicode characters. These are 16-bit size.

If the constructors of these classes do not specify an encoding, the conversions will be accepted by default.

public class DefaultEncoding{ public static void main(String args[]) { String p = System.getProperty("file.encoding") ; System.out.println(p); }}

Exemplary results: ISO8859_1, ibm-852, Cp852 (Latin 2) , Cpl252 (Windows Western Europe/Latin-1).

Character streams can - invisible to us - convert the source byte character streams, and vice versa. "Under cover" of this process, there are two classes:

InputStreamReader and OutputStreamWriter, which make the appropriate conversions when reading / writing.

HTML conversionimport java.io.*;public class Convert{ public static void main(String[] args) { String infile = args[0],// plik we in_enc = args[1], // strona kodowa wejścia outfile = args[2], // plik wynikowy out_enc = args[3]; // strona kodowa wyjścia

int c; while ((c = in.read()) != -1) out.write(c); in.close(); out.close(); } catch (IOException e){ System.err.println(e);} }}

try{ FileInputStream fis = new FileInputStream(infile); InputStreamReader in = new InputStreamReader(fis, in_enc); FileOutputStream fos = new FileOutputStream(outfile); OutputStreamWriter out = new OutputStreamWriter(fos, out_enc);

21

In Java, there exists a powerful mechanism for storing arbitrary objects in files (e.g., on disk).Or, more generally, sending them to a stream.

We save objects and then can recover them.This uses object serialization.

Basically, we can use methods writeObject()and readObject() – for any objects.

myOutFile.writeObject(thisObject);val = (MyClass)myInFile.readObject(); // casting necessary

Object I/O

22

We shall use classes derived from OutputStream and InputStream:

OutputStreamFileOutputStreamObjectOutputStream

InputStreamFileInputStreamObjectInputStream

Import them from java.io.

Object I/O

23

For output:

ObjectOutputStream out;out = new ObjectOutputStream (

new(FileOutputStream("myFile.dat") );// use out.writeObject(...)out.close();

For input (say, we read from the same file):

ObjectInputStream inp;inp = new ObjectOutputStream (

new(FileOutputStream("myFile.dat") );// use inp.readObject()inp.close();

Object I/O

24

writeObject() is powerful. It converts an object of any class into a bit sequence and then

sends it to a stream, in a way that it can be subsequently retrieved

(using readObject()) as an object of the original class.

Not surprisingly, static fields are NOT written to the stream.

Serialized objects may contain e.g. other objects as their fields.

No problem with e.g. arrays either.Even multi-dim arrays (btw, in Java a 2-d array is,

formally speaking, an array of arrays).

Object serialization

25

Our class must simply implement the interfaceSerializable.

public class Tank implements Serializable { ... }

Serializable is empty (=has no methods)!Marker interfaces simply make possible to check

a data type, e.g. with instanceof operator.

That’s what the writeObject(...) method does:checks if

x instanceof Serializable == true.

If so, it sends the object to a stream, otherwise it doesn’t.

(Other marker interfaces: Clonable, RandomAccess...)

How to serialize our own class

26

When we read an object with readObject(),what we get is actually an object of the class Object().

Cast examples:

Student s = (Student)inpFile.readObject();

int[] tab = (int[])inpFile.readObject();

Casting required

27

The signatures are:

public final readObject() throws IOException, ClassNotFoundException;

public final void writeObject(Object obj)throws IOException;

Remember about (checked) exceptions

28

public class GradStudent{ private Supervisor sv; ... }// many grad students may have the same supervisor

No problem! Each GradStudent storesa ‘reference’ to the same Supervisor.

BUT! Those refs are not just mem addresses!Instead, SERIALizable objects get unique

SERIAL numbers.

What if an object is shared by a coupleothers as part of their state?

29

• each encountered object reference gets a unique serial number,

• if an object reference is encounted for the first time, the object data are saved to the output stream,

• else (object ref already saved), it is written that here we have the same object as the one already saved with serial number x.

Reading back: reversed procedure.

Serialization algorithm

30

Yes.

Mark a field with keyword transient and it won’t be serialized.

E.g. (example from Core Java, vol. 2):

public class LabeledPoint implements Serializable{ . . . private String label; private transient Point2D.Double point;}

// because Point2D.Double from java.awt.geom // is not serializable and we want to avoid NonSerializableException

Can we serialize selected fields?

31

Sometimes we’re not happy with the default serialization mechanism.

Cont’d previous slide example:we do want to serialize the data from the Point2D.Double field.

Shall we write the serialization routine from scratch?

First we label the Point2D.Double field as transient (of course).Then we implement

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

private void writeObject(ObjectOutputStream out) throws IOException;

A serializable class is allowed to define those methods.

Serialize on our own

32

private void writeObject(ObjectOutputStream out) throws IOException{ out.defaultWriteObject(); // can be called only from writeObject(...) ! out.writeDouble(point.getX()); out.writeDouble(point.getY());}

private void readObject(ObjectInputStream in) throws IOException{ in.defaultReadObject(); // can be called only from readObject(...) ! double x = in.readDouble(); double y = in.readDouble(); point = new Point2D.Double(x, y);}

Serialize on our own, cont’d

33

writeObject(...) writes several things, incl. class nameand 8-byte class fingerprint.

The fingerprint is to ensure the class definition has not changed (between serialization and deserialization).

What if it has changed?

Indicate that the new class is compatible with the old one.

Run serialver OurClass for the old class to obtain its fingerprint.

Then add as (e.g.):public static final long serialVersionUID =

-1814239825517340645L;to the new version of OurClass.

No problemif only methods

changed.

If new fields added: risky.

If field type changed: even worse.

Versioning

34

The package java.util.zip contains several classes for data compression / decompression.

A few class examples:

Deflater – in-memory compression using zlib library

Inflater – in-memory decompression using zlib library

GzipOutputStream – compresses data using the GZIP format. To use it, construct a GZIPOutputStream that wraps a regular output stream and use write() to write compressed data

GzipInputStream – like above, but for reading data

ZipOutputStream, ZipInputStream – I won’t offend your intelligence

ZipEntry – represents a single element (entry) in a zip file

java.util.zip (compressed I/O)

35

Writing to a stream of GZipOutStream type. Which means it’ll be compressed on-the-fly.

Compression example (using gzip format)

36

Used to read the entries in a zip file. In order to create or to read these entries,

you have to pass a file as argument to the constructor:ZipFile zipfile = new ZipFile(new File("c:\\test.zip"));

This instantiation can throw a ZipException, as well as IOException.

One of the solutions is to surround it in a try/catch:

try { zipfile = new ZipFile(new File("c:\\test.zip"));} catch (ZipException e) { } catch (IOException e) { }

Now, files in a zip can be e.g. listed.

ZipFile class

37

Listing a zip file

38

Representation of a file or directory path name.This class contains several methods for

working with the path name, deleting and renaming files, creating new directories, listing the contents of a directory, etc.

File f1 = new File("/usr/local/bin/dog");File f2 = new File("d:/my_pets/cat1.txt");File f3 = new File("cat.txt");

File g = new File("/windows/system");File h = new File(g, "rabbit.gif");File i = new File("/windows/system", "rabbit.gif");// objects h and i refer to the same file!

File class

39

Selected methods (1/2)

boolean canRead() // Returns true if the file is readableboolean exists() // Returns true if the file existsboolean isDirectory() // Returns true if the file name is a directoryboolean isFile() // Returns true if the file name

// is a "normal" file (depends on OS)long length() // Returns the file lengthboolean setReadOnly() // (since 1.2) Marks the file read-only

// (returns true if succeeded)boolean delete() // Deletes the file specified by this name.boolean mkdir() // Creates this directory.

// All parent directories must already exist.

File class

FileFilter: public interface with only one method:boolean accept(File pathname);

// tests if the specified pathname should be included in a pathname list FileNameFilter: similar interface

but the arg list for accept() different

Beware: JFileChooser (Swing) makes use of javax.swing.filechooser.FileFilter, not java.io.FileFilter! 40

Selected methods (2/2)

String[] list() // Returns an array of Strings with names of the // files from this directory. Returns null if not a dir.

String[] list(FileNameFilter filter)

File[] listFiles()File[] listFiles(FileFilter)File[] listFiles(FileNameFilter) // (all three since 1.2)

File class

Recommended