一、传统IO流类结构设计:
传统IO设计是面向流(字节流或字符流)的,部分是有缓冲区的,对于没有缓冲区的流,为了提高效率,是需要我们自行使用缓冲区的。
ByteArrayOutputStream 内部用一个自动增长byte[]将数据保存起来,即使流被关闭,数据还是可以访问的,适合多次访问流的场景,一般输入流只能读一次。
ByteArrayInputStream 内部用一个自动增长的byte[]将数据保存起来,即使流被关闭,数据还是可以访问的,适合多次访问流的场景,一般输入流只能读一次。
BufferedOutputStream 具备缓冲能力的输出流
BufferedInputStream 具备缓冲能力的输入流
/**
* 输入流拷贝到输出流
*
* @throws Exception
*/
void testCopyStream() throws Exception {
FileInputStream fileInputStream = new FileInputStream("img/a.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("img/a2.jpg");
byte[] buf = new byte[1024];
int len;
// 这里的缓冲对输入和输出流都有作用
while ((len = fileInputStream.read(buf)) != -1) {
fileOutputStream.write(buf, 0, len);
}
fileOutputStream.flush();
fileInputStream.close();
fileOutputStream.close();
}
/**
* ByteArrayOutputStream功效,能将整个流内容存起来,后续继续构造一个输出或输入流重复使用,一般输入流只能读取一次,想重复使用需要自己保存内容
* 输入流转换成String
*
* @throws Exception
*/
void testByteArrayStream() throws Exception {
FileInputStream fileInputStream = new FileInputStream("text/a.txt");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(10240);
byte[] buf = new byte[2048];
int len;
while ((len = fileInputStream.read(buf)) != -1) {
byteArrayOutputStream.write(buf, 0, len);
}
byteArrayOutputStream.flush();
String content = byteArrayOutputStream.toString("UTF-8");
System.out.println(content);
}
void testBufferedStream() throws Exception{
FileInputStream fis = new FileInputStream("test");
BufferedInputStream bis = new BufferedInputStream(fis);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 读取bis流中的下一个字节
int c = bis.read();
while (c != -1) {
baos.write(c);
c = bis.read();
}
bis.close();
byte retArr[] = baos.toByteArray();
}
二、特立独行的RandomAccessFile
Java中有一个操作文件随机任意位置读写的类型,JDK1.0就有了,他就是RandomAccessFile。他并没有性能上的优势。
三、NIO
0、操作系统在与外部设备进行交互时,是需要发生系统中断的,此时用户进程(线程)会被挂起,内核操作完成后才会恢复用户程序,其中,用户程序是不直接与外部设备交互,用户程序和内核交互,内核与外部设备交互。
1、Java NIO中主要组件是Selector、Channel、Buffer,看到Selector就知道Java NIO的IO模型多路复用。
2、NIO的3个组件中用户编程最多的是Buffer,Buffer就是用户程序缓冲区的接口,背后他会与内核缓冲区进行数据交互,所以读、写操作就完全依赖Buffer的使用了。
3、创建Buffer时需要指定缓冲区的大小,这个大小最好比实际传输的消息大小略大一些。
4、Buffer有个flip()反转方法,用来反转Buffer的读、写状态,为什么需要这个方法?因为当你从Channel中读取数据时,对Buffer来说是写状态,Buffer被写完以后,需要从Buffer中读取到用户的业务程序中,这个时候对Buffer来说是读状态,需要调用flip()反转,所以由此看来,flip()方法一定不能少。
Channel实现,包含了文件IO和网络IO
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
Buffers实现,包含了各个数据类型
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
// 读文件
public static void readFile(File file) throws Exception{
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
FileChannel channel = randomAccessFile.getChannel();
// 传统IO用BufferedReader
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
byte[] b = buffer.array();
System.out.println(new String(b));
channel.close();
randomAccessFile.close();
}