乐趣区

关于java:一文搞懂IO流

前言

io 流在 Java 的常识体系中,属于很杂的那种了,次要是波及的类太多,输出流、输入流、文件输出流、文件输入流、字节输出流和输入流、字符输出流和输入流等等,常常把人弄晕。然而它的用处却十分宽泛,文件的操作、网络的操作等都离不开它,本文通过平时罕用操作来梳理一下 io 流中波及的重要类及办法。

什么是流

在 Java 中,文件个别不是独自解决的,而是视为输入输出设施的一种,Java 应用一个对立的概念来解决所有的输入输出,包含键盘输入和网络输入输出等。这个对立的概念就是 ,流又分为输出流和输入流,InputStream 和 OutputStream 别离示意输出流和输入流,但这两个类是抽象类,大部分办法都是子类来实现的,如:

  1. BufferedInputStream 和 BufferedOutputStream 对流起缓冲作用
  2. DataInputStream 和 DataOutputStream 能够按 8 种根本类型和字符串类型对流进行读写
  3. ZipInputStream 和 ZipOutputStream 能够对流进行压缩和解压缩
  4. printStream 能够对根本类型和对象输入为其字符串示意

以 InputStream 和 OutputStream 为基类的流根本都是以二进制模式解决数据的,不可能不便地解决文本文件,没有编码的概念,可能不便地解决文本数据的基类是 Reader 和 Writer,它也有很多子类,如:

  1. FileReader 和 FilerWriter 用来读写文件
  2. BufferedReader 和 BufferedWriter 起缓存装璜
  3. CharArrayReader 和 CharArrayWriter 能够将字符数组包装为 Reader 和 Writer
  4. StringReader 和 StringWriter 能够将字符串包装为 Reader 和 Writer
  5. InputStreamReader 和 OutputStreamWriter 能够将 InputStream 和 OutputStream 转换为 Reader 和 Writer
  6. printWriter 能够将根本类型和对象输入为其字符串示意

二进制文件和字节流

二进制文件:所有文件外面的数据都是通过二进制来保留的,但为了不便解决数据,引入了文件类型的概念,次要分为文本文件和二进制文件。文本文件是指那些文本编辑器能够间接查看和编辑的文件,如.txt、.html、.java 等文件;二进制文件是指一些非凡格局和性能的文件,如.zip、.jpg、.doc、.pdf 等文件

Java 次要通过 InputStream 和 OutputStream 来实现二进制字节模式解决文件。

OutputStream

OutputStream 次要有上面几个办法:

public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException
public void close() throws IOException
public void flush() throws IOException

write(int b)办法向流中写入一个字节,须要子类来实现。

write(byte b[])办法外面调用 write(byte b[], int off, int len) 办法来实现,将 b[]数组从第 off 地位开始,len 长度的数据写入到流中。

close()敞开流,下面几个办法就算流中的数据都操作完了,也会阻塞,期待流中持续增加数据,因而咱们操作完数据当前,须要在 finally 办法中通过 close()办法来敞开流。

flush()将缓冲而未理论写入的数据进行理论写入,个别作用于带缓冲的流中,如 BufferedOutputStream。

举例

OutputStream outputStream = null;
try {outputStream = new FileOutputStream("hello.txt");
    String data = "hello world";
    outputStream.write(data.getBytes());
} catch (Exception e) {e.printStackTrace();
} finally {outputStream.close();
}

InputStream

InputStream 次要有上面几个办法:

public abstract int read() throws IOException;
public int read(byte b[]) throws IOException 
public int read(byte b[], int off, int len) throws IOException
public void close() throws IOException

read()办法从流中读取下一个字节,须要子类来实现,返回这个字节的值,当读到流结尾时返回 -1。

read(byte b[])外面也是调用 read(byte b[], int off, int len) 来实现,从流的第 off 地位开始,读取 len 长度放入 b[]数组中。

close()敞开流,同 OutputStream。

举例:

InputStream inputStream = null;
try {inputStream = new FileInputStream("hello.txt");
    byte[] arr = new byte[1024];
    inputStream.read(arr);
    System.out.println(new String(arr));
} catch (Exception e) {e.printStackTrace();
} finally {inputStream.close();
}

