*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}

.hljs-comment,
.hljs-template_comment,
.diff .hljs-header,
.hljs-javadoc {
color: #998;
font-style: italic;
}

.hljs-keyword,
.css .rule .hljs-keyword,
.hljs-winutils,
.javascript .hljs-title,
.nginx .hljs-title,
.hljs-subst,
.hljs-request,
.hljs-status {
color: #333;
font-weight: bold;
}

.hljs-number,
.hljs-hexcolor,
.ruby .hljs-constant {
color: #099;
}

.hljs-string,
.hljs-tag .hljs-value,
.hljs-phpdoc,
.tex .hljs-formula {
color: #d14;
}

.hljs-title,
.hljs-id,
.coffeescript .hljs-params,
.scss .hljs-preprocessor {
color: #900;
font-weight: bold;
}

.javascript .hljs-title,
.lisp .hljs-title,
.clojure .hljs-title,
.hljs-subst {
font-weight: normal;
}

.hljs-class .hljs-title,
.haskell .hljs-type,
.vhdl .hljs-literal,
.tex .hljs-command {
color: #458;
font-weight: bold;
}

.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rules .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}

.hljs-attribute,
.hljs-variable,
.lisp .hljs-body {
color: #008080;
}

.hljs-regexp {
color: #009926;
}

.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.lisp .hljs-keyword,
.tex .hljs-special,
.hljs-prompt {
color: #990073;
}

.hljs-built_in,
.lisp .hljs-title,
.clojure .hljs-built_in {
color: #0086b3;
}

.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold;
}

.hljs-deletion {
background: #fdd;
}

.hljs-addition {
background: #dfd;
}

.diff .hljs-change {
background: #0086b3;
}

.hljs-chunk {
color: #aaa;
}

#container {
padding: 15px;
}
pre {
border: 1px solid #ccc;
border-radius: 4px;
display: block;
background-color: #f8f8f8;
}
pre code {
white-space: pre-wrap;
}
.hljs,
code {
font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;
}
:not(pre) > code {
padding: 2px 4px;
font-size: 90%;
color: #c7254e;
background-color: #f9f2f4;
white-space: nowrap;
border-radius: 4px;
}
-->

数据流分为输入、输出流,无论是输入流还是输出流,都可看作是在源和目标之间架设一根"管道",这些管道都是单向流动的,要么流入到内存(输入流),要么从内存流出(输出流)。

应用于java上,输入流和输出流分别为InputStream和OutputStream。输入流用于读取(read)数据,将数据加载到内存(应用程序),输出流用于写入(write)数据,将数据从内存写入到磁盘中。

数据流可分为字节流和字符流,字节流按字节输入、输出数据,字符流按字符个数输入、输出数据。本文介绍的是字节流。

1.OutputStream类和FileOutputStream类

OutputStream类是字节输出流的超类。它只提供了几个方法,注意这几个方法都会抛出IOExcepiton异常,因此需要捕获或向上抛出:

  • close():关闭输出流,即撤掉管道。
  • flush():将缓存字节数据强制刷到磁盘上。
  • write(byte[] b):将byte数组中的数据写入到输出流。
  • write(byte[] b, int off, int len):将byte数组中从off开始的len个字节写入到此输出流。
  • write(int b):将指定的单个字节写入此输出流。

FileOutputStream类是OutputStream的子类,专门用于操作文件相关的流,例如向文件中写入数据。该类有以下几个构造方法:

 FileOutputStream(File file):创建一个向指定 File 对象表示的文件中写入数据的文件输出流。  FileOutputStream(File file, boolean append):创建一个向指定 File 对象表示的文件中写入数据的文件输出流。  FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。  FileOutputStream(String name, boolean append):创建一个向具有指定 name 的文件中写入数据的输出文件流。

例如:创建一个新文件,向其中写入"abcde"。

import java.io.*;

public class OutStr1 {
public static void main(String[] args) throws IOException { File tempdir = new File("D:/temp"); //create a tempdir
if(!tempdir.exists()) {
tempdir.mkdir();
} File testfile = new File(tempdir,"test.txt");
FileOutputStream fos = new FileOutputStream(testfile)//在内存和testfile之间架一根名为fos的管道 fos.write("abcde".getBytes()); //将字节数组中所有字节(byte[] b,b.length)都写入到管道中
fos.close();
}
}

