Input and Output in Java Byte- and Character-based I/O File Objects jonas.kvarnstrom@liu.se – 2015 Two forms of file access: 2 jonkv@ida Introduction A stream: A sequence of elements Made available one at a time Sequential access Used in most of Java's I/O classes 3 jonkv@ida Streams 1: The Concept 4 Reading “raw” bytes abstract class InputStream Read a single byte or an array of bytes InputStream is; Reading from a file is = new FileInputStream("java.dat"); Reading from an array in memory is = new ByteArrayInputStream(…); Reading from a network socket is = socket.getInputStream(); … Interfaces would have been better – why? Writing “raw” bytes abstract class OutputStream Write a single byte or an array of bytes OutputStream is; Writing to a file is = new FileOutputStream("java.dat"); This is how you create an empty file! Writing to an array in memory is = new ByteArrayOutputStream(…); Writing to a network socket is = socket.getOutputStream(); … One class for each source / destination jonkv@ida Streams 2: Binary I/O A simple OutputStream example: 5 Declare an AutoCloseable resource in try()… (Java 7+) ▪ public static void main(String[] args) { try (OutputStream os = new FileOutputStream("java.dat")) { This is os.write(3); Use it inside the block… basically all os.write(new byte[] { 6, 7, 8 }); you can do! … … and when you exit the block, } catch (IOException e) { the resource (file) will … handle I/O errors that may arise automatically be closed, even if there is an exception! when opening or using the stream… } ▪ try ( InputStream is = new FileInputStream("foo.dat"); OutputStream os = new FileOutputStream("java.dat")) { Using two resources… … } catch (IOException e) { Error handling is important! … handle it … Always make sure that every } file is closed! } jonkv@ida Streams 3: Example 6 – Very little basic functionality… for a reason! Division of responsibilities ”Basic” streams handle sources and destinations Filter streams provide additional functionality ▪ Add buffering ▪ Support other types of data ▪ … Your code Filter Stream Filter Stream Basic Stream jonkv@ida Stream Filters 1: Introduction 7 A simple filter stream example: ▪ public class MyOutputStream { private OutputStream out; Your code public MyOutputStream(final OutputStream out) { this.out = out; } MyOutputStream ▪ public void write(byte b) { out.write(b); } public void write(byte[] b) { out.write(b); } … FileOutputStream ▪ public void writeShort(short s) { out.write((s & 0xFF00) >> 8); …or any other out.write(s & 0xFF); ”destination } stream” … } ▪ try (MyOutputStream out = new MyOutputStream( new FileOutputStream("foo.dat"))) { … } jonkv@ida Stream Filters 2: How do they Work? 8 Useful output filters BufferedOutputStream Buffers I/O: Don't write a single byte at a time… PrintStream (System.out / err) print(), println() methods for primitive datatypes and Strings Uses platform character encoding to convert chars bytes DataOutputStream writeLong(), writeFloat(), … writeUTF() – writes UTF-8 format try (DataOutputStream os = new DataOutputStream( new BufferedOutputStream( new FileOutputStream("java.dat")))) { os.write(new byte[] { 6, 7, 8 }); os.writeLong(1234567890123L); os.writeFloat(2.7f); } catch (final IOException e) { … handle it … } jonkv@ida Stream Filters 3: Output Useful input filters BufferedInputStream Buffers I/O: Don't read a single byte at a time… DataInputStream readLong(), readFloat(), … readUTF() – reads UTF-8 format readLine() – deprecated! Ignores character encodings Use Readers instead! 9 jonkv@ida Stream Filters 4: Input 11 Distinct subsystem for text-based I/O: Reader, Writer Sends full 16-bit Unicode text, not 8-bit bytes Your code Unicode Unicode text Filter Writer Filter Reader Unicode Unicode Filter Writer Filter Reader Unicode Unicode OutputStreamWriter 8-bit bytes Your code UTF-8: “ä” c3 a4 ISO Latin-1: “ä” e4 OutputStream InputStreamReader 8-bit bytes UTF-8: c3 a4 “ä” ISO Latin-1: c3 a4 “ä” InputStream jonkv@ida Readers and Writers 2 12 Reading text Writing text abstract class Reader abstract class Writer Read a single char or an array of chars Reader re; Reading from a file re = new FileReader("java.dat"); Reading from an array in memory Read a single byte or an array of bytes Writer wr; Writing to a file wr = new FileWriter("java.dat"); Writing to an array in memory re = new CharArrayReader(…); wr = new CharArrayWriter(…); re = new StringReader(…); wr = new StringWriter(…); (writes to a StringBuffer) … … No encoding specified: Use the platform’s default to convert bytes chars So don’t use this for files that should be machine-readable on multiple systems! jonkv@ida Readers and Writers 3 13 Useful filters Text output BufferedWriter Buffers I/O: Don't write a single char at a time… PrintWriter print(), println() methods for primitive datatypes and Strings Text input BufferedReader Buffers I/O: Don't read a single char at a time… Method for reading a line jonkv@ida Readers and Writers 4: Filters 14 Specify a character encoding using OSW and ISR OutputStreamWriter OutputStream os = new FileOutputStream("file.txt"); Writer wr = new OutputStreamWriter(os, “Big5"); Your code Unicode chars OutputStreamWriter Bytes in Big5 encoding (Traditional Chinese) FileOutputStream InputStreamReader Socket sock = …; InputStream is = sock.getInputStream(); Reader re = new InputStreamReader(is, “Cp1046”); Your code Unicode chars InputStreamReader Bytes in Cp1046 encoding (IBM arabic) ”SocketInputStream” UTF-8 all characters supported, readable on most systems! jonkv@ida R/W 5: Specifying Encodings File Objects (java.io.File) represent file and path names They do not represent open files! Also file system operations: ▪ File f = new File("/"); Find available space, total space, File f2 = new File(f, "etc"); file system roots (C:\, D:\), File f3 = new File(f, "passwd"); System.out.print(f3.getAbsolutePath()); if (f3.exists()) { System.out.println("You have a password file"); System.out.println("It's in the directory " + f3.getParent()); System.out.println("Its length is " + f3.length()); if (f3.canRead()) System.out.println("I can read it"); if (f3.canWrite()) System.out.println("I can write to it"); if (f3.isHidden()) System.out.println("It is hidden"); if (f3.delete()) System.out.println("I have deleted it!"); try (OutputStream os = new FileOutputStream(f3)) { … }; } File.createNewFile() is used for atomic locking – just use a FileOutputStream in most cases! Also directory operations: listFiles(), mkdir(), renameTo(), … 16 jonkv@ida File[name] Objects: The File Class Object-based I/O: Serialization jonas.kvarnstrom@liu.se – 2015 18 Serialization: Convert objects to/from sequences of bytes ObjectOutputStream ObjectInputStream OutputStream os = new FileOutputStream("file.dat"); ObjectOutputStream out = new ObjectOutputStream(os); Writes an out.writeObject(gameBoard); out.writeObject(highscoreList); object and everything it out.close(); refers to! Your code Objects ObjectOutputStream Bytes: All you need to reconstruct the objects FileOutputStream Socket sock = …; InputStream is = sock.getInputStream(); ObjectInputStream in = new ObjectInputStream(is); List<Score> highscoreList = (List<Score>) in.readObject(); in.close(); Your code Objects ObjectInputStream Bytes ”SocketInputStream” jonkv@ida Serialization 1: Intro 19 The objects must implement java.io.Serializable An interface without methods, indicating that serialization is allowed ▪ By accessing the byte stream you can read private fields! ▪ public class Pair implements Serializable { private Object first; All fields must also be Serializable! private Object second; private transient int hashCodeCache; …except transient fields, which Pair(Object first, Object second) { this.first = first; this.second = second; } we assume can be reconstructed or are unnecessary for other reasons } The superclass must: ▪ Be Serializable, so we can save its data to the stream, or ▪ Have a no-args constructor, so we can reconstruct it from scratch Many Java classes already implement Serializable ▪ Strings, Collections subclasses, … jonkv@ida Serialization 2: Serializable Interface ObjectOutputStream must handle circular references Node structure example: ▪ Node parent = new Node(null); Node child = new Node(parent); // child points to parent parent.addChild(node); // parent points to child Remembers which objects were written ▪ First time: Write object ID + entire object representation ▪ Second time: Write object ID Does not care whether the object was updated! ▪ List list = new ArrayList(); oos.write(list); // Writes object ID + entire list list.add("Another element"); oos.write(list); // Writes the object ID… To write a new copy of the object: Use reset() ▪ oos.reset(); 20 jonkv@ida Serialization 3: Writing An Object Twice Can old saved objects be read after changing the class? Some changes are allowed ▪ Adding fields – if you read an old object, the field will be set to 0/null ▪ Changing public/protected/private ▪ A few more types of changes Others are forbidden ▪ Changing the class hierarchy in certain ways ▪ Removing Serializable ▪ … You must have the same serial version ID ▪ By default this is a hash of certain features in the class — too strict! ▪ To allow adding new fields, declare your own version ID: private final static long serialVersionUID = 1; // for example ▪ IMPORTANT! Change this if you make incompatible changes to your class! 21 jonkv@ida Serialization 4: Class Versions 22 Error handling was omitted ▪ ▪ ▪ ▪ ▪ ▪ ClassNotFoundException InvalidClassException StreamCorruptedException OptionalDataException NotSerializableException IOException – received an object of a non-existing class – bad control information in the stream – primitive data found instead of objects – an object was not Serializable – the usual Input/Output related exceptions Many more serialization features… ▪ http://docs.oracle.com/javase/7/docs/technotes/guides/serialization/index.html jonkv@ida Serialization 5: Exceptions 24 How does your program find its image files, audio files, etc? Your software may be installed in different locations Path syntax depends on the OS Your program, including images, can be packed inside a single JAR file ▪ Java ARchive, essentially a ZIP file Resources reuse the Java class loading mechanism Place files together with your code Use resources to find and load them Everything in a single JAR file: mypackage/Main.class mypackage/ShowImage.class … data/bin.dat img/test.png jonkv@ida Resources 1: Data in the ClassPath 25 Since the class loading mechanism is used: Must first have a ClassLoader or a java.lang.Class object ▪ Simplest way: SomeClassInYourProgram.class Not a file name: ”.class” is magic syntax Then, getResource() returns a java.net.URL ▪ URL url = Main.class.getResource("img/test.png"); Finally: ▪ Use the URL's getContent() method to return: ▪ an ImageProducer, for images mypackage/Main.class ▪ an AudioClip, for audio mypackage/ShowImage.class … ▪ an InputStream, otherwise data/bin.dat ▪ Or construct an ImageIcon from the URL! img/test.png ▪ It calls getContent() for you ▪ ImageIcon icon = new ImageIcon(url); jonkv@ida Resources 2: Example Most IDEs have a setting determining which files are resources You place them in the source folders Compilation copies them to output folders or JAR files 26 jonkv@ida Resources 3: IDEs