首先构建一个须要读取的文件的输出流,而后通过 read(byte b[]) 办法将流中的数据读取到字节数组中,这样就简略实现了一个读取文件的性能。但这里有一个前提,文件中的字节长度不超过 1024,那如果超过这个长度,咱们怎么样能力将流中数据读取到一个字节数组中呢?

能够借助 ByteArrayOutputStream。通过名字能够看出是将流中数据输入到字节数组中。

InputStream inputStream = null;
try {inputStream = new FileInputStream("hello.txt");
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    byte[] arr = new byte[4];
    int len;
    while ((len = inputStream.read(arr)) > -1) {byteArrayOutputStream.write(arr, 0, len);
    }
    String data = byteArrayOutputStream.toString();
    System.out.println(data);
} catch (Exception e) {e.printStackTrace();
} finally {inputStream.close();
}

ByteArrayOutputStream 的输入指标是 byte 数组,这个数组的长度是依据数据内容动静扩大的,当调用 write 办法的过程中,如果数组大小不够,会进行扩大,每次扩大长度翻倍。

BufferedInputStream 和 BufferedOutputStream

FileInputStream 和 FileOutputStream 是没有缓冲的,按单个字节读写时性能比拟低,尽管能够按字节数组读取来进步性能,但有时必须按字节读写,这时就须要 将文件流包装到缓冲流中来进步性能。

应用办法只有在对应的流外层包上对应的缓冲类就行了,如下:

BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("hello.txt"));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("hello.txt"));

文本文件和字符流

下面介绍了如何以字节流的形式解决文件,对于文本文件,字节流没有编码的概念,不能按行解决,应用不太不便,更适宜用字符流来解决。

字符流和字节流相似,也是有两个基类,Reader 和 Writer。Reader 和 InputStream 相似,Writer 和 OutputStream 相似,具体的操作都是通过子类来实现。

InputStreamReader 和 OutputStreamWriter

InputStreamReader 能够将 InputStream 转换为 Reader,构造方法如下:

public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in, String charsetName)

一个重要的参数是编码类型,对于字符流来说,编码是很重要的,雷同二进制内容,不同编码状况下,可能会呈现不同后果。通过传入输出流和编码类型来结构出字符输出流,如果不传编码类型,则为零碎默认编码,默认编码能够通过 Charset.defaultCharset()失去

应用举例

Reader reader = null;
try {reader = new InputStreamReader(new FileInputStream("hello.txt"));
    char[] chars = new char[1024];
    reader.read(chars);
    System.out.println(new String(chars));
}finally {reader.close();
}

将文件中数据读取到内存中,这段代码一次 reader 就读取了所有的内容,前提是文件内容不超过数组长度,如果文件内容很长,能够应用 CharArrayWriter 或 StringWriter,前面会介绍。

OutputStreamWriter 能够将 OutputStream 转换为 Writer,构造方法如下:

public OutputStreamWriter(OutputStream out)
public OutputStreamWriter(OutputStream out, String charsetName)

构造方法和 InputStreamReader 相似

应用举例

Writer writer = null;
try {writer = new OutputStreamWriter(new FileOutputStream("hello.txt"),"UTF-8");
    writer.write("hello pipi 蛋");
}finally {writer.close();
}

InputStreamReader 和 OutputStreamWriter 外部都有一个类型为 StreamDecoder 的解码器,能将字节依据编码转换为 char。

CharArrayReader 和 CharArrayWriter

CharArrayWriter 与 ByteArrayOutputStream 相似,它的输入指标是 char 数组,这个数组长度能够依据内容动静扩大。

下面通过字符流读取文件内容的代码,咱们能够优化一下,如下:

Reader reader = null;
try {reader = new InputStreamReader(new FileInputStream("hello.txt"));
    CharArrayWriter arrayWriter = new CharArrayWriter();
    char[] chars = new char[1024];
    int len = 0;
    while ((len = reader.read(chars))> -1) {arrayWriter.write(chars, 0, len);
    }
    System.out.println(arrayWriter.toString());
}finally {reader.close();
}

