1 输入和输出
Java类库中的I/O类分成输入和输出两部分,通过叠合多个对象来提供所期望的功能(装饰者设计模式)。
1.1 InputStream类型
InputStream的作用是用来表示那些从不同数据源产生输入的类。这些数据源包括:字节数组、String对象、文件、管道、一个由其他种类的流组成的序列、其他数据源等。
常用的类:
- FileInputStream:用于从文件中读取信息,构造器参数是字符串,表示文件名、文件或者FileDescriptor对象。
//可以使用字符串类型的文件名来创建一个输入流对象来读取文件
InputStream fin = new FileInputStream("C:\java\hello.class");
//也可以使用一个文件对象来创建一个输入流对象来读取文件
File f = new File("C:\java\hello.class");
InputStream fin2 = new FileInputStream(f);
1.2 OutputStream类型
该类别的类决定了输出所要去的目标:字节数组、文件或者管道。
常用的类:
- FileOutoutStream:用于将信息写至文件,构造器参数是字符串,表示文件名、文件或者FileDescriptor对象。如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
import java.io.*;
public class fileStreamTest {
public static void main(String args[]) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
2 Reader和Writer
Java 1.1对基本的I/O流类库进行了重大的修改,Reader和Writer这两个类不是用来替代上述的InputStream和OutputStream的类。它们面向的对象不同,Stream在以面向字节形式的I/O中仍然可以提供极有价值的功能,Reader和Writer则提供兼容Unicode与面向字符I/O功能。
有时我们必须把来自于“字节”层次结构中的类和“字符”层次结构中的类结合起来使用,为了实现这个目的,要用到适配器(adapter)类:InputStreamWriter和OutputStreamWriter。
设计Reader和Writer继承层次结构主要是为了国际化。老的I/O流继承层次结构仅支持8位字节流,并且不能很好地处理16位的Unicode字符。由于Unicode用于字符国际化,所以添加Reader和Writer继承层次的结构就是为了在所有的I/O操作中都支持Unicode。另外,新类库的设计使得它的操作比旧类库更快。
3 NIO
3.1 什么是NIO?
JDK 1.4的java.nio.*包中引入了新的Java I/O类库,其目的在于提高速度。实际上,旧的I/O包已经用nio重新实现过,以便充分利用这种速度提高,因此,即使我们不显式地使用nio编程,也能从中受益。
速度的提高来自于所使用的结构更接近于操作系统执行I/O的方式:通道和缓冲器。
NIO主要有三大核心部分:Channel,Buffer,Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer进行。数据总是从通道读取到缓冲区,或者从缓冲区写入到通道中。Selector用于监听多个通道的事件,因此,单个线程可以监听多个数据通道。
public void method(){
RandomAccessFile aFile = null;
try{
aFile = new RandomAccessFile("F:\\发票与回单\\抬头与税务号.txt","rw");
FileChannel fileChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
int bytesRead = fileChannel.read(buf);
System.out.println(bytesRead);
while(bytesRead != -1)
{
buf.flip();
while(buf.hasRemaining())
{
System.out.print((char)buf.get());
}
buf.compact();
bytesRead = fileChannel.read(buf);
}
}catch (IOException e){
e.printStackTrace();
}finally{
try{
if(aFile != null){
aFile.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
3.2 主要区别
3.3 缓冲区
在java nio中负责数据存取。缓冲区是数组。根据数据类型的不同,提供了响应的缓冲区:ByteBuffer, CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer。通过allocate() 获取缓冲区,主要方法为put() 和get() 。
四个核心属性:
- capacity:表示缓冲区中最大存储数据的容量,一旦声明不能改变,即数组大小。
- limit:缓冲区中可以操作数据的大小。(limit后面的数据不能进行读写)
- position:表示缓冲区正在操作的位置。
- mark:标记,表示记录当前position位置,可以通过reset() 回到刚才记录位置
position <= limit <= capacity
public void bufferTest() {
//1. 分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//2. 调用put方法存入数据
String ss = "7777";
buf.put(ss.getBytes());
//3. 切换取数据 flip()
buf.flip();
//4. 利用get方法读取
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst));
//5. rewind方法可重复读数据
buf.rewind();
//6. 清空缓冲区,但是缓冲区的数据易燃存在,只是指针回到初始,数据处于“被遗忘”状态。
buf.clear();
}
非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM内存中
直接缓冲区:通过allocateDirect()方法分配直接内存,将缓冲区建立在物理内存中,可以提高效率。
通过isDirect() 方法判断是否是直接缓冲区。
直接缓冲区
3.4 通道
通道用于源节点与目标节点的链接,需要配合缓冲区进行传输。通道的主要实现类:FileChannel, SocketChannel, ServerSocketChannel, DatagramChannel。
获取方式:
- getChannel()。
- 在 JDK 1.7中NIO.2 针对各个通道提供了静态方法open()。
- 在 JDK 1.7中NIO.2的Files工具类的newByteChannel()。
public void channelTest() throws IOException {
FileInputStream fis = new FileInputStream("1.jpg");
FileOutputStream fos = new FileOutputStream("2.jpg");
//1. 获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//2. 分配指定大小缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//3. 将通道数据存入缓冲区
while (inChannel.read(buf) != -1) {
buf.flip();
outChannel.write(buf);
buf.clear();
}
outChannel.close();
inChannel.close();
fos.close();
fis.close();
}
网友评论