1、io的基本概念

I/O操作指的是进行输入、输出的操作,输入/输出操作都是基于「数据流」的,这些数据流表示了字节或字符流动的序列。Java中的I/O流提供了读写数据的标准方法,任何表示数据源的对象都会以数据流的方式读写它的数据方法。java把用于操作输入/输出的类放在java.io包中。

2、io流的分类

「按照流的方向分,可以分为输入流和输出流」

  • 输入流:从外部读入数据到内存,外部可以是磁盘、网络等。
  • 输出流:将数据从内存写出到外部。

NOTE:我们这里的输入/输出可以从程序运行所在内存的角度来看,输入/输出的方向是单向的。

IO编程,你真的懂么?_java

「按照数据流的类型,可以分为字节流和字符流」

字节流和字符流的不同之处在于操作数据单元的大小不同,字节流操作的数据单元是8位的字节,字符流操作的数据单元是16位的字符。

「按处理流的方式,可以分为节点流和处理流。」

  • 节点流:也可被称为低级流,它和数据源直接对接
  • 高级流:建立在低级流的基础上,对已存在的流进行连接和封装,通过封装后的流实现数据的读写功能。
  • IO编程,你真的懂么?_java_02

3、Java中常用的IO类的实现及使用

IO编程,你真的懂么?_java_03

3.1四个抽象类(基类)

InputStrem「字节」为单位的「输入流」
OutputStream「字节」为单位的「输出流」
Reader「字符」为单位的「输入流」
Writer「字符」为单位的「输出流」

IO编程,你真的懂么?_java_04IO编程,你真的懂么?_java_05

这四个抽象类是所有输入/输出流的基类,它们中定义的方法用于操作数据流的传输

  • InputStream和Reader

read():从输入流中读取单个字节/字符,并返回读取的字节/字符数据

read(byte/char b[]):从输入流中最多读取b.length个字节/字符,将其存储在数组b中,并返回读取的字节/字符数据

read(byte/char b[], int off, int len):从输入流中最多读取len字节/字符,将其存储在数组b中,并返回读取的字节/字符数据,读取字节/字符的起点从off开始

「以InputStream为例,具体代码如下」

/**
*从输入流中读取单个字节,并返回读取的字节数据
*/

public abstract int read();
/**
*从输入流中最多读取b.length个字节,将其存储在数组b中,并返回读取的字节数据
*/

public int read(byte b[]) throws IOException {
       return read(b, 0, b.length);
   }
/**
*从输入流中最多读取len字节,将其存储在数组b中,并返回读取的字节数据,读取字节的起点从off开始
*/

   public int read(byte b[], int off, int len) throws IOException {
       if (b == null) {
           throw new NullPointerException();
       } else if (off < 0 || len < 0 || len > b.length - off) {
           throw new IndexOutOfBoundsException();
       } else if (len == 0) {
           return 0;
       }

       int c = read();
       if (c == -1) {
           return -1;
       }
       b[off] = (byte)c;

       int i = 1;
       try {
           for (; i < len ; i++) {
               c = read();
               if (c == -1) {
                   break;
               }
               b[off + i] = (byte)c;
           }
       } catch (IOException ee) {
       }
       return i;
   }

Reader内的方法和InputStream内的方法类似,只是将「byte换成了char」

  • OutputStream和Writer

write(int b):将指定的字节/字符输出到输出流中

write(byte/char b[]):将字节/字符数组中的数据输出到输出流中

write(byte/char b[], int off, int len):将起始点为off,长度为len的字节/字符数组中的数据输出到输出流中。

以OutPutStream为例

 public abstract void write(int b);
public void write(byte b[]) throws IOException {
       write(b, 0, b.length);
   }
public void write(byte b[], int off, int len) throws IOException {
       if (b == null) {
           throw new NullPointerException();
       } else if ((off < 0) || (off > b.length) || (len < 0) ||
                  ((off + len) > b.length) || ((off + len) < 0)) {
           throw new IndexOutOfBoundsException();
       } else if (len == 0) {
           return;
       }
       for (int i = 0 ; i < len ; i++) {
           write(b[off + i]);
       }
   }