StringReader 和 StringWriter 与 CharArrayReader 和 CharArrayWriter 相似,只是输入指标是 StringBuffer,而且 String 和 StringBuffer 外部都是由 char 数组组成的,所以他们实质上是一样的。

BufferedReader 和 BufferedWriter

BufferedReader/BufferedWriter 与 BufferedInputStream/BufferedOutputStream 相似,提供缓冲性能,只须要在外层包装一层缓冲类就行了,而且能够按行读写。

按行写入举例:

BufferedWriter writer = null;
try {writer = new BufferedWriter(new FileWriter("hello.txt"));
    writer.write("第一行");
    writer.newLine();
    writer.write("第二行");
    writer.newLine();}finally {writer.close();
}

按行读取举例:

BufferedReader reader = null;
try {reader = new BufferedReader(new FileReader("hello.txt"));
    String s = null;
    while ((s = reader.readLine()) != null) {System.out.println(s);
    }
}finally {reader.close();
}

PrintWriter

PrintWriter 有很多构造方法,能够承受文件路径名、文件对象、OutputStream、Writer 等。PrintWriter 是一个十分不便的类,能够间接指定文件名作为参数,能够指定编码类型,能够主动缓冲,能够字段将多种类型转换为字符串,在输入到文件时,能够优先选择该类

下面按行写入的代码,能够应用 PrintWriter 写为:

PrintWriter printWriter = null;
try {printWriter = new PrintWriter("hello.txt");
    printWriter.println("第一行");
    printWriter.println("第二行");
    printWriter.println("第三行");
}finally {printWriter.close();
}

能够看出 printWriter 相对而言,不便很多。

封装办法

字节流

复制 InputSteam 到 OutputStream

public void copy(InputStream in, OutputStream out) throws Exception{byte[] arr = new byte[1024];
    int len = 0;
    while ((len = in.read(arr)) > -1) {out.write(arr, 0, len);
    }
}

将文件内容读取到字节数组中

public byte[] file2Array(String fileName) throws Exception{
    InputStream in = null;
    try {in = new FileInputStream(fileName);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        copy(in,out);
        return out.toByteArray();}finally {in.close();
    }
}

再将字节数组写入到文件中

public void array2File(String fileName,byte[] bytes) throws Exception{
    OutputStream out = null;
    try {out = new FileOutputStream(fileName);
        out.write(bytes);
    }finally {out.close();
    }
}

字符流

复制 Reader 到 Writer 中

public void copy(Reader reader, Writer writer) throws Exception{char[] chars = new char[1024];
    int len = 0;
    while ((len = reader.read(chars)) > -1) {writer.write(chars, 0, len);
    }
}

将文件内容读入到一个字符串中

public String file2String(String fileName,String encoding) throws Exception {
    BufferedReader reader = null;
    try {reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), encoding));
        StringWriter writer = new StringWriter();
        copy(reader,writer);
        return writer.toString();}finally {reader.close();
    }
}

将字符串内容写入到文件中

public void string2File(String fileName,String encoding,String data) throws Exception{
    Writer writer = null;
    try {writer = new OutputStreamWriter(new FileOutputStream(fileName),encoding);
        writer.write(data);
    }finally {writer.close();
    }
}

按行写入到文件中

public void writeLines(String fileName, String encoding, Collection lines) throws Exception{
    PrintWriter writer = null;
    try {writer = new PrintWriter(fileName,encoding);
        for (Object line : lines) {writer.println(line);
        }
    }finally {writer.close();
    }
}

按行读取文件中内容,返回内容汇合

public List<String> readLines(String fileName,String encoding)throws Exception {
    BufferedReader reader = null;
    try {reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),encoding));
        List<String> list = new ArrayList<>();
        String line = null;
        while ((line = reader.readLine()) != null) {list.add(line);
        }
        return list;
    }finally {reader.close();
    }
}

总结

咱们平时开发的时候对文件或者网络内容应用流操作时,可能都是应用第三方类库,比方 Apache 有一个类库 Commons IO,外面提供了很多简略易用的办法,当然应用这些类库一点故障都没有,然而,如果有些时候不能应用第三方类库的时候,比方开发一些通用的 jar 包时,必定越少依赖越好,这时候就要相熟 jdk 中的一些罕用 io 操作了。

退出移动版