Java io 流常用基础总结

一、java io 流概念、分类、类层次图

1、概念:

  流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。

2、分类:

    按流向分(以内存或者程序角度看):

输入流 : 把数据读取到内存或者程序中的流
输出流 : 内存或者程序中的数据写入到磁盘或者其他的存储数据的数据源的流
按数据传输单位(程序操作单元)分:
字节流 : 以字节为单位传输数据的流
字符 : 以字符为单位传输数据的流
按功能分:
节点流 : 用于直接操作目标设备的流(比如磁盘,网络)
过滤流 : 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。

3、类层次图

JDK所提供的所有流类位于java.io包中,都分别继承自以下四种抽象流类。
InputStream:继承自InputStream的流都是用于向程序中输入数据的,且数据单位都是字节(8位)。
OutputStream:继承自OutputStream的流都是程序用于向外输出数据的,且数据单位都是字节(8位)。
Reader:继承自Reader的流都是用于向程序中输入数据的,且数据单位都是字符(16位)。
Writer:继承自Writer的流都是程序用于向外输出数据的,且数据单位都是字符(16位)。

①、先来看下度娘给的一张图


这需要我们自己去验证一下到底是不是这样子的。





上面4幅图是在idea中用show digram打开,用于展示类继承和实现关系;

蓝色实线表示的是继承关系绿色虚线表示的是接口实现关系绿色实线表示的是接口与接口的关系。

抽象类InputStream(这个抽象类是表示输入字节流的所有类的超类)里方法:

intavailable()
返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。
voidclose()
关闭此输入流并释放与流相关联的任何系统资源。
voidmark(int readlimit)
标记此输入流中的当前位置。
booleanmarkSupported()
测试这个输入流是否支持 markreset方法。
abstract intread()
从输入流读取数据的下一个字节。
intread(byte[] b)
从输入流读取一些字节数,并将它们存储到缓冲区 b
intread(byte[] b, int off, int len)
从输入流读取最多 len字节的数据到一个字节数组。
voidreset()
将此流重新定位到上次在此输入流上调用 mark方法时的位置。
longskip(long n)
跳过并丢弃来自此输入流的 n字节数据。 
抽象类 Reader 用于读取字符流的抽象类。) 里主要有几个常用的方法:
abstract voidclose()
关闭流并释放与之相关联的任何系统资源。
voidmark(int readAheadLimit)
标记流中的当前位置。
booleanmarkSupported()
告诉这个流是否支持mark()操作。
intread()
读一个字符
intread(char[] cbuf)
将字符读入数组。
abstract intread(char[] cbuf, int off, int len)
将字符读入数组的一部分。
intread(CharBuffer target)
尝试将字符读入指定的字符缓冲区。
booleanready()
告诉这个流是否准备好被读取。
voidreset()
重置流。
longskip(long n)
跳过字符 

抽象类OutputStream(这个抽象类是表示字节输出流的所有类的超类)里方法:

voidclose()
关闭此输出流并释放与此流相关联的任何系统资源。
voidflush()
刷新此输出流并强制任何缓冲的输出字节被写出。
voidwrite(byte[] b)
b.length字节从指定的字节数组写入此输出流。
voidwrite(byte[] b, int off, int len)
从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
abstract voidwrite(int b)
将指定的字节写入此输出流。 

抽象类Writer(用于写入字符流的抽象类)里方法:

Writerappend(char c)
将指定的字符附加到此作者。
Writerappend(CharSequence csq)
将指定的字符序列附加到此作者。
Writerappend(CharSequence csq, int start, int end)
将指定字符序列的子序列附加到此作者。
abstract voidclose()
关闭流,先刷新。
abstract voidflush()
刷新流。
voidwrite(char[] cbuf)
写入一个字符数组。
abstract voidwrite(char[] cbuf, int off, int len)
写入字符数组的一部分。
voidwrite(int c)
写一个字符
voidwrite(String str)
写一个字符串
voidwrite(String str, int off, int len)
写一个字符串的一部分。 

在借鉴一下这位博主的表格https://blog.csdn.net/nightcurtis/article/details/51324105



二、常用的文件流基本操作演示代码:

1、FileInputStream简单读取:

public class FileInputStreamTest {

    public static void main(String[] args) throws IOException {
        getFileContentByFileInputStream();
    }

    public static void getFileContentByFileInputStream() throws IOException {
        FileInputStream fis = null;
        try{
            //创建字节输入流
            fis = new FileInputStream("F:\\file\\test.txt");
            //创建一个长度为1024字节的竹筒
            byte[] b = new byte[1024];
            //用于保存的实际字节数
            int hasRead = 0;
            //循环取水滴(字节)
            while ((hasRead = fis.read(b)) > 0){
                System.out.println(new String(b,0,hasRead));
            }
        }catch (Exception e){
            System.out.println("FileInputStreamTest_exception:" + e);
        }finally {
            if(null!=fis){
                fis.close();
            }
        }
    }

}

2、FileOutputStream简单写入:

