Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I choose which of the main Java IO classes I need to write strings to a file?

Tags:

java

file

io

There are many different classes in the IO package, some seems to only have suble differences. If you are to write some Strings to a text file, and read them for later use. Which classes would you use for each of those two purposes and why?

  • BufferedInputStream
  • BufferedOutputStream
  • BufferedWriter
  • BufferedReader
  • DataInputStream
  • DataOutputStream
  • FileInputStream
  • FileOutputStream
  • FileReader
  • FileWriter
  • InputStreamReader
  • OutputStreamReader
  • Reader
  • Writer
like image 225
Jonas Grønbek Avatar asked Jan 03 '23 02:01

Jonas Grønbek


2 Answers

It all depends on what you want to do.

Output Streams

In case you want to write byte data, you would use some subclass of OutputStream that is used to write bytes in destination defined by concrete subclass.

For example FileOutputStream is used to write data in file byte by byte (or bunch of bytes).

BufferedOutputStream is an optimized extension of FileOutputStream to write blocks of bytes to minimize number of disk operations. So when you call write, actually it may or may not be written in file - depends on buffer's state whether it is full or not. If buffer reaches it's max capacity, all bytes are flushed to file at once.

ByteArrayOutputStream is using just block of memory as a destination instead of file. Current stream content is obtained via toByteArray() method (why would you need such a stream will be explained later).

ObjectOutputStream is used to write objects (class instances) in some destination defined by the underlying OutputStream. For example, ObjectOutputStream(FileOutputStream) would write an objects in file, while ObjectOutputStream(ByteArrayOutputStream) would write an objects in memory. The last option allows you to serialize objects in byte buffer (array of bytes) which can be then send somewhere via network.

Note, that any object you want to write somewhere via ObjectOutputStream has to implement Serializable interface. Since object may contain references to another objects, all the objects that are accessible from the object to be serialized also has to implement Serializable interface as serialization, by default, is process of writting of full graph of objects - assuming you are not using transient keyword to exclude class field from serialization, or you are not defining at object's class a special methods writeObject/readObject that overrides default serialization behaviour. These methods are designed to implement custom serialization i. e. you can define on your own how to write/read class field in/from ObjectOutputStream/ObjectInputStream. Suppose class A for which you are implementing custom serialization contains an object of class B. Instead of writting object b as a full graph, i. e. instead of calling oos.writeObject(b), where oos is an argument of the writeObject method being implemented in class A, you would write in stream only some fields of class B e. g. oos.writeBoolean(b.isEnabled), oos.writeInt(b.value). As long as object b is not written via oos.writeObject(b), it does not have to implement Serializable interface. For more details see Serializable documentation.

DataOutputStream is used to write primitives like boolean, char, int, long, float, double. Since any object can be decomposed to primitives e. g. class A { int i = 0; B b = new B();}, class B {double d = 0.0;} can be written simply as int a, double d, you can use DataOutputStream to serialize objects in compressed way unlike serialization which writes much more data e. g. class path, class version, ... so ClassLoader can identify a given class in runtime, instantiate this class and finally load data to this empty object. Note that instantiating class by it's name + initialising object in this way is much slower than instantiating class on your own and initializing it's fields on your own from "raw" DataOutputStream (that's why OS Android prefers custom Parcelable interface over standard serialization, which works as writting/reading to/from DataOutputStream/DataInputStream).

In case you want to write string data, you would use OutputStreamWriter a higher level of output-stream which writes characters/Strings in destination defined by OutputStream (in case of writting in file, you would pass FileOutputStream, in case of writtin in memory you would pass ByteArrayOutputStream).

FileWriter is an extension of OutputStreamWriter designed for writting in file.

BufferedWriter works the same as BufferedOutputStream except it is designed for work with strings and characters.

CharArrayWriter works the same as ByteArrayOutputStream except characters are stored in char[] which is obtained by calling getCharArray().

StringWriter is similar to CharArrayWriter except it allows you to write strings which are stored in StringBuffer that is obtained by calling getBuffer().

PrintWriter allows you to write formatted strings in specified destination defined by OutputStream (this writer is designed for easy logging).

Input Streams

The same concept is applied to input-streams (readers) associated with given output-streams (writers) mentioned above.

like image 119
matoni Avatar answered Jan 05 '23 14:01

matoni


  • Readers and InputStreams are for input while Writers and OutputStreams are for output.
  • Readers and Writers are for text (with a character set) while InputStreams and OutputStreams are for binary data (such as images).
  • Buffering helps with performance because reads and writes are batched rather than making a system call each time any method is used.
  • If it has "File" in the name, it is used for files.

So for your specific case you want to write text to a file, the most obvious candidate is a FileWriter:

Writer out = new FileWriter(
    new File("foo.txt")
);

If you want better performance for making many small writes to it, you could wrap it in a buffer:

Writer out = new BufferedWriter(
    new FileWriter(
        new File("foo.txt")
    )
);

The FileWriter assumes the default character encoding when writing text. If you want to choose the encoding, or ensure that it is consistent on all platforms, you could create the writer from an OutputStream specifying the encoding explicitly:

Writer out = new BufferedWriter(
    new OutputStreamWriter(
        new FileOutputStream(
            new File("foo.txt")
        ), "UTF-8"
    )
);
like image 36
Stephen Ostermiller Avatar answered Jan 05 '23 16:01

Stephen Ostermiller