Java Programming Tutorial: Basic Input & Output (I/O)
Java Programming Tutorial: Basic Input & Output (I/O)
Java Programming Tutorial: Basic Input & Output (I/O)
| HOME
Interface java.nio.file.Path
Helper class java.nio.file.Paths
Helper Class java.nio.file.Files
File Attributes
FileChannel
Random Access File
Directory Operations
Walking the File Tree - Files.walkFileTree()
Watching the Directory for Changes - Interface java.nio.file.WatchService
introduces
more
A path string is used to locate a file or a directory. Unfortunately, path strings are system dependent,
e.g., "c:\myproject\java\Hello.java" in Windows or "/myproject/java/Hello.java" in Unix/Mac.
Windows use back-slash '\' as the directory separator; while Unixes/Mac use forward-slash '/'.
Windows use semi-colon ';' as path separator to separate a list of paths; while Unixes/Mac use
colon ':'.
Windows use "\r\n" as line delimiter for text file; while Unixes use "\n" and Mac uses "\r".
The "c:\" or "\" is called the root. Windows supports multiple roots, each maps to a drive (e.g.,
"c:\", "d:\"). Unixes/Mac has a single root ("\").
A path could be absolute (beginning from the root) or relative (which is relative to a reference directory).
Special notations "." and ".." denote the current directory and the parent directory, respectively.
The java.io.File class maintains these system-dependent properties, for you to write programs that are
portable:
Directory
Separator:
and File.separatorChar.
[They failed to follow the Java naming convention for constants adopted since JDK 1.2.] As
mentioned, Windows use backslash '\'; while Unixes/Mac use forward slash '/'.
Path
Separator:
in static fields File.pathSeparator (as String)
and File.pathSeparatorChar. As mentioned, Windows use semi-colon ';' to separate a list of
paths; while Unixes/Mac use colon ':'.
You can construct a File instance with a path string or URI, as follows. Take note that the physical
file/directory
may
or
may
not
exist.
A
file
URL
takes
the
form
of file://...,
e.g., file:///d:/docs/programming/java/test.html.
public File(String pathString)
public File(String parent, String child)
public File(File parent, String child)
// Constructs a File instance based on the given path string.
public File(URI uri)
// Constructs a File instance by converting from the given file-URI "file://...."
For examples,
File file = new File("in.txt");
// A file relative to the current working directory
File file = new File("d:\\myproject\\java\\Hello.java"); // A file with absolute path
File dir = new File("c:\\temp"); // A directory
For applications that you intend to distribute as JAR files, you should use the URL class to reference the
resources, as it can reference disk files as well as JAR'ed files , for example,
java.net.URL url = this.getClass().getResource("icon.png");
boolean exists()
// Tests if this file/directory exists.
long length()
// Returns the length of this file.
boolean isDirectory() // Tests if this instance is a directory.
boolean isFile()
// Tests if this instance is a file.
boolean canRead()
// Tests if this file is readable.
boolean canWrite()
// Tests if this file is writable.
boolean delete()
// Deletes this file/directory.
void deleteOnExit() // Deletes this file/directory when the program terminates.
boolean renameTo(File dest) // Renames this file.
boolean mkdir()
// Makes (Creates) this directory.
List Directory
For a directory, you can use the following methods to list its contents:
public String[] list()
Example: The following program recursively lists the contents of a given directory (similar to Unix's "ls
-r" command).
// Recursively list the contents of a directory (Unix's "ls -r" command).
import java.io.File;
public class ListDirectoryRecusive {
public static void main(String[] args) {
File dir = new File("d:\\myproject\\test"); // Escape sequence needed for '\'
listRecursive(dir);
}
public static void listRecursive(File dir) {
if (dir.isDirectory()) {
File[] items = dir.listFiles();
for (File item : items) {
System.out.println(item.getAbsoluteFile());
if (item.isDirectory()) listRecursive(item); // Recursive call
}
}
}
}
The list() and listFiles() methods does a call-back to accept() for each of the file/sub-directory
produced. You can program your filtering criteria in accept(). Those files/sub-directories that result in
a false return will be excluded.
Example: The following program lists only files that meet a certain filtering criteria.
// List files that end with ".java"
import java.io.File;
import java.io.FilenameFilter;
public class ListDirectoryWithFilter {
public static void main(String[] args) {
File dir = new File("."); // current working directory
if (dir.isDirectory()) {
// List only files that meet the filtering criteria
// programmed in accept() method of FilenameFilter.
String[] files = dir.list(new FilenameFilter() {
public boolean accept(File dir, String file) {
return file.endsWith(".java");
}
}); // an anonymous inner class as FilenameFilter
for (String file : files) {
System.out.println(file);
}
}
}
}
program). In Java standard I/O, inputs and outputs are handled by the so-called streams. A stream is a
sequential and contiguous one-way flow of data (just like water or oil flows through the pipe). It is
important to mention that Java does not differentiate between the various types of data sources or sinks
(e.g., file or network) in stream I/O. They are all treated as a sequential flow of data. Input and output
streams can be established from/to any data source/sink, such as files, network, keyboard/console or
another program. The Java program receives data from a source by opening an input stream, and sends
data to a sink by opening an output stream. All Java I/O streams are one-way (except
the RandomAccessFile, which will be discussed later). If your program needs to perform both input
and output, you have to open two streams - an input stream and an output stream.
Stream I/O operations involve three steps:
1.
Open an input/output stream associated with a physical device (e.g., file, network,
console/keyboard), by constructing an appropriate I/O stream instance.
2.
Read from the opened input stream until "end-of-stream" encountered, or write to the opened
output stream (and optionally flush the buffered output).
3.
Close the input/output stream.
Java's I/O operations is more complicated than C/C++ to support internationalization (i18n). Java
internally stores characters (char type) in 16-bit UCS-2 character set. But the external data source/sink
could store characters in other character set (e.g., US-ASCII, ISO-8859-x, UTF-8, UTF-16, and many
others), in fixed length of 8-bit or 16-bit, or in variable length of 1 to 4 bytes. [Read "Character Sets and
Encoding Schemes"]. As a consequence, Java needs to differentiate between byte-based I/O for
processing raw bytes or binary data, and character-based I/O for processing texts made up of characters.
Byte streams are used to read/write raw bytes serially from/to an external device. All the byte streams
are derived from the abstract superclasses InputStream and OutputStream, as illustrated in the
class diagram.
The read() method returns an int instead of a byte, because it uses -1 to indicate end-of-stream.
The read() method blocks until a byte is available, an I/O error occurs, or the "end-of-stream" is
detected. The term "block" means that the method (and the program) will be suspended. The program
will resume only when the method returns.
Two variations of read() methods are implemented in the InputStream for reading a block of bytes into
a byte-array. It returns the number of bytes read, or -1 if "end-of-stream" encounters.
public int read(byte[] bytes, int offset, int length) throws IOException
// Read "length" number of bytes, store in bytes array starting from offset of index.
public int read(byte[] bytes) throws IOException
// Same as read(bytes, 0, bytes.length)
Similar to the read(), two variations of the write() method to write a block of bytes from a byte-array
are implemented:
public void write(byte[] bytes, int offset, int length) throws IOException
// Write "length" number of bytes, from the bytes array starting from offset of index.
public void write(byte[] bytes) throws IOException
// Same as write(bytes, 0, bytes.length)
It is a good practice to explicitly close the I/O stream, by running close() in the finally clause of trycatch-finally to free up the system resources immediately when the stream is no longer needed. This
could prevent serious resource leaks. Unfortunately, the close() method also throws a IOException,
and needs to be enclosed in a nested try-catch statement, as follows. This makes the codes somehow
ugly.
FileInputStream in = null;
......
try {
in = new FileInputStream(...); // Open stream
......
......
} catch (IOException ex) {
ex.printStackTrace();
} finally { // always close the I/O streams
try {
if (in != null) in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
JDK 1.7 introduces a new try-with-resources syntax, which automatically closes all the opened resources
after try or catch, as follows. This produces much neater codes.
try (FileInputStream in = new FileInputStream(...)) {
......
......
} catch (IOException ex) {
ex.printStackTrace();
} // Automatically closes all opened resource in try (...).
to
This example copies a file by reading a byte from the input file and writing it to the output file. It
uses FileInputStream and FileOutputStream directly without buffering. Notice that most the I/O
methods "throws" IOException, which must be caught or declared to be thrown. The method close() is
programmed inside the finally clause. It is guaranteed to be run after try or catch. However,
method close() also throws an IOException, and therefore must be enclosed inside a nested trycatch block, which makes the codes a little ugly.
I used System.nanoTime(), which was introduced in JDK 1.5, for a more accurate measure of the
elapsed time, instead of the legacy not-so-precise System.currentTimeMillis(). The output shows that
it took about 4 seconds to copy a 400KB file.
As mentioned, JDK 1.7 introduces a new try-with-resources syntax, which automatically closes all the
resources opened, after try or catch. For example, the above example can be re-written in a much
neater manner as follow:
import java.io.*;
public class FileCopyNoBufferJDK7 {
public static void main(String[] args) {
String inFileStr = "test-in.jpg";
String outFileStr = "test-out.jpg";
long startTime, elapsedTime; // for speed benchmarking
// Check file length
File fileIn = new File(inFileStr);
System.out.println("File size is " + fileIn.length() + " bytes");
// "try-with-resources" automatically closes all opened resources.
try (FileInputStream in = new FileInputStream(inFileStr);
FileOutputStream out = new FileOutputStream(outFileStr)) {
startTime = System.nanoTime();
int byteRead;
// Read a raw byte, returns an int of 0 to 255.
while ((byteRead = in.read()) != -1) {
// Write the least-significant byte of int, drop the upper 3 bytes
out.write(byteRead);
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
This example again uses FileInputStream and FileOutputStream directly. However, instead of
reading/writing one byte at a time, it reads/writes a 4KB block. This program took only 3 millisecond - a
more than 1000 times speed-up compared with the previous example.
Larger buffer size, up to a certain limit, generally improves the I/O performance. However, there is a
trade-off between speed-up the the memory usage. For file copying, a large buffer is certainly
recommended. But for reading just a few bytes from a file, large buffer simply wastes the memory.
I re-write the program using JDK 1.7, and try on various buffer size on a much bigger file of 26MB.
import java.io.*;
public class FileCopyUserBufferLoopJDK7 {
public static void main(String[] args) {
String inFileStr = "test-in.jpg";
String outFileStr = "test-out.jpg";
long startTime, elapsedTime; // for speed benchmarking
// Check file length
File fileIn = new File(inFileStr);
System.out.println("File size is " + fileIn.length() + " bytes");
int[] bufSizeKB = {1, 2, 4, 8, 16, 32, 64, 256, 1024}; // in KB
int bufSize; // in bytes
for (int run = 0; run < bufSizeKB.length; ++run) {
bufSize = bufSizeKB[run] * 1024;
try (FileInputStream in = new FileInputStream(inFileStr);
FileOutputStream out = new FileOutputStream(outFileStr)) {
startTime = System.nanoTime();
byte[] byteBuf = new byte[bufSize];
int numBytesRead;
while ((numBytesRead = in.read(byteBuf)) != -1) {
out.write(byteBuf, 0, numBytesRead);
}
elapsedTime = System.nanoTime() - startTime;
System.out.printf("%4dKB: %6.2fmsec%n", bufSizeKB[run], (elapsedTime / 1000000.0));
//System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
File size is 26246026 bytes [26 MB]
1KB: 573.54msec
2KB: 316.43msec
4KB: 178.47msec
8KB: 116.32msec
16KB: 85.61msec
32KB: 65.92msec
64KB: 57.81msec
256KB: 63.38msec
1024KB: 98.87msec
In
this
example,
I
chain
the FileInputStream with BufferedInputStream, FileOutputStream with BufferedOutputStrea
m, and read/write byte-by-byte. The JRE decides on the buffer size. The program took 62 milliseconds,
about 60 times speed-up compared with example 1, but slower than the programmer-managed buffer.
The JDK 1.7 version of the above example is as follows:
import java.io.*;
public class FileCopyBufferedStreamJDK7 {
public static void main(String[] args) {
String inFileStr = "test-in.jpg";
String outFileStr = "test-out.jpg";
long startTime, elapsedTime; // for speed benchmarking
// Check file length
File fileIn = new File(inFileStr);
System.out.println("File size is " + fileIn.length() + " bytes");
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(inFileStr));
BufferedOutputStream
out
=
new
BufferedOutputStream(new
FileOutputStream(outFileStr))) {
startTime = System.nanoTime();
int byteRead;
while ((byteRead = in.read()) != -1) {
out.write(byteRead);
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
final int readUnsignedByte() throws IOExcpetion; // Read 1 byte in [0, 255] upcast to int
final int readUnsignedShort() throws IOExcpetion; // Read 2 bytes in [0, 65535], same as char, upcast
final void readFully(byte[] b, int off, int len) throws IOException;
final void readFully(byte[] b) throws IOException;
// Strings
public final String readLine() throws IOException;
// Read a line (until newline), convert each byte into a char - no unicode support.
public final String readUTF() throws IOException;
// read a UTF-encoded string with first two bytes indicating its UTF bytes length
public final int skipBytes(int n) // Skip a number of bytes
Example: The following program writes some primitives to a disk file. It then reads the raw bytes to
check how the primitives were stored. Finally, it reads the data as primitives.
import java.io.*;
public class TestDataIOStream {
public static void main(String[] args) {
String filename = "data-out.dat";
String message = "Hi,!";
System.out.print(in.readChar());
}
System.out.println();
System.out.print("chars: ");
for (int i = 0; i < message.length(); ++i) {
System.out.print(in.readChar());
}
System.out.println();
System.out.print("bytes: ");
for (int i = 0; i < message.length(); ++i) {
System.out.print((char)in.readByte());
}
System.out.println();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
7F FF FF 00 00 AB CD 00 00 00 00 00 0F 42 3F
byte short int
long
41 33 85 1F 40 4B D4 7A E1 47 AE 14
float
double
01
00
boolean boolean
00 48 00 69 00 2C 60 A8 59 7D 00 21
H
i
,
!
00 48 00 69 00 2C 60 A8 59 7D 00 21
H
i
,
!
48 69 2C A8 7D 21
[low byte of the char only]
byte: 127
short: -1
int:
43981
long: 305419896
float: 11.22
double: 55.66
boolean: true
boolean: false
char: Hi,!
chars: Hi,!
bytes: Hi,?}!
The data stored in the disk are exactly in the same form as in the Java program internally (e.g., UCS-2 for
characters). The byte-order is big-endian (big byte first, or most significant byte in lowest address).
Java internally stores characters (char type) in 16-bit UCS-2 character set. But the external data
source/sink could store characters in other character set (e.g., US-ASCII, ISO-8859-x, UTF-8, UTF-16, and
many others), in fixed length of 8-bit or 16-bit, or in variable length of 1 to 4 bytes. [Read "Character Sets
and Encoding Schemes"]. Hence, Java has to differentiate between byte-based I/O for processing 8-bit
raw bytes, and character-based I/O for processing texts. The character streams needs
to translate between the character set used by external I/O devices and Java internal UCS-2 format. For
example, the character '' is stored as "60 A8" in UCS-2 (Java internal), " E6 82 A8" in UTF8, "C4 FA"
in GBK/GB2312, and "B1 7A" in BIG5. If this character is to be written to a file uses UTF-8, the character
stream needs to translate "60 A8" to "E6 82 A8". The reserve takes place in a reading operation.
The byte/character streams refer to the unit of operation within the Java programs, which does not
necessary correspond to the amount of data transferred from/to the external I/O devices. This is because
some charsets use fixed-length of 8-bit (e.g., US-ASCII, ISO-8859-1) or 16-bit (e.g., UCS-16), while some
use variable-length of 1-4 bytes (e.g., UTF-8, UTF-16, UTF-16-BE, UTF-16-LE, GBK, BIG5). When a
character stream is used to read an 8-bit ASCII file, an 8-bit data is read from the file and put into the 16bit char location of the Java program.
Other than the unit of operation and charset conversion (which is extremely complex), character-based
I/O is almost identical to byte-based I/O. Instead of InputStream and OutputStream, we
use Readerand Writer for character-based I/O.
The abstract superclass Reader operates on char. It declares an abstract method read() to read one
character from the input source. read() returns the character as an int between 0 to 65535 (a charin
Java can be treated as an unsigned 16-bit integer); or -1 if end-of-stream is detected; or throws
an IOException if I/O error occurs. There are also two variations of read() to read a block of characters
into char-array.
public abstract int read() throws IOException
public int read(char[] chars, int offset, int length) throws IOException
public int read(char[] chars) throws IOException
The abstract superclass Writer declares an abstract method write(), which writes a character to the
output sink. The lower 2 bytes of the int argument is written out; while the upper 2 bytes are discarded.
public void abstract void write(int aChar) throws IOException
public void write(char[] chars, int offset, int length) throws IOException
import java.io.*;
// Write a text message to an output file, then read it back.
// FileReader/FileWriter uses the default charset for file encoding.
public class BufferedFileReaderWriterJDK7 {
public static void main(String[] args) {
String strFilename = "out.txt";
String message = "Hello, world!\nHello, world again!\n"; // 2 lines of texts
// Print the default charset
System.out.println(java.nio.charset.Charset.defaultCharset());
try (BufferedWriter out = new BufferedWriter(new FileWriter(strFilename))) {
out.write(message);
out.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
try (BufferedReader in = new BufferedReader(new FileReader(strFilename))) {
String inLine;
while ((inLine = in.readLine()) != null) { // exclude newline
System.out.println(inLine);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Example
import java.nio.charset.Charset;
public class TestCharset {
public static void main(String[] args) {
// Print the default Charset
System.out.println("The default charset is " + Charset.defaultCharset());
System.out.println("The default charset is " + System.getProperty("file.encoding"));
// Print the list of available Charsets in name=Charset
System.out.println("The available charsets are:");
System.out.println(Charset.availableCharsets());
// Check if the given charset name is supported
System.out.println(Charset.isSupported("UTF-8")); // true
System.out.println(Charset.isSupported("UTF8")); // true
System.out.println(Charset.isSupported("UTF_8")); // false
// Get an instance of a Charset
Charset charset = Charset.forName("UTF8");
// Print this Charset name
System.out.println(charset.name());
// "UTF-8"
// Print all the other aliases
System.out.println(charset.aliases()); // [UTF8, unicolor-1-1-utf-8]
}
}
The default charset for file encoding is kept in the system property " file.encoding". To change the JVM's
default charset for file encoding, you can use command-line VM option " -Dfile.encoding". For example,
the following command run the program with default charset of UTF-8.
> java -Dfile.encoding=UTF-8 TestCharset
Most importantly, the Charset class provides methods to encode/decode characters from UCS-2 used in
Java program and the specific charset used in the external devices (such as UTF-8).
public final ByteBuffer encode(String s)
public final ByteBuffer encode(CharBuffer cb)
// Encodes Unicode UCS-2 characters in the CharBuffer/String
// into a "byte sequence" using this charset, and returns a ByteBuffer.
public final CharBuffer decode(ByteBuffer bb)
// Decode the byte sequence encoded using this charset in the ByteBuffer
// to Unicode UCS-2, and return a charBuffer.
The encode()/decode() methods operate on ByteBuffer and CharBuffer introduced also in JDK 1.4,
which will be explain in the New I/O section.
Example: The following example encodes some Unicode texts in various encoding scheme, and
display the Hex codes of the encoded byte sequences.
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
!
UTF-16: FE FF 00 48 00 69 00 2C 60 A8 59 7D 00 21 [2-4 bytes variable-length]
BOM H
i
,
!
[Byte-Order-Mark indicates Big-Endian]
UTF-16BE: 00 48 00 69 00 2C 60 A8 59 7D 00 21 [2-4 bytes variable-length]
H
i
,
!
UTF-16LE: 48 00 69 00 2C 00 A8 60 7D 59 21 00 [2-4 bytes variable-length]
H
i
,
!
GBK: 48 69 2C C4 FA BA C3 21 [1-2 bytes variable-length]
H i , !
Big5: 48 69 2C B1 7A A6 6E 21 [1-2 bytes variable-length]
H i , !
Example: The following example tries out the encoding/decoding on CharBuffer and ByteBuffer.
Buffers will be discussed later under New I/O.
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
You
can
list
the
available
charsets
via static method java.nio.charset.Charset.availableCharsets().
The
commonlyused Charset names supported by Java are:
"ISO-8859-1": Latin-1
"UTF-16": with a 2-byte BOM (Byte-Order-Mark) to specify the byte order. FE FF indicates bigendian, FF FE indicates little-endian.
As the InputStreamReader/OutputStreamWriter often needs to read/write in multiple bytes, it is
best to wrap it with a BufferedReader/BufferedWriter.
Example: The following program writes Unicode texts to a disk file using various charsets for file
encoding. It then reads the file byte-by-byte (via a byte-based input stream) to check the encoded
characters in the various charsets. Finally, it reads the file using the character-based reader.
import java.io.*;
// Write texts to file using OutputStreamWriter specifying its charset encoding.
// Read byte-by-byte using FileInputStream.
// Read char-by-char using InputStreamReader specifying its charset encoding.
public class TextFileEncodingJDK7 {
public static void main(String[] args) {
String message = "Hi,!"; // with non-ASCII chars
// Java internally stores char in UCS-2/UTF-16
// Print the characters stored with Hex codes
for (int i = 0; i < message.length(); ++i) {
char aChar = message.charAt(i);
System.out.printf("[%d]'%c'(%04X) ", (i+1), aChar, (int)aChar);
}
System.out.println();
// Try these charsets for encoding text file
String[] csStrs = {"UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16", "GB2312", "GBK", "BIG5"};
String outFileExt = "-out.txt"; // Output filenames are "charset-out.txt"
// Write text file in the specified file encoding charset
for (int i = 0; i < csStrs.length; ++i) {
try (OutputStreamWriter out =
new OutputStreamWriter(
new FileOutputStream(csStrs[i] + outFileExt), csStrs[i]);
BufferedWriter bufOut = new BufferedWriter(out)) { // Buffered for efficiency
System.out.println(out.getEncoding()); // Print file encoding charset
bufOut.write(message);
bufOut.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
// Read raw bytes from various encoded files
// to check how the characters were encoded.
for (int i = 0; i < csStrs.length; ++i) {
try (BufferedInputStream in = new BufferedInputStream( // Buffered for efficiency
new FileInputStream(csStrs[i] + outFileExt))) {
System.out.printf("%10s", csStrs[i]); // Print file encoding charset
int inByte;
while ((inByte = in.read()) != -1) {
System.out.printf("%02X ", inByte); // Print Hex codes
}
System.out.println();
} catch (IOException ex) {
ex.printStackTrace();
}
}
// Read text file with character-stream specifying its encoding.
// The char will be translated from its file encoding charset to
// Java internal UCS-2.
for (int i = 0; i < csStrs.length; ++i) {
try (InputStreamReader in =
new InputStreamReader(
new FileInputStream(csStrs[i] + outFileExt), csStrs[i]);
BufferedReader bufIn = new BufferedReader(in)) { // Buffered for efficiency
System.out.println(in.getEncoding()); // print file encoding charset
int inChar;
int count = 0;
while ((inChar = in.read()) != -1) {
++count;
System.out.printf("[%d]'%c'(%04X) ", count, (char)inChar, inChar);
}
System.out.println();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
UTF-8: 48 69 2C E6 82 A8 E5 A5 BD 21
H i ,
!
UTF-16BE: 00 48 00 69 00 2C 60 A8 59 7D 00 21
H
i
,
!
UTF-16LE: 48 00 69 00 2C 00 A8 60 7D 59 21 00
H
i
,
!
UTF-16: FE FF 00 48 00 69 00 2C 60 A8 59 7D 00 21
BOM H
i
,
!
GB2312: 48 69 2C C4 FA BA C3 21
H i , !
GBK:
48 69 2C C4 FA BA C3 21
H i , !
BIG5:
48 69 2C B1 7A A6 6E 21
H i , !
UTF8
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
UnicodeBigUnmarked [UTF-16BE without BOM]
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
UnicodeLittleUnmarked [UFT-16LE without BOM]
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
UTF-16
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
EUC_CN [GB2312]
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
GBK
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
Big5
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
As seen from the output, the characters is encoded differently in different charsets. Nonetheless,
the InputStreamReader is able to translate the characters into the same UCS-2 used in Java program.
The
byte-based java.io.printSteam supports
convenient
printing
methods
such
as print() and println() for printing primitives and text string. Primitives are converted to their string
representation for printing. The printf() and format() were introduced in JDK 1.5 for formatting output
with former specifiers. printf() and format() are identical.
A PrintStream never throws an IOException. Instead, it sets an internal flag which can be checked via
the checkError() method. APrintStream can also be created to flush the output automatically. That is,
the flush() method is automatically invoked after a byte array is written, one of the println() methods is
invoked, or after a newline ('\n') is written.
The standard output and error streams (System.out and System.err) belong to PrintStream.
All characters printed by a PrintStream are converted into bytes using the default character encoding.
The PrintWriter class should be used in situations that require writing characters rather than bytes.
The character-stream PrintWriter is similar to PrintStream, except that it write in characters instead of
bytes.
The PrintWriter also
supports
all
the
convenient
printing
methods print(), println(), printf() and format(). It never throws an IOException and can optionally
be created to support automatic flushing.
[TODO] Example to show the difference between PrintStream and PrintWriter.
Data streams (DataInputStream and DataOutputStream) allow you to read and write primitive data
(such
as int, double)
and String,
rather
than
individual
bytes.
Object
streams
(ObjectInputStreamand ObjectOutputStream) go one step further to allow you to read and write
entire objects (such as Date, ArrayList or any custom objects).
Object serialization is the process of representing a "particular state of an object" in a serialized bitstream, so that the bit stream can be written out to an external device (such as a disk file or network).
The bit-stream can later be re-constructed to recover the state of that object. Object serialization is
necessary to save a state of an object into a disk file for persistence or sent the object across the
network for applications such as Web Services, Distributed-object applications, and Remote Method
Invocation (RMI).
In
Java,
object
that
requires
to
be
serialized
must
implement java.io.Serializable or java.io.Externalizable interface. Serializable interface
is
an empty interface (or tagged interface) with nothing declared. Its purpose is simply to declare that
particular object is serializable.
The ObjectInputStream and ObjectOutputStream can be used to serialize an object into a bitstream and transfer it to/from an I/O streams, via these methods:
public final Object readObject() throws IOException, ClassNotFoundException;
public final void writeObject(Object obj) throws IOException;
ObjectOutputStream out =
new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream("object.ser")));
out.writeObject("The current Date and Time is "); // write a String object
out.writeObject(new Date());
// write a Date object
out.flush();
out.close();
To read and re-construct the object back in a program, use the method readObject(), which returns
an java.lang.Object. Downcast the Object back to its original type.
ObjectInputStream in =
new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("object.ser")));
String str = (String)in.readObject();
Date d = (Date)in.readObject(new Date()); // downcast
in.close();
// Write objects
try (ObjectOutputStream out =
new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream(filename)))) {
// Create an array of 10 MySerializedObjects with ascending numbers
MySerializedObject[] objs = new MySerializedObject[numObjs];
for (int i = 0; i < numObjs; ++i) {
objs[i] = new MySerializedObject(0xAA + i); // Starting at AA
}
// Write the objects to file, one by one.
for (int i = 0; i < numObjs; ++i) {
out.writeObject(objs[i]);
}
// Write the entire array in one go.
out.writeObject(objs);
out.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
// Read raws bytes and print in Hex
try (BufferedInputStream in =
new BufferedInputStream(
new FileInputStream(filename))) {
int inByte;
while ((inByte = in.read()) != -1) {
System.out.printf("%02X ", inByte); // Print Hex codes
}
System.out.printf("%n%n");
} catch (IOException ex) {
ex.printStackTrace();
}
// Read objects
try (ObjectInputStream in =
new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(filename)))) {
// Read back the objects, cast back to its original type.
MySerializedObject objIn;
for (int i = 0; i < numObjs; ++i) {
objIn = (MySerializedObject)in.readObject();
System.out.println(objIn.getNumber());
}
MySerializedObject[] objArrayIn;
objArrayIn = (MySerializedObject[])in.readObject();
for (MySerializedObject o : objArrayIn) {
System.out.println(o.getNumber());
}
} catch (ClassNotFoundException|IOException ex) { // JDK 7
ex.printStackTrace();
}
}
}
class MySerializedObject implements Serializable {
private int number;
public MySerializedObject(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
}
AC ED 00 05 73 72 00 12 4D 79 53 65 72 69 61 6C 69 7A 65 64 4F 62 6A 65
63 74 1F 7B 91 BD 02 1C DC 30 02 00 01 49 00 06 6E 75 6D 62 65 72 78 70
00 00 00 AA 73 71 00 7E 00 00 00 00 00 AB 73 71 00 7E 00 00
00 00 00 AC 73 71 00 7E 00 00 00 00 00 AD 73 71 00 7E 00 00
00 00 00 AE 75 72 00 15 5B 4C 4D 79 53 65 72 69 61 6C 69 7A 65 64 4F 62
6A 65 63 74 3B 13 95 A0 51 BC 86 75 38 02 00 00 78 70 00 00 00 05 71 00
7E 00 01 71 00 7E 00 02 71 00 7E 00 03 71 00 7E 00 04 71 00 7E 00 05
such
static fields are not serialized, as it belongs to the class instead of the particular instance to be
serialized.
To prevent certain fields from being serialized, mark them using the keyword transient. This
Warning Message "The serialization class does not declare a static fi nal
serialVersionUID fi eld of type long" (Advanced)
This warning message is triggered because your class (such as java.swing.JFrame) implements
the java.io.Serializable interface. This interface enables the object to be written out to an output
stream serially (via
method writeObject());
and
read
back
into
the
program
(via
method readObject()). The serialization runtime uses a number (called serialVersionUID) to ensure
that the object read into the program (during deserialization) is compatible with the class definition, and
not belonging to another version. It throws an InvalidClassException otherwise.
You have these options:
1.
2.
Simply ignore this warning message. If a serializable class does not explicitly declare
a serialVersionUID,
then
the
serialization
runtime
will
calculate
a
default serialVersionUID value for that class based on various aspects of the class.
Add a serialVersionUID (Recommended), e.g.
private static final long serialVersionUID = 1L; // verion 1
3.
Suppress
this
particular
package java.lang) (JDK 1.5):
4.
@SuppressWarnings("serial")
warning
via
java.io.Externalizable Interface
The Serializable has a sub-interface called Externalizable, which you could used if you want to
customize the way a class is serialized. Since Externalizable extends Serializable, it is also
aSerializable and you could invoke readObject() and writeObject().
Externalizable declares two abstract methods:
void writeExternal(ObjectOutput out) throws IOException
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
RandomAccessFile can be treated as a huge byte array. You can use a file pointer (of type long),
similar to array index, to access individual byte or group of bytes in primitive types (such as int and
double). The file pointer is located at 0 when the file is opened. It advances automatically for every read
and write operation by the number of bytes processed.
In constructing a RandomAccessFile, you can use flags 'r' or 'rw' to indicate whether the file is "readonly" or "read-write" access, e.g.,
RandomAccessFile f1 = new RandomAccessFile("filename", "r");
RandomAccessFile f2 = new RandomAccessFile("filename", "rw");
RandomAccessFile does
not
inherit
from InputStream or OutputStream.
However,
it
implements DataInput and DataOutput interfaces
(similar
to DataInputStream and DataOutputStream). Therefore, you can use various methods to read/write
primitive types to the file, e.g.,
public
public
public
public
Example: Read and write records from a RandomAccessFile. (A student file consists of student
record of name (String) and id (int)).
[PENDING]
The tokens may then be converted into primitive values of different types using the
various nextXxx() methods
(nextInt(), nextByte(), nextShort(), nextLong(), nextFloat(), nextDouble(),nextBoolean(), ne
xt() for String, and nextLine() for an input line). You can also use the hasNextXxx() methods to
check for the availability of a desired input.
The commonly-used constructors are as follows. You can construct a Scanner to parse a bytebased InputStream (e.g., System.in), a disk file, or a given String.
// Scanner piped from a disk File
public Scanner(File source) throws FileNotFoundException
public Scanner(File source, String charsetName) throws FileNotFoundException
// Scanner piped from a byte-based InputStream, e.g., System.in
public Scanner(InputStream source)
public Scanner(InputStream source, String charsetName)
// Scanner piped from the given source string (NOT filename string)
public Scanner(String source)
For examples,
// Construct a Scanner to parse an int from keyboard
Scanner in1 = new Scanner(System.in);
int i = in1.nextInt();
// Construct a Scanner to parse all doubles from a disk file
Scanner in2 = new Scanner(new File("in.txt")); // need to handle FileNotFoundException
while (in2.hasNextDouble()) {
double d = in.nextDouble();
}
// Construct a Scanner to parse a given text string
Scanner in3 = new Scanner("This is the input text String");
while (in3.hasNext()) {
String s = in.next();
}
Example 1: The most common usage of Scanner is to read primitive types and String form the
keyboard (System.in), as follows:
import java.util.Scanner;
public class TestScannerSystemIn {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter an integer: ");
int anInt = in.nextInt();
System.out.println("You entered " + anInt);
System.out.print("Enter a floating-point number: ");
double aDouble = in.nextDouble();
System.out.println("You entered " + aDouble);
System.out.print("Enter 2 words: ");
String word1 = in.next(); // read a string delimited by white space
String word2 = in.next(); // read a string delimited by white space
System.out.println("You entered " + word1 + " " + word2);
in.nextLine(); // flush the "enter" before the next readLine()
System.out.print("Enter a line: ");
String line = in.nextLine(); // read a string up to line delimiter
System.out.println("You entered " + line);
}
}
The nextXxx() methods throw InputMismatchException if the next token does not match the type
to be parsed.
Example 2: You can easily modify the above program to read the inputs from a text file, instead of
keyboard (System.in).
import java.util.Scanner;
import java.io.*;
public class TestScannerFile {
public static void main(String[] args) throws FileNotFoundException {
Scanner in = new Scanner(new File("in.txt"));
System.out.print("Enter an integer: ");
int anInt = in.nextInt();
System.out.println("You entered " + anInt);
System.out.print("Enter a floating-point number: ");
double aDouble = in.nextDouble();
System.out.println("You entered " + aDouble);
System.out.print("Enter 2 words: ");
String word1 = in.next(); // read a string delimited by white space
String word2 = in.next(); // read a string delimited by white space
System.out.println("You entered " + word1 + " " + word2);
in.nextLine(); // flush the "enter" before the next readLine()
System.out.print("Enter a line: ");
String line = in.nextLine(); // read a string up to line delimiter
System.out.println("You entered " + line);
}
}
Delimiter
Instead of the default white spaces as the delimiter, you can set the delimiter to a chosen regular
expression via these methods:
public Pattern delimiter() // Returns the current delimiter Regexe Pattern
public Scanner useDelimiter(Pattern pattern) // Sets the delimiter Regexe Pattern
public Scanner useDelimiter(String pattern)
The regular expression \s*apple\s* matches zero or more white spaces ( \s*) followed by "apple"
followed by zero or more white spaces (\s*). An additional backslash (\) is needed to use a backslash (\)
in Java String's literal. Read "Regular Expression" for more details.
You can use the following methods to find the next occurrence of the specified pattern using regular
expressions:
// Find the next occurrence of a pattern, ignoring delimiters
public String findInLine(Pattern pattern)
public String findInLine(String pattern)
public String findWithinHorizon(Pattern pattern, int horizon)
public String findWithinHorizon(String pattern, int horizon)
// Skips input that matches the specified pattern, ignoring delimiters
public Scanner skip(Pattern pattern)
public Scanner skip(String pattern)
Charset
By default, Scanner uses the default charset to read the character from the input source. You can
ask Scanner to read text file which is encoded using a particular charset, by providing the charset
name.
Example 4:
import java.util.*;
import java.io.*;
public class TestScannerTextFile {
public static void main(String[] args) throws FileNotFoundException {
String filename = "test_scanner.txt";
String message = "Hi,!\n"; // with non-ASCII chars
// Create a Text file in UTF-8
// Can also use formatter (see below)
try (BufferedWriter out =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(filename), "UTF-8"))) {
out.write("12345 55.66\n");
out.write(message);
out.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
// Read raws bytes and print in Hex
try (BufferedInputStream in =
new BufferedInputStream(
new FileInputStream(filename))) {
int inByte;
while ((inByte = in.read()) != -1) {
System.out.printf("%02X ", inByte); // Print Hex codes
}
System.out.println();
! LF [UTF-8]
12345
55.66
Hi,!
printf() takes a variable number (zero or more) of arguments (or varargs). Varargs was introduced in JDK
1.5 (that is the reason Java cannot support printf() earlier).
printf() can be called from System.out or System.err ( which are PrintStreams). For example,
System.out.printf("Hello %4d %6.2f %s, and%n Hello again%n", 123, 5.5, "Hello");
Hello 123 5.50 Hello, and
Hello again
You can also use the System.out.format() method, which is identical to System.out.printf().
A format specifier begins with '%' and ends with a conversion-type character (e.g. "%d" for
integer, "%f" for float and double), with optional parameters in between, as follows:
%[argument_position$][flag(s)][width][.precision]conversion-type-character
The optional argument_position specifies the position of the argument in the argument list. The
first argument is "1$", second argument is "2$", and so on.
The optional width indicates the minimum number of characters to be output.
The optional precision restricts the number of characters (or number of decimal places for floatpoint numbers).
The mandatory conversion-type-character indicates how the argument should be formatted. For
examples: 'b', 'B' (boolean), 'h', 'H' (hex string), 's', 'S' (string), 'c', 'C' (character), 'd' (decimal
integer), 'o' (octal integer), 'x', 'X' (hexadecimal integer), 'e', 'E' (float-point number in scientific
notation), 'f' (floating-point number), '%' (percent sign). The uppercase conversion code (e.g., 'S')
Examples
+3,1416
Notice that the method format() has the same syntax as the method printf(), using the same set of
format specifier as printf().
Other methods are:
public void flush() // flush out all the buffered output
public void close()
Example
the Formatter.
1import java.util.Formatter;
2import java.util.Locale;
3
4public class TestFormatter {
5 public static void main(String[] args) {
6
7
// Use a StringBuilder (Appandable) as the output sink for the Formatter
8
StringBuilder sb = new StringBuilder();
9
Formatter formatter = new Formatter(sb, Locale.US);
10
11
12 // Re-order output.
13 formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d");
14 System.out.println(sb); // -> " d c b a"
15
16 // Use the optional locale as the first argument.
17 sb.delete(0, sb.length());
18 formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E);
19 System.out.println(sb); // -> "e = +2,7183"
20
21 // Try negative number with '(' flag and group separator.
22 sb.delete(0, sb.length());
23 formatter.format("Net gained or lost: $ %(,.2f", -1234.567);
System.out.println(sb);
24
}
25
}
Read JDK API java.util.Formatter's "Format String Syntax" for details on format specifiers.
Example 2: Setting the charset for Formatter's output.
import java.io.*;
import java.util.*;
SP 55.66
SP H i ,
! LF
170
55.66
Hi,!
9.4 String.format()
The Formatter with StringBuilder as the output sink allows you to build up a formatted string
progressively.
To
produce
a
simple
formatted String,
you
can
simply
use
the static methodString.format(). This is handy in the toString() method, which is required to return
a String. For example,
1public class Time {
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20}
10.2
To create a Path, use the static method get() of the helper class java.nio.file.Paths. The helper
class Paths contains
exclusively static methods
for
creating Path objects. Paths.get() returns
aPath object by converting a given path string or URI.
public static Path get(String first, String... more)
// This method accepts variable number of arguments (varargs).
// It converts a path string, or a sequence of strings that when joined form a path string, to a Path object.
// The location of the Path may or may not exist.
public static Path get(URI uri)
// Converts the given URI to a Path object.
For example,
Path p1 = Paths.get("in.txt");
// A file in current directory, relative
Path p2 = Paths.get("c:\\myproejct\\java\\Hello.java"); // File, absolute, need escape sequence for '\'
Path p3 = Paths.get("/use/local"); // A directory
As the directory-separator is system dependent, for writing portable and more flexible program, it is
recommended to use an existing Path instance as an anchor to resolve the filename, e.g.,
Path dir = ...
Path file = dir.resolve("filename"); // Joins the filename to the dirname with a directory-separator
A Path can be broken down as root, level-0, level-1, .... The method getNameCount() returns the
levels. The method getName(i) returns the name of level-i. For example,
import java.nio.file.*;
public class PathInfo {
public static void main(String[] args) {
// Windows
Path path = Paths.get("D:\\myproject\\java\\test\\Hello.java");
// Unix/Mac
//Path path = Paths.get("/myproject/java/test/Hello.java");
// Print Path Info
System.out.println("toString: " + path.toString()); // D:\myproject\java\test\Hello.java
System.out.println("getFileName: " + path.getFileName()); // Hello.java
System.out.println("getParent: " + path.getParent());
// D:\myproject\java\test
System.out.println("getRoot: " + path.getRoot());
// D:\
// root, level-0, level-1, ...
int nameCount = path.getNameCount();
System.out.println("getNameCount: " + nameCount); // 4
for (int i = 0; i < nameCount; ++i) {
System.out.println("getName(" + i + "): " + path.getName(i)); // (0)myproject, (1)java,
}
// (2) test, (3) Hello.java
System.out.println("subpath(0,2): " + path.subpath(0,2)); // myproject\java
System.out.println("subpath(1,4): " + path.subpath(1,4)); // java\test\Hello.java
}
}
Properties of a File/Directory
You can use static boolean methods Files.exists(Path) and File.notExists(Path) to verify if a
given Path exists or does not exist (as a file, directory or symlink). A Path could be verified to exist, or
not exist, or unknown (e.g., the program does not have access to the file). If the status is unknown,
the exists() and noExists() returns false.
You
could
also
use static boolean methods Files.isDirectory(Path), Files.isRegularFile(Path) and Files.isSymb
olicLink(Path) to verify whether a Path locates a file, directory, or symlink.
Many of these methods take an optional second argument of LinkOption, which is applicable for
symlink only. For example, LinkOption.NOFOLLOW_LINKS specifies do not follow the symlink.
public static long size(Path path) // Returns the size of the file
public static boolean exists(Path path, LinkOption... options)
// Verifies whether the given Path exists, as a file/directory/symlink.
// It returns false if the file does not exist or status is unknown.
// LinkOption specifies how symlink should be handled,
// e.g., NOFOLLOW_LINKS: Do not follow symlinks.
public static boolean notExists(Path path, LinkOption... options)
// Not exists?
public static boolean isDirectory(Path path, LinkOption... options) // a directory?
public static boolean isRegularFile(Path path, LinkOption... options) // a file?
public static boolean isSymbolicLink(Path path)
// a symblink?
public static boolean isReadable(Path path) // readable?
public static boolean isWritable(Path path) // writable?
public static boolean isExecutable(Path path) // executable?
Deleting a File/Directory
You can use static methods delete(Path) to delete a file or directory. Directory can be deleted only if it
is empty. A boolean method deleteIfExists() is also available.
public static void delete(Path path) throws IOException
public static boolean deleteIfExists(Path path) throws IOException
Copying/Moving a File/Directory
You can use static methods copy(Path, Path, CopyOption) or move(Path, Path, CopyOption) to
copy or move a file or directory. The methods return the target Path.
The
methods
accepts
an optional third
argument
of CopyOption.
For
examples: CopyOption.REPLACE_EXISTING replaces
the
target
if
it
exists; CopyOption.COPY_ATTRIBUTES copies
the
file
attributes
such
as
the
dates; CopyOption.NOFOLLOW_LINKS specifies not to follow symlinks.
public static Path copy(Path source, Path target, CopyOption... options) throws IOException
public static Path move(Path source, Path target, CopyOption... options) throws IOException
Byte-based Operation
Character-based Operation
public static List<String> readAllLines(Path path, Charset cs) throws IOException
// Reads all lines and returns a list of String.
// Line terminator could be "\n", "\r\n" or "\r".
public static Path write(Path path, Iterable<? extends CharSequence> lines,
Charset cs, OpenOption... options) throws IOException
Examples: The following example write a small text file (in "UTF-8"), and read the entire file back as
bytes as well as characters.
import java.io.*;
import java.nio.file.*;
import java.nio.charset.*;
import java.util.*;
! CR LF
48 65 6C 6C 6F 2C E5 90 83 E9 A5 B1 E4 BA 86 E6 B2 A1 E6 9C 89 3F 0D 0A
H e l l o ,
? CR LF
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
[1]'H'(0048) [2]'e'(0065) [3]'l'(006C) [4]'l'(006C) [5]'o'(006F) [6]','(002C)
[7]''(5403) [8]''(9971) [9]''(4E86) [10]''(6CA1) [11]''(6709) [12]'?'(003F)
Example: Similar to the previous program which read/write the entire file, this program read/write via
Buffered I/O.
import java.io.*;
import java.nio.file.*;
import java.nio.charset.*;
import java.util.*;
}
48 69 2C E6 82 A8 E5 A5 BD 21
H i ,
!
48 65 6C 6C 6F 2C E5 90 83 E9 A5 B1 E4 BA 86 E6 B2 A1 E6 9C 89 3F
H e l l o ,
?
[1]'H'(0048) [2]'i'(0069) [3]','(002C) [4]''(60A8) [5]''(597D) [6]'!'(0021)
[7]'H'(0048) [8]'e'(0065) [9]'l'(006C) [10]'l'(006C) [11]'o'(006F) [12]','(002C)
[13]''(5403) [14]''(9971) [15]''(4E86) [16]''(6CA1) [17]''(6709) [18]'?'(003F)
DOS:
Unixes: Nine file permissions: read, write, and execute permissions for the file owner, members
of the same group, and "everyone else", e.g., "rwxr-x---".
Example: [TODO]
Similarly, you can create a new directory, symlink as follows:
public static Path createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException
// Creates a new directory.
public static Path createDirectories(Path dir, FileAttribute<?>... attrs) throws IOException
// Creates a directory by creating all nonexistent "parent" directories first
public public static Path createSymbolicLink(Path link, Path target,
FileAttribute<?>... attrs) throws IOException
// Creates a symbolic link to a target
Example:
try {
Files.createDirectory(Paths.get("d:\\temp\\test"));
// With default attribute
// FileAlreadyExistsException if already exists
Files.createDirectory(Paths.get("d:\\temp\\test1\\test2"));
// NoSuchFileException if parent does not exist
Files.createDirectories(Paths.get("d:\\temp\\test1\\test2"));
// Also create the parent directory
} catch (IOException ex) {
ex.printStackTrace();
}
Example: [TODO]
10.5 FileChannel
public static SeekableByteChannel newByteChannel(
Path path, OpenOption... options) throws IOException
// Opens or creates a file, returning a seekable byte channel to access the file.
public static SeekableByteChannel newByteChannel(
Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
throws IOException
// Opens or creates a file, returning a seekable byte channel to access the file.
Listing a directory
You can list the contents of a directory by using the Files.newDirectoryStream(Path) method. The
returned DirectoryStream object implements Iterable. You can iterate thru the entries with for-each
loop.
public static DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException
Glob
A glob is a subset of regular expression.
'**' behaves like '*', but can cross directory boundary, for matching on full path.
[...] encloses a set of single character or a range of character with '-', e.g., [aeiou], [a-z], [0-9].
Any other character matches itself. To match '*', '?' or special characters, prefix with '\'.
For example: "h*.{java,class,txt}" matches entry starts with " h" and ends with ".java", ".class", or
".txt".
You can also write your own codes to filter the entries.
public static DirectoryStream<Path> newDirectoryStream(
Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException
The interface DirectoryStream.Filter<T> declares one abstract boolean method accept(), which
will be call-back for each entry. Those entries that resulted in false accept() will be discarded.
public boolean accept(T entry) throws IOException
Example: The
following
program
uses
an
anonymous
instance
of
an
anonymous DirectoryStream.Filter sub-class to filter the DirectoryStream. The call-back
method accept() returns true for regular files, and discards the rest. Take note that this filtering
criterion cannot be implemented in a glob-pattern.
// List the contents of a directory with a custom filter
Path dir = Paths.get("d:\\temp");
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dir,
new DirectoryStream.Filter<Path>() { // anonymous inner class
public boolean accept(Path entry) throws IOException {
return (Files.isRegularFile(entry)); // regular file only
}
})) {
for (Path entry : dirStream) {
System.out.println(entry.getFileName()); // Filename only
System.out.println(entry.toString());
}
} catch (IOException ex) {
ex.printStackTrace();
}
// Full-path name
These
methods
return
an enum of FileVisitResult,
which
could
take
values
of CONTINUE, TERMINATE, SKIP_SUBTREES, SKIP_SIBLINGS.
Instead
of
implementing FileVisitor interface,
you
could
also
extend
from
superclass SimpleFileVisitor, and override the selected methods.
There are two versions of walkFileTree(). The first version take a starting directory and a FileVisitor,
and transverse through all the levels, without following the symlinks.
public static Path walkFileTree(Path startDir, FileVisitor<? super Path> visitor) throws IOException
Example:
import java.util.EnumSet;
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
The second version takes 2 additional arguments: the options specifies whether to follow symlink
(e.g., EnumSet.noneOf(FileVisitOption.class) or EnumSet.of(FileVisitOption.FOLLOW_LINKS))
; the maxDepth specifies the levels to visit (set to Integer.MAX_VALUE for all levels).
public static Path walkFileTree(
Path startDir, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor)
throws IOException
For example, the following main() method can be used for the previous example.
public static void main(String[] args) {
try {
Path startingDir = Paths.get("..");
EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
// or EnumSet.noneOf(FileVisitOption.class)
Files.walkFileTree(startingDir, opts, 2, new WalkFileTreeTest());
// use Integer.MAX_VALUE for all level
} catch (IOException ex) {
System.out.println(ex);
}
}
Java
Online
Tutorial
on
@ http://download.oracle.com/javase/tutorial/essential/io/index.html,
(Featuring NIO.2)".
"Basic
in particular
I/O"
"File I/O
InputStreamReader-This class reads bytes from an underlying stream and converts it to characters using
a Charset. It acts as a bridge between a byte stream and character stream
PipedReader- This class connects to a PipedWriter and reads characters from it.
BufferedReader-Reads characters from a character input stream and buffers data. The buffering can efficiently
read lines or array of characters. It is wrapped around another reader that reads a single character of data
such as FileReader or InputStreamReader
FileReader-This is a convenience class for reading characters from a file. It extends InputStreamReader. It
uses the default encoding scheme and byte buffer. To specify another encoding scheme or byte buffer create
an implementation of InputSteamReader on a FileInputStream
OutputStreamWriter-This class encodes characters to bytes using a specified charset.It acts as a bridge
between a character stream and byte stream
FileWriter-This class extends OutputStreamWriter and is a convenience method for writing to files. It uses a
default encoding and buffer size. To specify a different buffer size or encoding scheme, create an
OutputStreamWriter on a FileOutputStream
PipedWriter-This can be connected to a PipedReader. It writes characters to the stream which is read by the
PipedReader.
PrintWriter-This class formats objects and writes them to a character stream. It has automatic flushing for
some methods. It never throws IOException. This class is very useful, for example, to write to a file the
method println(String s) writes the string s, writes a newLine and then flushes the stream if auto flushing is
enabled
BufferedWriter-Writes characters to a character stream after buffering them.A newLine() method is provided
that writes the platforms line separator to the stream.
1
2
3
int a = 0;
writer.write(a);
10
} catch (FileNotFoundException e) {
11
e.printStackTrace();
12 } catch (IOException e) {
e.printStackTrace();
13
14
} finally {
try {
15
reader.close();
16
writer.close();
17
} catch (IOException e) {
18
e.printStackTrace();
19
20
}
}
21
1
2
3
4
String s = "";
bufferedWriter.write(s);
10
bufferedWriter.newLine();
11
// flush
12
bufferedWriter.flush();
}
13
14 } catch (FileNotFoundException e) {
e.printStackTrace();
15
16
} catch (IOException e) {
e.printStackTrace();
17
18
} finally {
// close without throwing exception
19
IOUtils.closeQuietly(bufferedReader);
20
21
IOUtils.closeQuietly(bufferedWriter);
}
22
?
Read and Write characters using PrintWriter
1
Reader readerUnicode =
int e = 0;
char f = (char) e;
System.out.print(f);
?
1
//or
byte[] data=Files.readAllBytes(tempFilePath);
// or
List<String> lines=
Files.readAllLines(tempFilePath, StandardCharsets.UTF_8);
InputStream stream2 =
IOUtils.toInputStream("String to be converted to input stream");
?
1
IOUtils.write(data2, outputStream);
Files.write(tempFilePath, data,StandardOpenOption.APPEND);
read the contents of a file into a string. There is a similar write method too.
?
1
FileUtils.readFileToString(file, string));
BufferedReader reader =
Files.newBufferedReader(tempFilePath,StandardCharsets.UTF_8);
3
4
BufferedWriter writer=
Files.newBufferedWriter(tempFilePath, StandardCharsets.UTF_8,StandardOpenOption.APPE
InputStream inputStream =
Files.newInputStream(tempFilePath, StandardOpenOption.READ);
FileInputStream- This stream reads raw bytes from a file. The read methods in this class return a byte of data
read from a file.
ObjectInputStream- This class is used to read objects written using ObjectOutputStream. It deserializes the
primitive data types and objects
PipedInputStream- A unix 'pipe' like implementation can be accomplished using this class. It can connect to a
pipedoutputsteam and read data off it.
BufferedInputStream- This is probably the most used class. It buffers the data from any of the above
inputstream. The methods in this class increase reading efficiency since they try to read the maximum number
of bytes that can be read from the file system in one operation.
ByteArrayInputStream-This class contains a buffer(array) or bytes that store the next few bytes that will be read
from the stream.
FileOutputStream- The methods in this class write bytes of data to a file. Note that this writes raw bytes and
not characters.
ObjectOutputStream - writes primitive java types and objects to an ouputstream. The data could be written to a
file or to a socket. Data written using this method can be read back using the ObjectInputStream
PipedOutputStream- A piped output stream connects to a PipedInputStream to read bytes. Data may be
written by one thread and read by another.
BufferedOutputStream-It buffers data from an inputstream and writes the buffered data. It is an efficient
method of writing data since the operating systems may write an array of bytes in a single operation and
invoking write operation for each byte may be inefficient
PrintStream-It wraps an InputStream and adds functionality to print various representations of data values. It
never throws IOException and there is an option for automatic flushing
?
File- Read and Write bytes
1
int a = 0;
while (a != -1) {
4
a = input.read();
out.write(a);
6
}
7
8
input.close();
out.close();
?
File- Read and Write bytes
1
2
3
4
5
Read the first three char bytes once, read the next five twice, skip the next four
7
8
9
10
int b = 0;
byte[] firstThree = new byte[3];
bufferedInput.read(firstThree);
// convert the array of bytes to characters using the default encoding.
System.out.println(Charset.defaultCharset().decode(ByteBuffer.wrap(firstThree)));
11
12
13
// mark the current position, since we would read this again. The mark position is v
bufferedInput.mark(6);
14
15
18
19 System.out.println(Charset.defaultCharset().decode(ByteBuffer.wrap(nextFive)));
20
21 // go back to the marked position and read it again
22 bufferedInput.reset();
23 byte[] readTheFiveAgain = new byte[5];
24 bufferedInput.read(readTheFiveAgain);
25
System.out.println(Charset.defaultCharset().decode(ByteBuffer.wrap(readTheFiveAgain)
26
27
28
29
30
31
32
33
34
35
36 bufferedOut.write(nextFive);
37 bufferedOut.write(readTheFiveAgain);
38 bufferedOut.write(lastThree);
39 bufferedOut.flush();
40 bufferedInput.close();
41 bufferedOut.close();
42
ObjectInputStream can be used to de-serialize and read java objects that have been serialized and written using
ObjectOutputStream. In the example below the 'Course' object is written to a persistent storage. The Course object
contains a list of Student objects. The object writing preserves object references. We first create an object output stream
that stores the bytes to a file using a FileOutputStream.
?
Create an object output stream.
1
2
3
// write a date
objectOutputStream.writeObject(new Date());
5
6
7
8
// write a boolean
objectOutputStream.writeBoolean(true);
// write a float
objectOutputStream.writeFloat(1.0f);
// the other primitive types and objects can be saved as well
9
10
11
// create two students objects and add them in a list. create a course
// object and add the list of students to a list
24 course.setStudents(students);
25
26 // write the course object to the objectoutputstream. This writes the
27 // object as well all objects that it referes to.
28
29
30
31
32
objectOutputStream.writeObject(course);
objectOutputStream.flush();
objectOutputStream.close();
33
// the object input stream reads the objects back from the file and
34
35
36
37
38
39 // start getting the objects out in the order in which they were written
40 Date date = (Date) objectInputStream.readObject();
41 System.out.println(date);
42 System.out.println(objectInputStream.readBoolean());
43 System.out.println(objectInputStream.readFloat());
44
45 // get the course object
46
47
48
49
50
51
52
53
54
The Course Class
?
Create an object output stream.
1
4
5
return name;
8
9
10
this.name = name;
11
12
public int getAge() {
13
return age;
14
}
15
16
17
this.age = age;
18
}
19
20
String name;
List<Student> students;
4
5
this.name = name;
8
9
10
this.students = students;
}
11
12
13
return name;
14
}
15
16
17
return students;
18
}
19
20
In our previous discussions we came across Byte Streams and Character Streams in Java. In this particular blog we
will dive into Buffered Streams. We will come to know how to read from and write to a file in java using Bufferd
Streams and why this is a good practice to use buffered streams over byte and character streams.
In case of byte and character streams every byte or piece of data that s being read or write requires an direct support
from underlying OS because of not having an intermediate buffer included. This makes a extensive use of memory and
resources. On the other hand Buffered streams works as a wrapper to hold unbuffered streams and make it possible to
store bunch of data or bytes in buffers (memory). The underlying OS resource are needed only when the buffer is full or
empty and not on every instance of read or write.
The native input API is called only when the buffer is empty. Similarly, buffered output streams write data to a buffer, and
the native output API is called only when the buffer is full. Unbuffered streams can be converted to buffered streams by
wrapping them in a constructor of Bufferd Stream class as shown below.
1.
package com.beingjavaguys.core;
2.
3.
import java.io.BufferedInputStream;
4.
import java.io.BufferedOutputStream;
5.
import java.io.FileInputStream;
6.
import java.io.FileNotFoundException;
7.
import java.io.FileOutputStream;
8.
import java.io.IOException;
9.
/**
10.
11.
12.
*/
15.
16.
17.
18.
19.
20.
try {
bis = new BufferedInputStream(new FileInputStream(
"files/source.txt"));
21.
22.
"files/destination.txt"));
23.
int data;
24.
25.
System.out.println(data);
26.
bos.write(data);
27.
28.
} catch (FileNotFoundException e) {
29.
e.printStackTrace();
30.
} finally {
31.
if (bis != null)
32.
bis.close();
33.
if (bos != null)
34.
bos.close();
35.
36.
}
}
37. }
1.
package com.beingjavaguys.core;
2.
3.
import java.io.BufferedReader;
4.
import java.io.BufferedWriter;
5.
import java.io.FileNotFoundException;
6.
import java.io.FileReader;
7.
import java.io.FileWriter;
8.
import java.io.IOException;
9.
/**
10.
11.
12.
*/
15.
16.
17.
18.
try {
19.
20.
21.
"files/destination.txt"));
22.
String data;
23.
24.
System.out.println(data);
25.
bufWriter.write(data);
26.
27.
} catch (FileNotFoundException e) {
28.
e.printStackTrace();
29.
} finally {
30.
if (bufReader != null)
31.
bufReader.close();
32.
if (bufWriter != null)
33.
bufWriter.close();
34.
35.
}
}
36. }
Here we are done with 'Read and Write a file in Java using 'Byte Buffered Streams' and 'Character Buffered Streams'. In
our upcoming blogs we will see more about Java Programming and other opensource technologies.