3.2输入流的使用

FileInputStream文件输入流,用于从文件系统中获得输入字节
FileReader用于文件的读取,获取字符流

以FileInputStream为例,FileReader的实现和FileInputStream相同,只是操作单位不同

public class testInput {
   public static void main(String[] args) throws Exception{
       FileInputStream fis=null;
       try {
           //创建字节输入流
           fis=new FileInputStream("F:\\学习笔记\\Simon学习笔记\\java\\test.txt");
           //创建一个长度为1024的byte数组
           byte[] b=new byte[1024];
           //用于保存的实际字节数
           int count=0;
           //重复读取,直到去完为止
           while ((count=fis.read(b))>0){
               //转成字符串进行输出
               System.out.println(new String(b,0,count));
           }
       }catch (IOException e){
          e.printStackTrace();
       }finally {
           fis.close();
       }

   }
}

「Note」:字节读取结束的条件是方法read方法返回了-1;所以我们可以用(count=fis.read(b))>0进行判断,

「因为IO不属于内存资源,需要手动关闭。但是java7引入了try(resource)的语法,只需要编写try语句,就可以让编译器自动的为我们关闭资源」

「所以代码可以改写为」

try( fis=new FileInputStream("F:\\学习笔记\\Simon学习笔记\\java\\test.txt")) {
           //创建一个长度为1024的byte数组
           byte[] b=new byte[1024];
           //用于保存的实际字节数
           int count=0;
           //重复读取,直到去完为止
           while ((count=fis.read(b))>0){
               //转成字符串进行输出
               System.out.println(new String(b,0,count));
           }//编译器在此自动为我们写入finally并调用close

3.3输出流的使用

FileOutputStream文件输出流,用于将数据写入File的输出流,字节流
FileWriter用于对文件的写入操作,字符流

以FileOutputStream为例,同理,FileWriter也是一样,只是操作单位不一样。

public class testOutPut {
   public static void main(String[] args) throws IOException {
       FileInputStream fis=null;
       FileOutputStream fos=null;
       try {
           //创建字节输入流
           fis=new FileInputStream("F:\\学习笔记\\Simon学习笔记\\java\\test.txt");
           //创建字符
           fos=new FileOutputStream("F:\\学习笔记\\Simon学习笔记\\java\\test2.txt");
           byte[] b=new byte[1024];
           int count=0;
           while((count=fis.read(b))>0){
               fos.write(b,0,count);
           }
       }catch (IOException e){
           e.printStackTrace();
       }finally {
           fis.close();
           fos.close();

       }
   }
}

3.4缓存流的使用

为什么需要缓存流:

一块缓存区可以理解为块操作,如今处理器支持按照块来读取数据,这会使得读取次数变少,进而提高性能,除此之外,它还可以采用完全相同的输入/输出代码来访问不同的数据源。

BufferedInputStream为输入流提供缓冲功能
BufferedOutputStream「输出流」提供缓冲功能
BufferedReader「输入流」提供缓冲功能
BufferedWriter「输出流」提供缓冲功能
public class testBuffer {
   public static void main(String[] args)throws IOException {
       FileInputStream fis=null;
       FileOutputStream fos=null;
       BufferedInputStream bis=null;
       BufferedOutputStream bos=null;
       try {
           //创建字节输入流
           fis=new FileInputStream("F:\\学习笔记\\Simon学习笔记\\java\\test.txt");
           //创建字节输出流
           fos=new FileOutputStream("F:\\学习笔记\\Simon学习笔记\\java\\test3.txt");
           //创建字节缓存输入流
           bis=new BufferedInputStream(fis);
           //创建字节缓存输出流
           bos=new BufferedOutputStream(fos);

           byte[] b=new byte[1024];
           int count=0;
           //循环从缓存流中读取数据
           while((count=bis.read(b))>0){
               //向缓存流中写入数据,读取多少写入多少
               bos.write(b,0,count);
           }
       }catch (IOException e){
           e.printStackTrace();
       }finally {

           bis.close();
           bos.close();
       }
   }
}