1 InputStream 与 OuputStream
面向字节。
InputStream的作用是用来表示那些从不同数据源产生输入的类。这些数据包括:1)字节数组。2)String对象。3)文件。4)多线程中的数据源。“管道”,即从一端流入,从另一端输出。5)一个由其他种类的流组成吃的序列。6)其他数据源,如Internet连接等。
FilterInputStream是用来提供装饰器类接口,以控制特定输入流(InputStream)。
图 InputStream UML分析
OutputStream 决定啦输出所要去的目标:字节数组(但不是String)、文件或管道(多线程数据)。
FilterOutputStream是用来提供装饰器类接口,以控制特定输出流(OutputStream)。
图 OutputStream UML分析
2 Reader和Writer
面向字符。
InputStreamReader可以把InputStream 转换为Reader,而OutputStreamWriter可以把OutputStream转换为Writer。
几乎所有原始Java I/O流类的相应操作都有相应的Reader和Writer来提供天然的Unicode操作。 因此最明智的做法是尽量尝试使用Reader和Writer。
来源与去处:Java 1.0类 | 相应的Java 1.1类 |
InputStream | Reader 适配器:InputStreamReader |
OutputStream | Writer 适配器: OutputStreamWriter |
FileInputStream | FileReader |
FileOutputStream | FileWriter |
StringBufferInputStream(已弃用) | StringReader |
无相应的类 | StringWriter |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
PipedInputStream | PipedReader |
PipedOutputStream | PipedWriter |
表 新旧I/O类对比
2.1 装饰器
过滤器:Java 1.0类 | 相应的 Java 1.1类 |
FilterInputStream | FilterReader |
FilterOutputStream | FilterWriter(抽象类,没有子类) |
BufferedInputStream | BufferedReader(也有readLine()) |
BufferedOutputStream | BufferedWriter |
DataInputStream | 使用DataInputStream(除了需要使用readLine()时以为,这时应该使用BufferedReader) |
PrintStream | PrintWriter |
LineNumberInputStream(已弃用) | LineNumberReader |
StreamTokenizer | StreamTokenizer(使用接受Reader的构造器) |
PushbackInputStream | PushbackReader |
表 新旧I/O类装饰器对比
3 自我独立的类:RandomAccessFile
适用于由大小已知的记录组成的文件。不是InputStream或者OutputStream继承层次结构中的一部分。其工作方式类似于把DataInputStream和DataOutStream组合起来使用。还添加了一些方法:
getFilePointer(),用于查找当前所处的文件位置。
seek()用于在文件内移至新的位置。
length()用于判断文件的最大尺寸。
另外其构造器还需要第二个参数,用来指示我们只是“随机读(r)”还是“既读又写(rw)”。并不支持只写文件。
只用RandomAccessFile支持搜寻方法,并且只适用于文件。
public class RandomAccessFileTest {
private static String FOLDER = "/Users/xxx/Desktop/个人/csdn/java/第九周/write_folder/";
private static void display() throws IOException {
RandomAccessFile file = new RandomAccessFile(FOLDER + "randomAccessFile.txt", "rw");
byte[] bytes = new byte[2];
while (file.read(bytes) != -1)
System.out.print(new String(bytes));
file.close();
}
@Test
public void test() throws IOException {
RandomAccessFile file = new RandomAccessFile(FOLDER + "randomAccessFile.txt", "rw");
file.writeUTF("Hello RandomAccessFile");
file.writeDouble(123.032);
file.writeBoolean(false);
System.out.println("FilePointer" + file.getFilePointer());
System.out.println("length:" + file.length());
file.close();
file = new RandomAccessFile(FOLDER + "randomAccessFile.txt","r");
System.out.println("readDouble:" + file.readDouble());
display();
// 运行结果:
// FilePointer33
// length:33
// readDouble:3.098804402169332E-308
// Hello RandomAccessFile@^�I�^55
}
}
4 标准 I/O
在程序设计中,当计算机程序开始执行时,标准流是指计算机程序与其运行环境之间预先连接的输入和输出通信信道。
三个输入/输出(I/O)连接的信道分别为标准输入(stdin)、标准输出(stdout)和标准错误(stderr)。Java提供了System.in、System.out和System.err。
System.out 是已经被包装成了PrintStream对象,System.err 也是PrintStream。但System.err是一个没有被包装过的未经加工的InputStream。
5 新I/O
新的Java I/O类库提高了速度。所使用的结构更接近于操作系统执行I/O的方式:通道和缓冲器。“通道是一个包含数据的矿藏,而缓冲器则是派送到矿藏的卡车。”
唯一直接与通道交互的缓冲器是ByteBuffer。
5.1 ByteBuffer
对于只读访问,我们必须显示地使用静态的allocate()方法来分配ByteBuffer。nio的目标是快速移动大量数据,因此ByteBuffer的大小就显得尤为重要。必须通过实际运行应用程序来找到最佳尺寸。
5.1.1 flip() 与 clear()
一旦调用read()来告知FileChannel 向 ByteBuffer 存储字节,就必须调用缓冲器上的flip(),让它做好让别人读取字节的准备。如果打算进一步read()操作,就必须调用clear()来为read()做好准备。
buffer.flip(); //为writing做准备
buffer.clear;//为reading做准备
5.1.2 视图缓冲器
视图缓冲器可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。对视图的任何修改都会映射成为对ByteBuffer中数据的修改。
@Test
public void test() throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
IntBuffer intBuffer = buffer.asIntBuffer();
intBuffer.put(new int[]{1,2,46,2,46,3556,244});
intBuffer.flip();
while (intBuffer.hasRemaining())
System.out.print(intBuffer.get() + " ");
DoubleBuffer doubleBuffer = buffer.asDoubleBuffer();
buffer.clear();
doubleBuffer.put(12.424);
doubleBuffer.flip();
System.out.println();
while (doubleBuffer.hasRemaining())
System.out.print(doubleBuffer.get() + " ");
// 运行结果:
// 1 2 46 2 46 3556 244
// 12.424
}
5.2 FileChannel
transFerFrom 与 transFerTo 拷贝文件
@Test
public void test2() throws IOException {
FileChannel inputChannel = new FileInputStream(FOLDER + "hello.txt").getChannel();
FileChannel channel = new FileOutputStream(FOLDER + "copyFromHello.txt").getChannel();
inputChannel.transferTo(0,inputChannel.size(),channel);
FileChannel fileChannel = new RandomAccessFile(FOLDER + "copyFromHello.txt", "r").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(16);
fileChannel.read(buffer);
buffer.flip();
while (buffer.hasRemaining())
System.out.print((char) buffer.get());
// 运行结果:
// Hello WordSom
}
5.3 内存映射文件
内存映射文件允许我们创建和修改那些因为太大而不能放入内存的文件。有了内存映射文件,我们就可以假定整个文件都放在内存中,并且可以完全把它当作非常大的数组来访问。
@Test
public void test3() throws IOException {
int length = 0x8FFFFFF; //128M
MappedByteBuffer out = new RandomAccessFile(FOLDER + "bigTxt.txt", "rw").getChannel()
.map(FileChannel.MapMode.READ_WRITE, 0, length);
for (int i = 0; i < length; i++)
out.put((byte)'x');
System.out.println("完成输入");
for (int i = length / 2; i < length / 2 + 6; i++)
System.out.print((char)out.get(i));
System.out.println();
// 运行结果:
// 完成输入
// xxxxxx
}
尽管“旧”的I/O流在用nio实现后性能有所提高,但是“映射文件访问”往往可以更加显著地加快速度。