Java 字节流操作
在java中我们使用输入流来向一个字节序列对象中写入,使用输出流来向输出其内容。C语言中只使用一个File包处理一切文件操作,而在java中却有着60多种流类型,构成了整个流家族。看似庞大的体系结构,其实只要使用适合的方法将其分门别类,就显得清晰明了了。而我准备将其按照处理文件类型的不同,分为字节流类型和字符流类型。共两篇文章,本篇从字节流开始。主要包含以下内容:
- InputStream/OutPutStream - - -字节流基类
- FileInputStream/FileOutputStream - - - - -处理文件类型
- ByteArrayInputStream/ByteArrayOutputStream - - - -字节数组类型
- DataInputStream/DataOutputStream - - - -装饰类
- BufferedInputStream/BufferedOutputStream - - - -缓冲流
一、基类流
其实始终有人搞不清楚到底InputStream是读还是OutputStream是读。其实很简单就可以记住,你把你自己想象为是一个程序,InputStream对你来说是输入,也就是你要从某个地方读到自己这来,而OutputStream对你来说就是输出,也就是说你需要写到某个地方。这样就可以简单的区分输入输出流。下面看看InputStream的成员方法:
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException
public int read(byte b[], int off, int len)
public long skip(long n) throws IOException
public int available() throws IOException
public void close() throws IOException
public synchronized void mark(int readlimit)
public synchronized void reset() throws IOException
public boolean markSupported()
InputStream是一个输入流,也就是用来读取文件的流,抽象方法read读取下一个字节,当读取到文件的末尾时候返回 -1。如果流中没有数据read就会阻塞直至数据到来或者异常出现或者流关闭。这是一个受查异常,具体的调用者必须处理异常。除了一次读取一个字节,InputStream中还提供了read(byte[]),读取多个字节。read(byte[])其实默认调用的还是read(byte b[], int off, int len)方法,表示每读取一个字节就放在b[off++]中,总共读取len个字节,但是往往会出现流中字节数小于len,所以返回的是实际读取到的字节数。
接下来是一些高级的用法,skip方法表示跳过指定的字节数,来读取。调用这种方法需要知道,一旦跳过就不能返回到原来的位置。当然,我们可以看到还有剩下的三种方法,他们一起合作实现了可重复读的操作。mark方法在指定的位置打上标记,reset方法可以重新回到之前的标记索引处。但是我们可以想到,它一定是在打下mark标记的地方,使用字节数组记录下接下来的路径上的所有字节数据,直到你使用了reset方法,取出字节数组中的数据供你读取(实际上也不是一种能够重复读,只是用字节数组记录下这一路上的数据而已,等到你想要回去的时候将字节数组给你重新读取)。
OutputStream是一种输出流,具体的方法和InputStream差不多,只是,一个读一个写。但是,他们都是抽象类,想要实现具体的功能还是需要依赖他们的子类来实现,例如:FileInputStream/FileOutputStream等。
二、文件字节流
FileInputStream继承与InputStream,主要有以下两个构造方法:
public FileInputStream(String name)
public FileInputStream(File file)
第一种构造方法传的是一个字符串,实际上是一个确定文件的路径,内部将此路径封装成File类型,调用第二种构造方法。第二中构造方法,直接绑定的是一个具体的文件。
FileInputStream 的内部方法其实和父类InputStream中定义的方法差不多,我们通过一个读文件的实例来演示用法。
public class Test_InputOrOutput {
public static void main(String[] args) throws IOException{
FileInputStream fin = new FileInputStream("hello.txt");
byte[] buffer = new byte[1024];
int x = fin.read(buffer,0,buffer.length);
String str = new String(buffer);
System.out.println(str);
System.out.println(x);
fin.close();
}
}
输出结果:
hello world
13
结果意料之中,调用了read方法将hello.txt中的内容读到字节数组buffer中,然后通过String类构造方法将字节数组转换成字符串。返回实际上读取到的字节数13。(10个字母+两个空格+一个字符串结束符)
FileOutputStream继承父类OutputStream,主要方法代码如下:
private final boolean append;
public FileOutputStream(String name)
public FileOutputStream(String name, boolean append)
public FileOutputStream(File file)
public FileOutputStream(File file, boolean append)
private native void writeBytes(byte b[], int off, int len, boolean append)
public void write(byte b[]) throws IOException
FileOutputStream的一些基本的操作和FileInputStream类似,只是一个是读一个是写。我们主要要知道,append属性是指定对于文件的操作是覆盖方式(false),还是追加方式(true)。下面通过一个实例演示其用法:
public class Test_InputOrOutput {
public static void main(String[] args) throws IOException{
FileOutputStream fou = new FileOutputStream("hello.txt");
String str = "Walker_YAM";
byte[] buffer = str.getBytes("UTF-8");
fou.write(buffer,0 ,buffer.length);
fou.close();
}
}
如我们所料,字符串"Walker_YAM"将会被写入hello.txt,由于没有指定append,所以将会覆盖hello.txt中的所有内容。
三、动态字节数组流
在我们上述的文件读取流中,我们定义 byte[] buffer = new byte[1024];,buffer数组为1024,如果我们将要读取的文件中的内容有1025个字节,buffer是不是装不下?当然我们也可以定义更大的数组容量,但是从内存的使用效率上,这是低效的。我们可以使用动态的字节数组流来提高效率。
ByteArrayInputStream的内部使用了类似于ArrayList的动态数组扩容的思想。
protected byte buf[];
protected int count;
public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[], int offset, int length)
public synchronized int read()
public synchronized int read(byte b[], int off, int len)
ByteArrayInputStream内部定义了一个buf数组和记录数组中实际的字节数,read方法也很简单,读取下一个字节,read(byte b[], int off, int len) 将内置字节数组读入目标数组。实际上,整个ByteArrayInputStream也就是将一个字节数组封装在其内部。为什么这么做?主要还是为了方便参与整个InputStream的体系,复用代码。
ByteArrayOutputStream的作用要比ByteArrayInputStream更加的实际一点:
protected byte buf[];
protected int count;
public ByteArrayOutputStream() {
this(32);
}
public ByteArrayOutputStream(int size)
private void ensureCapacity(int minCapacity)
public synchronized void write(byte b[], int off, int len)
public synchronized void writeTo(OutputStream out)
public synchronized byte toByteArray()[]
public synchronized String toString()
和ByteArrayInputStream一样,内部依然封装了字节数组buf和实际容量count,通过构造方法可以指定内置字节数组的长度。主要的是write方法,将外部传入的字节数组写到内置数组中,writeTo方法可以理解为将自己内置的数组交给OutputStream 的其他子类使用。toByteArray和toString则会将内置数组转换成指定类型返回。
下面我们利用他们解决刚开始说的效率问题。
public class Test_InputOrOutput {
public static void main(String[] args) throws IOException{
FileInputStream fin = new FileInputStream("hello.txt");
ByteArrayOutputStream bou = new ByteArrayOutputStream();
int x = 0;
while((x = fin.read()) !=-1){
bou.write(x);
}
System.out.println(bou.toString());
}
}
从hello文件中每读取一个字节写入ByteArrayOutputStream 中,我们不用担心hello文件太大而需要设置较大的数组,使用ByteArrayOutputStream 动态增加容量,如果添加字节即将超过容量上限,进行扩充(往往是指数级扩充)
四、装饰者字节流
上述的流都是直接通过操作字节数组来实现输入输出的,那如果我们想要输入一个字符串类型或者int型或者double类型,那还需要调用各自的转字节数组的方法,然后将字节数组输入到流中。我们可以使用装饰流,帮我们完成转换的操作。我们先看DataOutputStream。
public DataOutputStream(OutputStream out)
public synchronized void write(byte b[], int off, int len)
public final void writeBoolean(boolean v)
public final void writeByte(int v)
public final void writeShort(int v)
public final void writeInt(int v)
public final void writeDouble(double v)
简单的列举了一些方法,可以看到,DataOutputStream只有一个构造方法,必须传入一个OutputStream类型参数。(其实它的内部还是围绕着OutputStream,只是在它的基础上做了些封装)。我们看到,有writeBoolean、writeByte、writeShort、writeDouble等方法。他们内部都是将传入的 boolean,Byte,short,double类型变量转换为了字节数组,然后调用从构造方法中接入的OutputStream参数的write方法。
//这是writeInt的具体实现
public final void writeInt(int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
incCount(4);
}
将一个四个字节的int类型,分开写入,先写入高八位。总共写四次,第一次将高八位移动到低八位与上0xFF获得整个int的低八位,这样就完成了将原高八位写入的操作,后续操作类似。
public class Test_InputOrOutput {
public static void main(String[] args) throws IOException{
DataOutputStream da = new DataOutputStream(new FileOutputStream("hello.txt"));
da.writeInt(11);
da.close();
}
}
使用UltraEditor打开hello文件,可以看到11这个int型数值被存入文件中。DataInputStream完成的就是读取的操作,基本和DataOutputStream 的操作是类似的,是一个逆操作。
五、缓冲流
在这之前,我们读取一个字节就要将它写会磁盘,这样来回开销很大,我们可以使用缓冲区来提高效率,在缓冲区满的时候,或者流关闭时候,将缓冲区中所有的内容全部写会磁盘。BufferedInputStream和BufferedOutputStream也是一对装饰流,我们先看看BufferedInputStream:
private static int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte buf[];
protected int pos;
protected int count;
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
public synchronized int read()
public synchronized void mark(int readlimit)
public synchronized void reset()
一样也是装饰类流,第一种构造方法要求必须传入InputStream类型参数,DEFAULT_BUFFER_SIZE 指定了默认的缓冲区的大小,当然还可以使用第二种构造方法指定缓冲区的大小(当然不能超过上界),read方法读取的时候会将数据读入内部的缓冲区中,当然缓冲区也是可以动态扩容的。
public class Test_InputOrOutput {
public static void main(String[] args) throws IOException{
BufferedInputStream bi = new BufferedInputStream(new FileInputStream("hello.txt"));
bi.read();
bi.read();
bi.read();
bi.read();
System.out.println(bi.available());
}
}
BufferedOutputStream和它是逆操作,不在赘述。这种缓冲字节流可以很大程度上提高我们的程序执行的效率,所以一般在使用别的流的时候都会包装上这层缓冲流。
最后,本文如有错误指出,望大家指出!下一篇会写字符流。
Java 字节流操作的更多相关文章
- Java 字节流实现文件读写操作(InputStream-OutputStream)
Java 字节流实现文件读写操作(InputStream-OutputStream) 备注:字节流比字符流底层,但是效率底下. 字符流地址:http://pengyan5945.iteye.com/b ...
- Java之字节流操作-复制文件
package test_demo.fileoper; import java.io.FileInputStream; import java.io.FileOutputStream; import ...
- java字节流和字符流,以及java文件操作
A.首先说字节流:1.字节流在操作的时候不会用到缓冲区(也就是内存)2.字节流可用于任何类型的对象,包括二进制对象3.字节流处理单元为1个字节,操作字节和字节数组.InputStream是所有字节输入 ...
- java 字节流与字符流的区别
字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢?实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作 ...
- (转)java字节流和字符流的区别
转载: http://www.cnblogs.com/dolphin0520/category/361055.html 字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同 ...
- java 字节流和字符流的区别 转载
转载自:http://blog.csdn.net/cynhafa/article/details/6882061 java 字节流和字符流的区别 字节流与和字符流的使用非常相似,两者除了操作代码上的不 ...
- java 字节流和字符流的区别
转载自:http://blog.csdn.net/cynhafa/article/details/6882061 java 字节流和字符流的区别 字节流与和字符流的使用非常相似,两者除了操作代码上的不 ...
- java 字节流与字符流的区别 (转)
字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操 ...
- 关于java字节流的read()方法返回值为int的思考
我们都知道java中io操作分为字节流和字符流,对于字节流,顾名思义是按字节的方式读取数据,所以我们常用字节流来读取二进制流(如图片,音乐 等文件).问题是为什么字节流中定义的read()方法返回值为 ...
随机推荐
- spring EL表达式,null-safe表达式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://ww ...
- loadrunner Analysis :SLA(Service Level Agreement服务水平协议)
SLA是为负载场景定义的具体目标,用于与实际负载结果比较,确定系统是否达到性能目标. 1.1.1 设置SLA(以Transaction Response Time(Average)为例) 可以 ...
- 《JAVASCRIPT高级程序设计》节点层次和DOM操作技术
DOM可以将任何HTML和XML文档描绘成一个由多层次节点构成的结构.节点分为几种不同的类型,每种类型分别表示文档中不同的信息,每种类型都继承与Node接口,因此都共同享有一些属性和方法,同时,也拥有 ...
- maven常用命令介绍(持续更新)
一.Maven的基本概念 主要服务于基于Java平台的项目构建,依赖管理和项目信息管理. 1.1.项目构建 项目构建过程包括[清理项目]→[编译项目]→[测试项目]→[生成测试报告]→[打包项目]→[ ...
- 蓝桥网试题 java 基础练习 矩形面积交
------------------------------------------------------------------------------------------- 思路见锦囊2 - ...
- 在Ubuntu12.0至14.04版本之间用Apache搭建网站运行环境
为了顺利安装各种软件,先更新下系统. apt-get update 安装Apache服务 apt-get install apache2 -y 安装php apt-get install php5 - ...
- php解析
vim /usr/local/apache/conf/httpd.conf ##修改apache的网页配置文件 → 解析php文件 /usr/local/apache/bin/apache ...
- testNG实现test失败后重复执行,
test失败自动执行大大提高测试结果的准确性, 1.修改testNG源码实现test失败自动执行, 首先获取testng的源码, 获取源代码,构建过程: $ git clone git://githu ...
- win7下安装maven3.1.1
1.下载maven的安装包,下载地址http://maven.apache.org/download.cgi ,在这个页面中,你可以选择要下载的最新版本的maven gz包.我下载的是maven3.1 ...
- API练习_图形
#include<windows.h> LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int WINAPI WinMain(HINS ...