注意:
(1).此处的FileOutputStream()构造方法会创建一个新文件并覆盖旧文件。如果要追加文件,采用FileOutputStream(file,true)构造方法构造字节输出流。
(2).上面的for.write("abcde".getBytes())用的是write(byte[] b)方法,它会将字节数组中b.length个字节即所有字节都写入到输出流中。可以采用write(byte[] b,int off,int len)方法每次写给定位置、长度的字节数据。
(3).无论是构造方法FileOutputStream(),还是write()、close()方法,都会抛出异常,有些是IOException,有些是FileNotFoundException。因此,必须捕获这些异常或向上抛出。

追加写入和换行写入

向文件中追加数据时,采用write(File file,boolean append)方法。

File testfile = new File(tempdir,"test.txt");
FileOutputStream fos = new FileOutputStream(testfile,true); byte[] data = "abcde".getBytes();
fos.write(data,0,2);
fos.close();

换行追加时,需要加上换行符,Windows上的换行符为"\r\n",unix上的换行符为"\n"。如果要保证良好的移植性,可获取系统属性中的换行符并定义为常量。

import java.io.*;

public class OutStr1 {
private static final String LINE_SEPARATOR = System.getProperty("line.separator"); //newline public static void main(String[] args) throws IOException {
File tempdir = new File("D:/temp"); //create a tempdir
if(!tempdir.exists()) {
tempdir.mkdir();
} File testfile = new File(tempdir,"test.txt");
FileOutputStream fos = new FileOutputStream(testfile,true); String str = LINE_SEPARATOR+"abcde"; //
fos.write(str.getByte());
fos.close();
}
}

2.捕获IO异常的方法

输出流中很多方法都定义了抛出异常,这些异常必须向上抛出或捕获。向上抛出很简单,捕获起来可能比想象中的要复杂一些。

以向某文件写入数据为例,以下是最终的捕获代码,稍后将逐层分析为何要如此捕获。

File file = new File("d:/tempdir/a.txt");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write("abcde".getBytes());
} catch (IOException e) {
...
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException("xxxx");
}
}
}

向某文件写入数据,大致会包含以下几个过程。

File file = new File("d:/tempdir/a.txt");
FileOutputStream fos = new FileOutputStream(file); //---->(2)
fos.write("abcde".getBytes()); //------------------->(3)
fos.close(); //--------------------------------------->(4)

其中(2)-(4)这三条代码都需要去捕获,如果将它们放在一个try结构中,显然逻辑不合理,例如(2)正常实例化了一个输出流,但(3)异常,这时无法用(4)来关闭流。无论异常发生在何处,close()动作都是应该要执行,因此需要将close()放入finally结构中。

File file = new File("D:/tempdir/a.txt");
try {
FileOutputStream fos = new FileOutputStream(file); //---->(2)
fos.write("abcde".getBytes()); //------------------->(3)
} catch (IOException e) {
...
} finally {
fos.close(); //--------------------------------------->(4)
}

但这样一来,fos.close()中的fos是不可识别的,因此,考虑将fos定义在try结构的外面。因此:

File file = new File("D:/tempdir/a.txt");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file); //---->(2)
fos.write("abcde".getBytes()); //-->(3)
} catch (IOException e) {
...
} finally {
fos.close(); //---------------------->(4)
}

如果d:\tempdir\a.txt文件不存在,那么(2)中的fos将指向空的流对象,即空指针异常。再者,finally中的close()也是需要捕获异常的,因此加上判断并对其try...catch,于是:

File file = new File("D:/tempdir/a.txt");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file); //---->(2)
fos.write("abcde".getBytes()); //-->(3)
} catch (IOException e) {
...
} finally {
if(fos != null) {
try {
fos.close();//-------------------->(4)
} catch (IOException e) {
throw new RuntimeException("xxx");
}
}
}

3.InputStream类和FileInputStream类

以下是InputStream类提供的方法:

  • close():关闭此输入流并释放与该流关联的所有系统资源。
  • mark(int readlimit):在此输入流中标记当前的位置。
  • read():从输入流中读取数据的下一个字节,返回的是0-255之间的ASCII码,读到文件结尾时返回-1
  • read(byte[] b):从输入流中读取一定数量的字节存储到缓冲区数组b中,返回读取的字节数,读到文件结尾时返回-1
  • read(byte[] b, int off, int len):将输入流中最多 len 个数据字节读入 byte 数组,返回读取的字节数,读到文件结尾时返回-1
  • reset():将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
  • skip(long n):跳过和丢弃此输入流中数据的 n 个字节。

