Java I/O流总结(一)

/**
@author StormWangxhu
@date    2017/10/31
*/

面对压力,我可以挑灯夜战,不眠不休。面对挑战,我愿意迎难而上,永不退缩!

在学习的这段时间里,基础知识学了很久,但却总是记不住。在前两天,自己找了一个qq项目,在这个小项目中,关于读写数据应用很频繁。也就是I/O流的应用。今天想进行一下流的总结。

在Java中,输入和输出多以流的方式进行,他的特点是数据的发送和获取都是沿数据序列进行的,每个数据必须等待它前面的数据发送或读入后才能被读写。
数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

流的分类

  • 根据输入输出的数据类型可分为: 字节流、字符流
  • 根据流的流向可分为: 输入流、输出流
  • 根据角色来分:节点流和处理流

字符流和字节流

1、读写单位不同:字节流操作的数据单元一次读取或写入8位。字符流以字符为单位,操作的数据单元是16位的字符,根据码表映射字符。
2、处理对象不同:字节流能够处理所有的对象,而字符流只能处理字符。
3、字节流主要InputStream和OutputStream作为基类,而字符流主要由Reader和Writer作为基类


输入流和输出流

输入流: 只能从中读取数据,不能写入数据。
输出流: 只能从中写入数据,不能读取数据。

注意:此处的输入、输出涉及一个方向问题,要更具具体的问题需求确定输入和输出流的方向问题。

节点流(低级流)和处理流(包装流)

1、节点流: 可以从一个特定的IO设备(如磁盘、网络)读、写数据的流,称为节点流。节点流也被称为低级流(Low Level Stream)。

特点: 程序直接连接到实际的数据源。

2、处理流:对一个已存在的节点流进行连接或封装,通过封装后的流试现数据读写功能。处理流也被称为高级流。

(1)特点:实际上,Java使用处理流来包装节点流,是一种典型的装饰设计模式,通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入/输出功能。因此,处理流也被称为包装流。处理流可以“嫁接”到任何已存在的流的基础之上。通过使用处理流,Java程序无须理会输入/输出设备,程序只要将节点流包装成处理流,就可以用形同的输入/输出操作代码来完成不同的输入/输出设备的数据。

(2)处理流的功能主要体现在以下两个方面:
—–性能的提高: 主要以增加缓冲的方式来提高输入输出的效率。
—–操作的便捷: 处理流可以提供一系列便捷的方法来一次输入/输出大批量的内容,而不是输入/输出一个或多个“水滴”。


I/O流的基本概念就先介绍到这里,下面来一起看看IO流的体系框架。

这里写图片描述

InputSream和Reader

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

  • 在inputStream里包含得方法:
    (1)、int read():
    从输入流中读取单个字节(相当于从水管 中读取一个水滴),返回所读取的字节数据。
    (2)、int read(byte[] b):
    从输入流中最多读取b.length个字节的数据,并将其存放在字节数组b中,返回实际读取的字节数。
    (3)int read(byte[] , int off ,int len ):
    从输入流中最多读取len个字节的数据,并将其存放在字节数据组b中。比不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。
  • Reader
    (1)、int read()
    (2)、int read(char[] cbuf )
    从输入流中最多读取cbuf.length长度个字符数据,并将其存放在char数组中。返回实际读取的字符数。
    (3)、int read(char[] , int off ,int len )

  • Writer

  • OutputStream
    上述两个基本流我们将在第(二)篇中讲述。

不难发现,inputStream和Reader两个基类的功能基本一样。但他们都有一个用于读取文件的输入流:
FileInputStream
FileReader  
下面我们通过程序是来示由Reader和Writer衍生出来的
FileReader
FileWriter
及其:
BufferedReader
BufferedWriter
的读取功能:

package com.stormwang.fileinputstream;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

/**
* @author StormWangxhu
* @version 创建时间:2017年10月31日 下午4:36:55
*
*/
/*
创建文件,通过流实现数据的写入
*/
public class FileWriterDemo {

