java io学习小结

其实平时的项目里面接触过不少java的io操作,但那毕竟是前人封装好的工具类,真要自己写起来,还真是云里雾里的。网上很多优秀的博客对于这个部分的内容都有很好的描述,在此,本文只记录个人的学习小结。PS:动手很重要!!!!!

数据流

所谓的数据流,在计算机的底层不过就是一串只有0和1的二进制数据。如果用1表示男性,0代表女性,那么这些数据流其实像极了春运火车站检票口排队进站的队伍。如:
←←←     
010010000110010101101100011011000110111100100000010101110110111101110010011011000110010000100001...

字节流

字节流是以字节为单位的数据流,相当于把排队的人,以八个为一组来分组。如:
←        ←        ←  
01001000 01100101 01101100 01101100 01101111 00100000 01010111 01101111 01110010 01101100 01100100 00100001 ...

字符流

每个字符根据不同的字符编码,占用的字节位数不一样,如在ASCII码表中,每个字符占用的是1个字节,01000001十进制值为65对应字母A,如此,上面的字节流根据ASCII码进行转换,则为
←    ←    ←
H e l l o   W o r l d !...

输入流

字节输入流

InputSrteam是所有字节输入流的超类,包含三种read方法,均是返回int类型,默认值-1。

方法返回值备注
read()int从输入流中读取数据的下一个字节。返回该字节十进制值。
read(byte[] b)int从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
返回读取的字节数组的长度。
read(byte[] b, int off, int len)int将输入流中最多 len 个数据字节读入 byte 数组。
返回读取的字节数组的长度。
len不能大于byte[]的长度,不然会报错。

InputStream 的子类有AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream。常见于项目中的有ByteArrayInputStream,FileInputStream以及FilterInputStream中的子类BufferedInputStream。如:

从字符串中获取字节流:InputStream inputStream = new ByteArrayInputStream(“String”.getBytes());
从文件中获取字符流:InputStream inputStream = new FileInputStream(new File(path));
从网络中获取字节流:InputStream inputStream = ((HttpURLConnection)new URL(urlStr).openConnection()).getInputStream();
BufferedInputStream继承于FilterInputStream,对InputStream进行一层封装(装饰),InputStream作为其内部一个变量,并提供一段缓冲区而具备内存缓存功能,提高了读取的效率。

字符输入流

对于字符流输入流的处理,JDK提供了Reader这个类。它有四个read方法,其中的三个其形参及返回值与InputStream中的read方法一致。第四个为 int read(CharBuffer target) 试图将字符读入指定的字符缓冲区。这个用的也比较少,忽略。

Reader的子类有BufferedReader, CharArrayReader, FilterReader, InputStreamReader, PipedReader, StringReader。常见的有InputStreamReader及其子类FileReader,BufferedReader(其他几个貌似看起来挺容易的,目前没接触过,先放一边)。
InputStreamReader是字节流通向字符流的桥梁,它封装了InputStream,并可根据指定字符编码将字节流转换成字符流。而它的子类FileReader更是进一步封装了从文本到字符流转换的这个过程,即FileReader(FilePath)等价于InputStreamReader(new FileInpustStream(new File(FilePath)))。
BufferedReader的作用跟BufferedInputSteam的作用类似,封装(装饰)了Reader,提高了字符流的读取效率。不同的是,BufferedReader中提供了一个readLine()方法,对于文本的逐行读取提供了很大的便利。

输出流

字节输出流

字节输出流的超类是OutputStream,对应InputStream中read方法的是write,无返回值。

方法返回值备注
write(int b)void将指定的字节写入此输出流。
write(byte[] b)void将 b.length 个字节从指定的 byte 数组写入此输出流。
write(byte[] b, int off, int len)void将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。

OutputStream的子类有ByteArrayOutputStream, FileOutputStream, FilterOutputStream, ObjectOutputStream, OutputStream, PipedOutputStream。常见的有ByteArrayOutputStream,FileOutputStream及FilterOutputStream中的子类BufferedOutputStream。

ByteArrayOutputStream 常用于收集网络中的数据,然后一次性把数据发送出去。
FileOutputStream 常用于文件的输出。
BufferedOutputStream 用于缓存输出,装饰OutputStream。

字符输出流

Writer 子类BufferedWriter, CharArrayWriter, FilterWriter, OutputStreamWriter, PipedWriter, PrintWriter, StringWriter。常见的有OutputStreamWriter及其子类FileWriter,BufferedWriter。

复制

