Java之IO流

一、流的定义

流是个抽象的概念,java中对于数据的输入/输出都是以“流”的方式进行;
流具有方向性,程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流。
作用:为数据源和目的地建立一个输送通道。

1.1 流的分类

1.1.1 根据流的方向

  • 输入流:

从别的地方(本地文件,网络上的资源等)获取资源 输入到 我们的程序中;

  • 输出流

从我们的程序中 输出到 别的地方(本地文件), 将一个字符串保存到本地文件中,就需要使用输出流;

1.1.2 根据处理的数据单位

  • 字符流:

每次输入/输出两个字节(Byte)的,使用该流可以正确传输显示中文;

  • 字节流

每次输入/输出一个字节,但传输中有中文时,会出现乱码;

1.1.3 根据功能

  • 节点流:

以从或向一个特定的地方(节点)读写数据。如FileInputStream

  • 处理流

是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装;

1.2 抽象流类型

Java所有的流类位于java.io包中,都分别继承字以下四种抽象流类型。

输入流输出流
字符流ReaderWriter
字节流InputStreamOutStream
  1. 继承自Reader/Writer的流都是用于向程序中输入/输出数据,且数据的单位都是字符(2byte=16bit),如图,深色的为节点流,浅色的为处理流。
    在这里插入图片描述

  2. 继承自InputStream/OutputStream的流都是用于向程序中输入/输出数据,且数据的单位都是字节(byte=8bit),如图,深色的为节点流,浅色的为处理流。
    在这里插入图片描述

1.3 IO流的特性

1、先进先出
最先写入输出流的数据最先被输入流读取到。

2、顺序存取
可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile可以从文件的任意位置进行存取(输入输出)操作)

3、功能唯一
每个流只能是输入流或输出流的一种,不能同时具备两个功能,。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

二、IO流对象

IO体系的基类为:InputStream/Reader、OutputStream/Writer

字节流和字符流的操作方式基本一致,只是操作的数据单元不同——字节流的操作单元是字节,字符流的操作单元是字符。所以字节流和字符流就整理在一起了。

2.1 File

对指定目录的文件进行操作(该类虽然是在IO包下,但是并不继承自四大基础类);

2.2 InputStream

字节输入流的所有类的超类(抽象类)。
在这里插入图片描述

ByteArrayInputStream:

字节数组输入流,该类的功能就是从字节数组(byte[])中进行以字节为单位的读取,将资源文件都以字节的形式存入到该类中的字节数组中;

PipedInputStream

管道字节输入流,该类用于实现多线程间的管道通信,常常和PipedOutputStream一起使用;

FilterInputStream

用来封装其他的输入类,提供额外的功能,常用的输入类为:BufferInputStream和DataInputStream;

BufferInputStream

缓冲字节输入流,FilterInputStream流的派生类,提供优化读取方式的功能,提高读取效率。

读取方式读取效率
基础输入流逐字节读取
BufferInputStream先读取到缓存流(内存中),然后一次性从内存中读取多个字符

DataInputStream

数据字节输入流,允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型

FileInputStream

文件输入类,通常对文件进行读取操作;

ObjectInputStream

