*/

.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. 【SSD,FIO,SAS选择的一些小结】SSD,FIO,SAS选择的一些小结

    最近重新摊上了数据库,公司核心数据库天天 IO爆满,在研究用SAS 16*RAID10 ,还是RAID10 SSD*6, 还是FIO:no.1 principle, no raid-5 for dat ...

  2. centOS7-配置网络地址

    1.首先需求切换都root账户 # su #输入密码: 2.进入网络配置文件目录查看 ()进入该目录 # /etc/sysconfig/network-scripts ()查看目录下文件 # ll - ...

  3. es5预览本地文件、es6练习代码演示案例

    es6简单基础: <!DOCTYPE html> <html> <head> <meta name="viewport" content= ...

  4. Linux 内核死锁

    死锁是指多个进程(线程)因为长久等待已被其他进程占有的的资源而陷入阻塞的一种状态.当等待的资源一直得不到释放,死锁会一直持续下去.死锁一旦发生,程序本身是解决不了的,只能依靠外部力量使得程序恢复运行, ...

  5. Nginx配置文件(2)

    一.配置文件结构 1.全局块:配置影响nginx全局的指令.一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等. 2 ...

  6. 用Windbg来分析.Net程序的dump

    介绍 1. 什么是Windbg WinDbg是微软发布的一款相当优秀的源码级(source-level)调试工具,可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件. WinDbg是微 ...

  7. 解决:java.io.IOException: No FileSystem for scheme: hdfs

    解决:java.io.IOException: No FileSystem for scheme: hdfs 开发项目初期,写完代码开始放到服务器上开始测试的时候,报出这样的一个错,不知道怎么处理了, ...

  8. CSS 换行问题white-space属性 window对象和global对象

    white-space: nowrap禁止换行 1.word-wrap:break-word; 内容将在边界内换行,仅用于块对象,内联对象要用的话,必须要设定height.width或display: ...

  9. deeplearning.ai 神经网络和深度学习 week2 神经网络基础 听课笔记

    1. Logistic回归是用于二分分类的算法. 对于m个样本的训练集,我们可能会习惯于使用for循环一个个处理,但在机器学习中,是把每一个样本写成一个列向量x,然后把m个列向量拼成一个矩阵X.这个矩 ...

  10. Vijos P1785 同学排序【模拟】

    同学排序 描述 现有m位同学,第1位同学为1号,第2位同学为2号,依次第m位同学为m号.要求双号的学生站出来,然后余下的重新组合,组合完后,再次让双号的学生站出来,重复n次,问这时有多少同学出来站着? ...