    public static void main(String[] args) {

        //读取文件中的内容,首先我们来创建一个文件
        File file = new File("wang.txt");
        FileWriter fileWriter = null ;
        try {
            fileWriter = new FileWriter(file);
             fileWriter.write("abcdefg");
        } catch (FileNotFoundException  e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }finally {
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
        }
}
我们会看到,文件创建并写入成功!结果如下所示:

这里写图片描述


接下来我们就来读取文件里的内容!

package com.stormwang.fileinputstream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
* @author StormWangxhu
* @version 创建时间:2017年10月31日 下午5:05:24
*
*/
public class FileReaderDemo {

    public static void main(String[] args) {
        FileReader fileReader= null ;
        /*
         * 关于流:
         * 我们发现,流在try外面申明,在try里面初始化,在finally中关闭。
         * */
        try {
            fileReader = new FileReader("wang.txt");
            char[] buf = new char[1024];
            int hasRead = 0 ;
            /*
             * 
             * 循环读取
            while ((hasRead = fileReader.read(buf))>0) {
                fileReader.read(buf);
            }*/
            hasRead = fileReader.read(buf);//将输入流中的数据读取到char数组中。
            System.out.println("读取字符数为:"+hasRead);
            System.out.println("读取字符数如下:"+new String(buf));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            /*
             * 在finally中关闭流时,要在try...catch中
                 错误关闭流资源方法:
                 finally{
                    if(fileReader != null ){
                        fileReader.close();
                    }
                 }
                 为什么这种方法是错误的呢?
                 理由如下:  待补!
             */
            if (fileReader!=null) {
                try {
                    //关闭流之前会自动刷新
                    fileReader.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
        }

    }
}

我们看到,读取成功了。
但是很明显,由于上面的操作是流直接从数据源或目的地进行操作,效率不高。对应的缓冲技术的出现大大减少了流和数据源之间的直接操作。通过API查看,可知BuffererReader装饰了Reader.从而演变出了装饰设计模式,这种模式很好的降低了类与类之间的耦合性。
这里写图片描述

BufferedReader演示代码:

package FileReaderWriter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
* @author StormWangxhu
* @version 创建时间:2017年10月31日 下午8:18:00
*
*/
public class BufferedReaderDemo {

    public static void main(String[] args) {
        String lines = null;
        //首先我们为了继续复习向文件里写入东西。进行创建、写入!
        FileWriter fileWriter = null ;
        FileReader fileReader = null;
        BufferedReader bufferedReader = null  ;
        try {
            //创建文件并写入数据
            fileWriter = new FileWriter("buf2.txt");
            fileWriter.write("bufferedReader,successful!");
            /*
             * 此处我在运行过程中出现了问题:
             * 在finally中忘记了关闭fileWriter流,从而数据一直停留在缓冲区,无法写入到文件中。
             * close()方法自带刷新技术。
             * 则要想写入数据:
             *(1) 要么在写后进行flush();
             * (2)要么在finally中进行关闭流资源,从而实现先刷新,再关闭。
             * */
            //fileWriter.flush();
            System.out.println("测试:已运行");
            //利用缓冲技术进行读取
            fileReader = new FileReader("buf2.txt");
            bufferedReader = new BufferedReader(fileReader);
            while ((lines=bufferedReader.readLine())!=null) {
                System.out.println(lines);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
        //进行流资源的关闭
            if (bufferedReader!=null) {
                try {
                    bufferedReader.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            if (fileWriter!= null) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
                    }
    }

}

读取成功!
这里写图片描述

读取和写入是一样的,写入操作BufferesWriter.

我们仔细观察上面的代码,不难发现经过BufferedReader的装饰之后,读取数据可以一次读取一行,这样很好的提高了流的操作效率,但是readLine()方法是怎么实现的呢?我们可不可以自己定义一个BufferReader类来实现这些功能呢?答案是肯定的!
实现一下吧!
package FileReaderWriter;
/**
* @author StormWangxhu
* @version 创建时间:2017年10月31日 下午9:06:14
*
*/
/*
 * 自定义BufferedReader
 * 实现readLine()方法
 * 实现close()方法
 * */

import java.io.FileReader;
import java.io.IOException;

public class MyBufferesReader {
    private FileReader fReader ;
    /*BuffedReader的初始化参数是流对象
     * 所以这里的构造函数应该是带有流参数的构造函数
     * */
    public MyBufferesReader(FileReader fReader) {
        this.fReader = fReader ;
    }
    public String myReadLine() throws IOException {
        /***
         * BuferedReader的readLine方法
         * 内部调用的是FileReader的read方法
         * 并且内部封装了一个字符数组,来存取读取的字符。
         * 这里为了方便,创建一个StringBuilder来存取字符。
         * 
         * */
        StringBuilder sBuilder = new  StringBuilder();
        int ch=0 ;
        /**
         * 因为这里的read()方法有可能抛出异常
         * 再次申明此函数抛出异常
         * */
        while ((ch=fReader.read())!=-1) {
            if (ch == '\r') {
                continue;
            }else if (ch=='\n') {
                return sBuilder.toString();
            }else {
                sBuilder.append((char)ch);
            }
            //避免丢失最后一行
            if (sBuilder.length() != 0) {
                return sBuilder.toString();//toString()方法将对象转化为字符串对象。
            }
        }
        return null;
    }
    /**
     * myClose方法
     * 实现关闭流功能
     * @throws IOException 
     *
     * */
    public void myClose() throws IOException {
    //可以看到,其实缓冲流的关闭,实际上是在关闭字符流。
    //因此,在其他缓冲流关闭资源时,可以直接关闭字符流。
        fReader.close();
    }
}

我们新建一个类来测试myBufferedReader

package FileReaderWriter;
/**
* @author StormWangxhu
* @version 创建时间:2017年10月31日 下午9:06:14
*
*/
/*
 * 自定义BufferedReader
 * 实现readLine()方法
 * 
 * */

import java.io.FileReader;
import java.io.IOException;

public class MyBufferesReader {
    private FileReader fReader ;
    /*BuffedReader的初始化参数是流对象
     * 所以这里的构造函数应该是带有流参数的构造函数
     * */
    public MyBufferesReader(FileReader fReader) {
        this.fReader = fReader ;
    }
    public String myReadLine() throws IOException {
        /***
         * BuferedReader的readLine方法
         * 内部调用的是FileReader的read方法
         * 并且内部封装了一个字符数组,来存取读取的字符。
         * 这里为了方便,创建一个StringBuilder来存取字符。
         * 因为最终还是要将数据变成字符串
         * 
         * */
        StringBuilder sBuilder = new  StringBuilder();
        int ch=0 ;
        /**
         * 因为这里的read()方法有可能抛出异常
         * 再次申明此函数抛出异常
         * */
        while ((ch=fReader.read())!=-1) {
            if (ch == '\r') {
                continue;
            }else if (ch=='\n') {
                return sBuilder.toString();
            }else {
                sBuilder.append((char)ch);
            }
            //避免丢失最后一行
            if (sBuilder.length() != 0) {
                return sBuilder.toString();
            }
        }
        return null;
    }
    /**
     * myClose方法
     * 实现关闭流功能
     * @throws IOException 
     *
     * */
    public void myClose() throws IOException {
        fReader.close();
    }
}

最后,我们看到实现了缓冲技术读取文本数据。
这里写图片描述
但此时问题出现了,每读取一个就换行一次,和原BufferedReader功能不符合,我查看了半天,好可惜,没有看出来是为什么?此处望路过的大佬指点迷津!


补充修改说明: 2017/11/1  
昨晚上写的时候还不知道为什么会出现这样的情况,输出一个就换一次行,和原BufferedReader输出形式不符合。今天就发现了,原来是使用了System.out.println(),换行打印,导致如是结果,现在回头看,昨晚估计是在睡梦中写的。。。哈哈哈
修改代码后,我们再来看看输出情况:

这里写图片描述

输出正常,自定义BufferedReader复写成功。

缓冲解释:

为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
那干嘛不干脆一次性将全部数据都读取到缓冲中呢?第一,读取全部的数据所需要的时间可能会很长。第二,内存价格很贵,容量不想硬盘那么大。


我们再来看看这个流

LineNumberReader

首先看看这个类的API

这里写图片描述


下面我们简单的演示一下这个类,再自定义一个myLineNumberReader
这个类好像过时了。。。。。。。
今天就写到这里吧,再把上述讲的总结一下:
字符流:
基类:
    Reader

    FileReader
    FileWriter

    BufferReader
    BufferedWriter
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章