从字节输入流中读取一个字节,返回该字节对应的十进制值,当字节不存在或者已读取完毕时返回-1,对应了OutputStream中的write()方法。通常在一些操作(如文件拷贝)过程中,并不需要知道具体每个字节的含义,只需要判断资源是否读写完整即可。使用其相应子类中的read方法和write()方法即可完成操作。文件拷贝核心代码如下:

InputStream in = new FileInputStream(oldFilePath);
FileOutputStream fs = new FileOutputStream(newFilePath);
byte [] buffer = new byte[1024];
int len;
while((len = in.read(buffer)) != -1){
    fs.write(buffer, 0, len);
}
in.close();
fs.close();

对于上面的代码,刚开始有两个疑问:一是,为什么要用byte数组作缓存,直接用fs.wirte(in.read())不就OK了吗?二是,为什么要用fs.write(buffer,0,len),而不是fs.wirte(buffer)?
然而实践之后终有所发现。

第一个问题,引用byte[]缓存必然是涉及效率问题。so,将程序改写一下使用单字节读写,拷贝含有1000行“hello world!”的文本,耗时为943毫秒,而使用以上的代码耗时1毫秒,结果不言而喻。网上有这么一句话形容这个过程,恰到好处:把一个水缸的水倒到另外一个水缸里去,用吸管太慢,可以用水瓢。说到这里,可能会想到,如果使用BufferedInpustream,那么它内部的缓存,跟这个buffer的作用有什么区别呢?BufferedInputStream中的缓存区只针对读取做缓存,如在文本的读取的操作中旨在减少内存与磁盘的IO操作,而buffer中的数据则在这个缓存区中读取。整个过程相当于存在二级缓存。

第二个问题,其实只需要debug一下就会发现问题所在。只有一行“hello world!”的文本,总共12个字节,若字节数组缓冲区大小设置为5,那么将是三次循环读取完毕,in.read(buffer)返回的是读取的字节数组的长度,将分别是5、5、2。使用fs.write(buffer)会将buffer里面的所有字节都写入。由于每次循环都是替换前一次循环的数据,第二次循环读取在buffer中的数据是”[ ,w,o,r,l]”,第三次循环读取只替换了前面两个字节,即[d,!,o,r,l]。那么最终拷贝输出的是“hello world!orl”,这是不正确的。fs.write(buffer,0,wirter)根据读取的偏移量写入正好解决了该问题。

从文本中逐行读取

    BufferedReader bf = new BufferedReader(new FileReader("E:\\1.txt"));
    String str=bf.readLine();
    /**
     * 读取UTF-8格式的文本,需要注意去掉BOM头,避免读取错误,推荐以下方法,需引入common.io.jar
     * BufferedReader reader = new BufferedReader(new InputStreamReader(new BOMInputStream(new FileInputStream(file)))); 
     */
    while(null != str){
        System.out.println(str);
        str=bf.readLine();
    }
    bf.close();

读取网络资源

        URL url = new URL(urlStr);
        conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
        conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
        conn.setRequestMethod("GET");
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connecion", "Keep-Alive");
        if(conn.getResponseCode() == 200){
            is = conn.getInputStream();
            //使用ByteArrayOutputStream收集网络信息
            baos = new ByteArrayOutputStream();
            int len = -1;
            byte[] buf = new byte[1024];
            while((len = is.read(buf)) != -1){
                baos.write(buf,0,len);
            }
            baos.flush();
            //将从网络收集到的信息,一次性输出,ByteArray转字符串时,需注意其字符编码
            return new String(baos.toByteArray(),"UTF-8");
        }else{
            throw new RuntimeException("responseCode is not 200...");
        }

将字节流/字符流输出到文本

将流输出到文本中,是流读取的逆向操作。如果数据源是字节流,可直接使用FileOutputStream直接输出,为提高效率,可使用BufferedOutputStream对FileOutputStream进行装饰。如果数据源是字符流,则需要使用FileOutStreamReader将其转换成字节流,再进行进一步的处理。

    //字节流输出到文本
    InputStream in=new ByteArrayInputStream("hello world!".getBytes());
    FileOutputStream fos= new FileOutputStream("E:\\3.txt");
    byte[] buffer = new byte[1024];
    int len;
    while((len=in.read(buffer)) != -1){
        fos.write(buffer, 0, len);
    }
    fos.close();
    bis.close();
    //字符流输出
    FileOutputStream fos= new FileOutputStream("E:\\4.txt");
    OutputStreamWriter writer = new OutputStreamWriter(fos,"utf-8");
    BufferedWriter bw = new BufferedWriter(writer);
    bw.write("helloword1!");
    bw.newLine();  //添加换行符
    bw.write("helloword2");
    bw.flush();
    bw.close();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值