对象输入流,用来提供对“基本数据或对象”的持久存储,即直接传输对象;(反序列化中使用

2.3 OutputStream

输出字节流的所有类的超类(抽象类);
在这里插入图片描述
ByteArrayOutputStream

字节数组输出流,向Byte数组写入数据;

PipedOutputStream

管道字节输出流,向与其它线程共用的管道中写入数据;

FilterOutputStream

用来封装其他的输出类,提供额外的功能,常用的输入类为:BufferOutputStream和DataOutputStream、PrintStream;

BufferOutputStream

字节缓冲输出流,提高输出效率;

DataOutputStream

数据字节输出流,允许应用程序以与机器无关方式将Java基本数据类型写到底层输出流。

PrintStream

为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。值得注意的是:

  • PrintStream流永远不会抛出异常;
  • 有自动刷新机制,例如当向PrintStream流中写入一个字节数组后自动调用flush()方法.

FileOutputStream

文件字节输出流,向本地文件写入数据;

ObjectOutputStream

对象输出流,将一个对象写出(序列化中使用);

2.4 Reader

字符输入流的所有类的超类(抽象类)。
在这里插入图片描述

CharArrayReader

字符数组输入流,从char数组中读取数据;

PipedReader

管道字符输入流,从与其它线程共用的管道中读取数据;

FilterReader

所有自定义具体字符装饰流的父类;

BufferReader

缓冲字符流,提到读取效率;

InputStreamReader

InputStreamReader是连接字节流和字符流的桥梁,它将字节流转变为字符流。

FileReader

文件字符输入流,对本地文件进行读取操作,是一个常用的工具类;

2.5 Writer

字符输出流的所有类的超类(抽象类)
在这里插入图片描述

CharArrayWriter

字符数组输出流,向char数组中写入数据;

PipedWriter

管道字符输出流,向与其它线程共用的管道中写入数据;

FilterWriter

所有自定义具体字符装饰流的父类;

BufferWriter

缓冲字符流,为Writer 提供缓冲功能;

OutputStreamWriter

OutputStreamWriter是从字符流到字节流的桥梁。,它将字节流转变为字符流。

FileWriter

文件字符输出流,将数据写入到本地文件中,是一个常用的工具类;

三、IO流的方法

3.1 输入流(InputStream/Reader)

InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。

在InputStream里包含以下三个方法:

  1. int read()

从输入流中读取数据的下一个字节。

  1. int read(byte[] b)

从输入流中读取一定数量的字节,并将其存储到缓冲数组b中

  1. nt read(byte[] b,int off,int len)

从输入流中最多读取len个字节的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。

  1. void close()

关闭此输入流并释放与该流关联的所有系统资源。

示例:

public class InputStreamDemo{
    public static void read() throws IOException{//抛出异常不处理
        //使用File类找到源文件
        File file = new File("C:\\Users\\ss\\Desktop\\test.txt");
        //通过子类实例化
        InputStream input = new FileInputStream(file);
        //所有内容存贮在数组中
        byte[] b = new byte[1024];
        //进行读操作
        input.read(b);
        //关闭流
        input.close();
        System.out.println("内容为:"+new String(b));
    }
}

程序里面打开的文件IO资源不属于内存的资源,垃圾回收机制无法回收该资源,所以应该显示地关闭打开的IO资源。Java 7改写了所有的IO资源类,它们都实现了AntoCloseable接口,因此都可以通过自动关闭资源的try语句来关闭这些Io流。

在Reader中包含以下三个方法:

  1. int read()

从输入流中读取单个字符,返回所读取的字符数据(字节数据可直接转换为int类型)。

  1. int read(char[] b)

从输入流中最多读取b.length个字符的数据,并将其存储在字节数组b中,返回实际读取的字符数。

  1. int read(char[] b,int off,int len)

从输入流中最多读取len个字符的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。

示例如下:

public static void read() throws IOException{
	/*********************逐个字符读取文件*************************/
	 FileReader fileReader1 = null;
     try{
         //创建字符输入流
         fileReader1 = new FileReader("C:\\Users\\ss\\Desktop\\test.txt");
         //字符存储
         int ch = fileReader1.read();
         //逐字符读取
         while (ch!=-1){
             System.out.println((char)ch);
             ch = fileReader1.read();
         }
     }catch (Exception e){
         e.printStackTrace();
     }finally {
         fileReader1.close();
     }
	/*********************使用数组读取本地文件*************************/
    FileReader fileReader2 = null;
    try{
        //创建字符输入流
        fileReader2 = new FileReader("C:\\Users\\ss\\Desktop\\test.txt");
        //创建一个用来存储的字符数组
        char c[] = new char[1024];
        int num = 0;
        //循环读取内容
        while((num=fileReader2.read(c))!=-1){
            //取出字符,将字符数组转换成字符串输出
            System.out.println(new String(c,0,num));
        }
    }catch (IOException e){
        e.printStackTrace();
    }finally {
        fileReader2.close();
    }

	/*********************用缓冲区读取文本文件*************************/
	FileReader fileReader3 = null;
    BufferedReader bufferedReader = null;
    try{
        fileReader3 = new FileReader("C:\\Users\\ss\\Desktop\\test.txt");
        bufferedReader = new BufferedReader(fileReader3);
        String line = null;
        //BufferedReader提供了按行读取文本文件的方法readLine()
        //readLine()返回行有效数据,不包含换行符,未读取到数据返回null
        while ((line=bufferedReader.readLine())!=null){
            System.out.println(line);
        }

    }catch (Exception e){
        e.printStackTrace();
    }finally {
        bufferedReader.close();
    }
}

3.2 输出流(OutputStream和Writer)

OutputStream和Writer的用法也非常相似,两个流都提供了如下三个方法::

  1. void write(int c)

将指定的字节/字符输出到输出流中,其中c即可以代表字节,也可以代表字符。

  1. void write(byte[]/char[] buf)

将字节数组/字符数组中的数据输出到指定输出流中。

  1. void write(byte[]/char[] buf, int off,int len )

将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。

因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里面还包含如下两个方法:

  1. void write(String str)

将str字符串里包含的字符输出到指定输出流中.

  1. void write (String str, int off, int len)

将str字符串里面从off位置开始,长度为len的字符输出到指定输出流中。

OutputStream示例如下:

public static void write() throws IOException {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            //创建字节输入流
            fileInputStream = new FileInputStream("C:\\Users\\ss\\Desktop\\test.txt");
            //创建字节输出流
            fileOutputStream = new FileOutputStream("C:\\Users\\ss\\Desktop\\test2.txt");

            byte[] b = new byte[1024];
            int hasread = 0;
            //循环读取数据
            while ((hasread=fileInputStream.read(b))>0){
                //每次读多少,写多少
                fileOutputStream.write(b,0,hasread);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            fileInputStream.close();
            fileOutputStream.close();
        }
    }

使用java的io流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。java很多输出流默认都提供了缓存功能,其实我们没有必要刻意去记忆哪些流有缓存功能,哪些流没有,只有正常关闭所有的输出流即可保证程序正常。

Writer示例如下:

public static void write() throws IOException {
        File file = null;
        FileWriter fileWriter = null;
        try{
            file = new File("C:\\Users\\ss\\Desktop\\test2.txt");
            fileWriter = new FileWriter(file);
            fileWriter.write("这是输出的内容");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            fileWriter.close();
        }
    }

四、字节流与字符流的区别

类型字节流字符流
处理单位字节(8bit)字符(一次可读写多个字符)
处理对象所有类型的数据(如视频、图片)字符类型的数据
有无缓冲区

缓冲区的有无对IO流的影响如下:

  • 字节流:不需要调用close()方法,就可以输出信息;
  • 字符流:只要调用close()方法时关闭缓冲区时,才会输出信息;如果想要关闭关闭缓冲区前输出信息,就需要手动调用flush()方法;

五、转换流

IO流中提供了两种用于将字节流转换为字符流的转换流InputStreamOutputStream;使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集。

5.1 InputStreamReader

用于将字节输入流转换为字符输入流。
InputStreamReader含有方法如下:

  1. close():关闭此流;
  2. getEncoding():获取此流使用的字符编码的名称;
  3. ready():判断此流是否已经准备好用于读取;
  4. read():读取单个字符。;
  5. read(char[] c,int offset ,int length):将字符读入数组的某一部分;

创建InputStreamReader时如果不指定字符集,就使用默认字符集创建InputStreamReader。转换后可以不关闭InputStream,示例如下:

public static void read() throws IOException{
   InputStreamReader inputStreamReader = null;
   try {
       //使用File类找到源文件
       File file = new File("C:\\Users\\ss\\Desktop\\test2.txt");
       //通过FileInputStream实例化
       InputStream inputStream = new FileInputStream(file);
       //指定字符集转换字节流
       inputStreamReader  = new InputStreamReader(inputStream,"UTF-8");
       //使用字符数组存储数据
       char[] c= new char[1024];
       int count = 0;
       while((count=inputStreamReader.read(c,0,c.length))!=-1){
           System.out.println(new String(c,0,c.length));
       }
   }catch (Exception e){
       e.printStackTrace();
   }finally {
       if(inputStreamReader!=null){
           inputStreamReader.close();
       }
   }
}

5.2 OutputStreamWriter

用于将字节输出流转换为字符输出流。
OutputStreamWriter含有方法如下:

  1. flsh():刷新该流的缓冲;
  2. close():关闭此流,关闭前需要刷新;
  3. getEncoding():获取此流使用的字符编码的名称;
  4. write():write(char[] c ,int offset ,int length),写入字符数组的某一部分;
  5. write(String str,int offset ,int length):写入字符串的某一部分;
  6. write(String str):写入字符传;

创建OutputStreamWriter如果不指定字符集,就使用默认字符集创建OutputStreamWriter。转换之后可以不用关闭OutputStream,示例如下:

public static void writer(){
    try{
        String str = "八月中秋白露,路上行人凄凉。 小桥流水桂花香,日夜千思万想。 心中不得宁静,清早览罢文章。 十年寒苦在书房,方显才高志广。";
        //使用File类找到源文件
        File file = new File("C:\\Users\\ss\\Desktop\\test2.txt");
        //通过FileOutputStream实例化
        OutputStream outputStream = new FileOutputStream(file);
        //使用指定字符集转换字节流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //写入数据
        outputStreamWriter.write(str);
        outputStreamWriter.flush();
        outputStreamWriter.close();
    }catch (Exception e){
        e.printStackTrace();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值