public class FileOutputStreamTest {

    public static void main(String[] args) throws IOException {
        copyByFileStream();
    }

    /**
     * @throws IOException
     */
    public static void copyByFileStream() throws IOException {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try{
            //创建字节输入流
            fis = new FileInputStream("F:\\file\\test.txt");
            //创建字节输出流
            fos = new FileOutputStream("F:\\file\\newTest.txt");
            //创建一个长度为1024字节数组
            byte[] b = new byte[1024];
            //保存实际的字节长度
            int hasRead = 0;
            //循环取字节,一次最多拿1024个
            while ((hasRead = fis.read(b))>0){
                //每读取一次,即写入文件输入流,读了多少,就写多少
                fos.write(b,0,hasRead);
            }
            System.out.println("复制完成!");
        }catch (Exception e){

        }finally {
            if(null!=fis){
                fis.close();
            }
            if(null!=fos){
                fos.close();
            }
        }
    }
}

3、FileReader流简单读取:

public class FileReaderTest {

    public static void main(String[] args) throws IOException {
        getFileContentByFileReader();
    }

    public static void getFileContentByFileReader() throws IOException {
        FileReader fr = null;
        try {
            //创建字符输入流
            fr = new FileReader("F:\\file\\test.txt");
            //创建一个长度为1024字符的竹筒
            char[] b = new char[1024];
            //实际保存的字符数
            int hasRead = 0;
            //循环取水滴(字符)
            while ((hasRead = fr.read(b))>0){
                //取出竹筒中的水滴(字符),将字符数组转换成字符串进行输出
                System.out.println(new String(b,0,hasRead));
            }
        }catch (Exception e){
            System.out.println("");
        }finally {
            if(null!=fr){
                fr.close();
            }
        }
    }
}

4、FileWriter流简单写入:

public class FileWriterTest {

    public static void main(String[] args) throws IOException {
        copyFileByFileRead();
    }

    public static void copyFileByFileRead() throws IOException {
        FileReader fr = null;
        FileWriter fw = null;
        try{
            //创建字符输入流
            fr = new FileReader("F:\\file\\test.txt");
            //创建字符输出流
            fw = new FileWriter("F:\\file\\newTest1.txt");
            //创建一个长度为1024字符的竹筒
            char[] c = new char[1024];
            //实际保存的字符长度
            int hasRead = 0;
            //循环读取字符
            while ((hasRead = fr.read(c))>0){
                //每读取一次,即写入文件输入流,读了多少,就写多少
                fw.write(c, 0 ,hasRead);
            }
            System.out.println("复制完成!");
        }catch (Exception e){

        }finally {
            if(null!=fr){
                fr.close();
            }
            if(null!=fw){
                fw.close();
            }
        }
    }
}

5、BufferedReader流简单读取写入:

public class BufferedReaderTest {

    public static void main(String[] args) throws IOException {
        brt();
    }

    /**
     * 原来的代码是:
     * BufferedReader buffer = new BufferedReader(in);
     * 这样会出现编码的问题。在bufferedReder后面加上了转换成utf-8的方法:
     *  BufferedReader buffer = new BufferedReader(new InputStreamReader(in,"utf-8"));
     *  发现还是乱码,试了其他一些编码,最终GBK和GB2312可以成功显示中文。
     *  BufferedReader buffer = new BufferedReader(new InputStreamReader(in,"GB2312"));
     * @throws IOException
     */
    public static void brt() throws IOException {
        FileInputStream fr = null;
        FileWriter fw = null;
        BufferedReader br = null;
        BufferedWriter bw = null;
        try{
            //创建字符输入流
            fr = new FileInputStream("F:\\file\\bufferedTest.txt");
            //创建字符输出流
            fw = new FileWriter("F:\\file\\bufferedNewTest.txt");
            //创建字符缓冲区输入流
            br = new BufferedReader(new InputStreamReader(fr,"GBK"));
            //创建字符缓冲区输出流
            bw = new BufferedWriter(fw);
            //创建一个长度为1024的字符竹筒
            char[] c = new char[1024];
            //实际保存的字符长度
            int hasRead = 0;
            //循环读取
            while ((hasRead = br.read(c))>0){
                bw.write(c,0,hasRead);
            }
            System.out.println("复制完成!");
        }catch (Exception e){

        }finally {
            if(null!=br){
                br.close();
            }
            if(null!=bw){
                bw.close();
            }
            if(null!=fr){
                fr.close();
            }
            if(null!=fw){
                fw.close();
            }
        }
    }
}

6、BufferedStream流简单读取写入:

public class BufferedStreamTest {

    public static void main(String[] args) throws IOException {
        bst();
    }