示例:d:\temp\test.txt文件中的内容为"abcde",读取该文件。

import java.io.*;

public class InStr1 {
public static void main(String[] args) throws IOException {
File file = new File("d:/temp/test.txt");
FileInputStream fis = new FileInputStream(file); System.out.println(fis.read());
System.out.println((char)fis.read());
System.out.println((char)fis.read());
System.out.println((char)fis.read());
System.out.println((char)fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
fis.close();
}
}

执行结果为:

97
b
c
d
e
-1
-1

从结果可知:

(1).read()每次读取一个字节并返回0-255之间的数值。可以强制转换为char字符。
(2).每次读取后指针下移一位,直到文件的结尾。
(3).读取文件结束符返回-1,且指针一直停留在结束符位置处,因此后面继续读取还是返回-1。

因此,可以使用如下代码来读取整个文件。

int ch = 0 ;
while ((ch=fis.read())!= -1) {
System.out.println((char)ch);
}

当文件很大时,这样一个字节一个字节读取的速度必然极慢。因此,需要一次读取多个字节。下面是使用read(byte[] b)一次读取多个字节的代码。

int len = 0;
byte[] buf = new byte[2];
while ((len=fis.read(buf))!=-1) {
System.out.println(new String(buf,0,len));
}

执行结果为:

ab
cd
e

几个注意点:
(1).read(byte[] b)是从文件中读取b.length个字节存储到字节数组中,第一个字节存储到b[0],第二个字节存储到b[2],以此类推,注意存储在字节数组中的每一个字节都是0-255的ASCII码。例如文件中有3个字节abc,字节数组b的长度为5,则b[0]=a,b[1]=b,b[2]=c,b[3]和b[4]不受影响(即仍为初始化值0),如果字节数组b长度为2,则第一批读取两个字节ab存储到b[0]和b[1],第二批再读取一个字节c存储到b[0]中,此时字节数组b中存储的内容为cb。
(2).read(b)返回的是读取的字节数,在到达文件末尾时返回-1。
(3).只有read()到字节数组才需要使用while的循环判断,因为自由读才需要考虑是否到达文件结尾。而write操作无需while循环和字节数组。

因此,上面的代码读取文件的过程大致为:读取a和b存放到buf[0]和buf[1]中,此时len=2,转换为字符串后打印得到ab,再读取c覆盖buf[0],读取d覆盖buf[1],转换为字符串后打印得到cd,最后读取e覆盖到buf[0],到了文件的末尾,此时buf[1]=d。也就是说到此为止,b数组中存放的仍然有d,但因为String(buf,0,len)是根据len来转换为字符串的,因此d不会被转换。但如果将new String(buf,0,len)改为new String(buf),将得到abcded。

由此可知,字节数组的长度决定了每次读取的字节数量。通常来说,可以将byte[]的长度设置为1024的整数倍以达到更高的效率,例如设置为4096,8192等都可,但也不应设置过大。

4.复制文件(字节流)

原理:

1.在源文件上架起一根输出流(read)管道。
2.在目标文件上架起一根输入流(write)管道。
3.但注意,这两根管道之间没有任何联系。可以通过中间媒介实现中转。每read()一定数量的字节到内存中,可以将这些字节write到磁盘中。
4.应该使用字节数组作为buffer做缓存,而不应该使用单字节的读取、写入,这样会非常非常慢。

import java.io.*;

public class CopyFile {
public static void main(String[] args) throws IOException {
// src file and src Stream
File src = new File("d:/temp/1.avi");
FileInputStream fis = new FileInputStream(src); // dest file and dest Stream
File dest = new File("d:/temp/1_copy.avi");
FileOutputStream fos = new FileOutputStream(dest); int len = 0;
byte[] buf = new byte[1024];
while((len=fis.read(buf)) != -1) { // read
fos.write(buf,0,len); // write
}
fis.close();
fos.close();
}
}

注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!

java IO(二):字节流的更多相关文章

  1. Java IO(二) 之 InputStream

    源代码均以JDK1.8作为參考 前言: InputStream实现了两个接口Closeable和AutoCloseable: Closeable:JDK1.5中引入,Closeable接口中仅仅有一个 ...

  2. Java IO: 其他字节流(上)

