1、io的基本概念

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

2、io流的分类

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

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

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

IO编程,你真的懂么?_缓存

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

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

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

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

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

IO编程,你真的懂么?_java_03

3.1四个抽象类(基类)

InputStrem 「字节」为单位的「输入流」
OutputStream 「字节」为单位的「输出流」
Reader 「字符」为单位的「输入流」
Writer 「字符」为单位的「输出流」
IO编程,你真的懂么?_输出流_04IO编程,你真的懂么?_数据_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();
        }
    }
}