    public static void bst() throws IOException {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try{
            //创建字节输入流
            fis = new FileInputStream("F:\\file\\bstTest.txt");
            //创建字节输出流
            fos = new FileOutputStream("F:\\file\\newBstTest.txt");
            //创建字节缓冲输入流
            bis = new BufferedInputStream(fis);
            //创建字节缓冲输出流
            bos = new BufferedOutputStream(fos);
            //创建一个长度为1024个字节的竹筒
            byte[] b = new byte[1024];
            //实际保存的字节数
            int hasRead = 0;
            //循环从缓冲流中读取数据
            while ((hasRead = bis.read(b))>0){
                bos.write(b,0,hasRead);
            }
            System.out.println("复制完成!");
        }catch (Exception e){

        }finally {
            //这里需要注意一个问题
            //先关闭内层流,在关闭外层流
            //BufferedInputStream、BufferedOutputStream是依赖FileInputStream、FileOutputStream
            //如果先关闭了FileInputStream、FileOutputStream,在关闭BufferedInputStream、BufferedOutputStream
            //则会报错,Steam Closed
            if(null!=bis){
                bis.close();
            }
            if(null!=bos){
                bos.close();
            }
            if(null!=fis){
                fis.close();
            }
            if(null!=fos){
                fos.close();
            }
        }
    }
}

7、转换流的使用(InputStreamReader/OutputStreamWriter):

/**
 * 转换流的使用(InputStreamReader/OutputStreamWriter):
 * 下面以获取键盘输入为例来介绍转换流的用法。java使用System.in代表输入。
 * 即键盘输入,但这个标准输入流是InputStream类的实例,
 * 使用不太方便,而且键盘输入内容都是文本内容,
 * 所以可以使用InputStreamReader将其包装成BufferedReader,
 * 利用BufferedReader的readLine()方法可以一次读取一行内容,
 * 如下代码所示:
 * Created by zw on 2018/6/11.
 */
public class InputStreamReaderTest {

    public static void main(String[] args) throws IOException {
        useInputStreamReader();
    }

    public static void useInputStreamReader() throws IOException {

        /**
         * 新语法糖try-with-resource
         * jdk1.7中try-with-resources语法糖详解:主要是针对所有凡是继承了Closeable这个类
         * 系统在方法退出的时候都会自动的关闭资源。
         * 不用最后手动关闭流
         */
        try(
            //将System.in对象转换为Reader对象
            InputStreamReader isr = new InputStreamReader(System.in);
            //将普通的Reader包装成BufferedReader
            BufferedReader bufferedReader = new BufferedReader(isr);
            //打开一个输出流
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("F:\\file\\bw.txt"))) {
            //一次读取一行的字符串内容
            String buffer;
            //循环读取
            while ((buffer = bufferedReader.readLine()) != null) {
                //打印读取的内容
                System.out.println("输入内容:" + buffer);
                //写入到新文件中
                bufferedWriter.write(buffer,0,buffer.length());
                //刷新流
                bufferedWriter.flush();
                //如果读取到的字符串为“exit”,则程序退出
                if ("exit".equals(buffer)) {
                    System.exit(1);
                }
            }
        } catch (Exception e) {
            System.out.println("exception:" + e);
        }
    }
}

三、何为NIO,和传统Io有何区别?

    我们使用InputStream从输入流中读取数据时,如果没有读取到有效的数据,程序将在此处阻塞该线程的执行。其实传统的输入里和输出流都是阻塞式的进行输入和输出。 不仅如此,传统的输入流、输出流都是通过字节的移动来处理的(即使我们不直接处理字节流,但底层实现还是依赖于字节处理),也就是说,面向流的输入和输出一次只能处理一个字节,因此面向流的输入和输出系统效率通常不高。 
    从JDk1.4开始,java提供了一系列改进的输入和输出处理的新功能,这些功能被统称为新IO(NIO)。新增了许多用于处理输入和输出的类,这些类都被放在java.nio包及其子包下,并且对原io的很多类都以NIO为基础进行了改写。新增了满足NIO的功能。 
    NIO采用了内存映射对象的方式来处理输入和输出,NIO将文件或者文件的一块区域映射到内存中,这样就可以像访问内存一样来访问文件了。通过这种方式来进行输入/输出比传统的输入和输出要快的多。

JDk1.4使用NIO改写了传统Io后,传统Io的读写速度和NIO差不了太多。

四、在开发中正确使用Io流

    了解了java io的整体类结构和每个类的一下特性后,我们可以在开发的过程中根据需要灵活的使用不同的Io流进行开发。下面是我整理2点原则:

  • 如果是操作二进制文件那我们就使用字节流,如果操作的是文本文件那我们就使用字符流。
  • 尽可能的多使用处理流,这会使我们的代码更加灵活,复用性更好。
本博文参考 https://blog.csdn.net/nightcurtis/article/details/51324105,第三、四点完全拷贝。


最后:其实在实际开发中一般字节用FileInputStream、FileOutputStream和BufferedInputStream、BufferedOutputStream就够了,大部分都用BufferedInputStream、BufferedOutputStream,因为它们有缓冲区,操作效率会好点;字符用FileReader、FileWriter和BufferedReader、BufferedWriter就够了,大部分使用的是BufferedReader和BufferedWriter,同样它们也有缓冲区。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值