    作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) 本小节会简要概括Java IO中的PushbackInputStream,SequenceInputS ...

  3. java IO之字节流和字符流-Reader和Writer以及实现文件复制拷贝

    接上一篇的字节流,以下主要介绍字符流.字符流和字节流的差别以及文件复制拷贝.在程序中一个字符等于两个字节.而一个汉字占俩个字节(一般有限面试会问:一个char是否能存下一个汉字,答案当然是能了,一个c ...

  4. Java IO流-字节流

    2017-11-05 17:48:17 Java中的IO流按数据类型分类分为两种,一是字节流,二是字符流.字符流的出现是为了简化文本数据的读入和写出操作. 如果操作的文件是文本文件,那么使用字符流会大 ...

  5. java IO流——字节流

    字节流主要操作byte类型数据,以byte数组为准,主要操作类有InputStream(字节输入流).OutputSteam(字节输出流)由于IputStream和OutputStream都是抽象类, ...

  6. java IO的字节流和字符流及其区别

    1. 字节流和字符流的概念    1.1 字节流继承于InputStream    OutputStream,    1.2 字符流继承于InputStreamReader    OutputStre ...

  7. [Java IO]02_字节流

    概要 字节流有两个核心抽象类:InputStream 和 OutputStream.所有的字节流类都继承自这两个抽象类. InputStream 负责输入,OutputStream 负责输出. 字节流 ...

  8. Java IO之字节流

    Java中的输入是指从数据源等读到Java程序中,这里的数据源可以是文件,内存或网络连接,输出则是指从Java程序中写到目的地. 输入输出流可以分为以下几种类型(暂时不考虑File类) 类名 中文名 ...

  9. 图解 Java IO : 二、FilenameFilter源码

    Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆         瓣:BYSocket FaceBook:BYSocket Twitter   ...

随机推荐

  1. Updates were rejected because the remote contains work that you do(git报错解决方案)

    Updates were rejected because the remote contains work that you do(git报错解决方案) 今天向GitHub远程仓库提交本地项目文件时 ...

  2. MySQL的insert ignore与replace into不同

    以前从来没有接触过replace into这个语法,但是却看到很多人都在使用这个语法,并且应用在很多生产环境中,于是我也去学习了一下repalce into的用法. 关于replace 一句话:正常情 ...

  3. [转载]常见slave 延迟原因以及解决方法

    一  序言在运维线上M-M 架构的MySQL数据库时,接收的比较多关于主备延时的报警: 点击(此处)折叠或打开 check_ins_slave_lag (err_cnt:1)critical-slav ...

  4. html的布局demo

    header section footer 都是水平,垂直居中,文本内容居中 section的高度是根据文本内容自适应的,footer会一直在最下面 <!DOCTYPE html> < ...

  5. 【CentOS】阿里云CentOS安装php环境

    本文在介绍安装php环境前,已安装了nginx.mysql. 一.安装 使用国内的搜狐镜像站下载php5.6安装包,执行: wget  http://mirrors.sohu.com/php/php- ...

  6. 连接WiFi工具类

    public class WifiConnect { WifiManager wifiManager; // 构造函数 public WifiConnect(WifiManager wifiManag ...

  7. loadrunner-获取返回值和自定义参数(参数运算)

    实例:手机端操作,A新增了一条事件(返回结果:事件id,例如:1), A这时需要获取新增产生的事件id,并作为参数进行传递,才能将这条事件上报给B(返回结果:事件id不变,步骤id等于事件id加1), ...

  8. bootstrap轮播组件之“如何关闭自动轮播”

    在一个页面里使用多个bootstrap轮播组件的时候,如果还让所有轮播图都自动轮播的话,整个画面都在动,会给用户一种很不好的体验感受.所以,需要关闭轮播图的自动轮播. 关闭方法:去除如下属性即可: d ...

  9. CTF---Web入门第七题 猫抓老鼠

    猫抓老鼠分值:10 来源: 实验吧 难度:难 参与人数:8697人 Get Flag:3740人 答题人数:3944人 解题通过率:95% catch!catch!catch!嘿嘿,不多说了,再说剧透 ...

  10. GDKOI2016 游记

    2016.2.19~2.15强行广州koi被虐…… DAY 0 19日下午到达,第六次入住中大西苑宾馆,怂逼抽签抽中外交大使特殊职位,然后就一边看<死神>一边等石门两位室友啦.必须吐槽宾馆 ...