1. 字节流框架
2. InputOutputStreanm 字节输入输出流
- 字节流类用于向字节流读写8位二进制的字节,一般的,字节流类主要用于读写诸如图像或声音等多媒体的二进制数据
- 字节流类以InputStream和OutputStream为基类,它们都是抽象类,但是有非抽象方法
InputStream方法摘要:
- public abstract int read():唯一的抽象方法,从输入流中读取数据的下一个字节
- public int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
- public int read(byte[] b, int off, int len):将输入流中从off开始,最多len个数据字节存入缓冲区数据b中
- public long skip(long n):跳过和丢弃此输入流中数据的n个字节
- public void close():关闭此输入流并释放与该流相关的所有系统资源
- public void reset():将此流重新定位到最后一次对此输入流调用mark方法时的位置
OutputStream方法摘要:
- public abstract void write(int b):唯一的抽象方法,将制定的字节写入此输出流
- public void write(byte[] b):将b.length个字节从指定的byte数组写入此输出流。
- public void write(byte[] b, int off, int len):将制定byte数组中从偏移量off开始的len个字节写入此输出流
- public void flush():出阿信此输出流并强制卸除所有缓冲的输出字节
- public void clase():关闭此输出流并释放与该流相关的所有系统资源
- OutputStream类所有方法无返回值,在出错情况下会抛出IOException异常。
3. FileInput/OutputStream:文件输入输出流
- FileInputStream类表示能从文件获取字节的InputStream类
构造方法:
- FileInputStream(String filepath)
- FileInputStream(File fileobj)
FileOutputStream类表示能向文件写入字节的OutputStream类
- 构造方法:
- FileOutputStream(String filepath)
- FileOutputStream(File fileobj)
- FileOutputStream(String filepath, boolean append)
对字节流进行读写操作时,我们通常都会将缓存数组传入到read(),write()方法,而不是直接用无参的read(),write()方法一个字节一个字节的传输。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileInputStreamOutputStreamDemo {
public static void main(String[] args) {
try {
//使用读写缓冲区对让文件的读写效率大大提高
FileCopyUtil.copyFile(new File("d:\\JAVAstudy\\JavaTest\\11.txt"), new File("d:\\JAVAstudy\\JavaTest\\111.txt"));
FileCopyWithCacheUtil.copyFileWithCache(new File("d:\\JAVAstudy\\JavaTest\\22.txt"), new File("d:\\JAVAstudy\\JavaTest\\222.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
//按字节复制
class FileCopyUtil{
public static void copyFile(File src,File dst)throws IOException{
//把要复制的文件读取到文件输入流fis中
FileInputStream fis=new FileInputStream(src);
//把文件输出流fos输出到文件dst中
FileOutputStream fos=new FileOutputStream(dst);
long time1=System.currentTimeMillis();
int data=-1;
//一个字节一个字节的传,读fis中的数据,赋给data,结束标志是-1
//read()方法表示读字节,并返回一个int类型的数据
while(((data=fis.read())!=-1)){
fos.write(data);
}
//把输入输出字节流关闭
fis.close();
fos.close();
long time2=System.currentTimeMillis();
System.out.println("复制完成,共花费:"+(time2-time1)+"ms");
}
}
//带缓存的复制
class FileCopyWithCacheUtil{
public static void copyFileWithCache(File src,File dst)throws IOException{
//把要复制的文件读取到文件输入流fis中
FileInputStream fis=new FileInputStream(src);
//把文件输出流fos输出到文件dst中
FileOutputStream fos=new FileOutputStream(dst);
//准备1M大小的缓冲区
byte[] cache=new byte[1024*1024];
long time1=System.currentTimeMillis();
int len=0;
//带缓存的read()方法返回实际读到的字节数,以“-1”为结束标志
while((len=fis.read(cache))!=-1){
//指定从0开始到实际读到的位置,写进fos
fos.write(cache,0,len);
}
//把输入输出字节流关闭
fis.close();
fos.close();
long time2=System.currentTimeMillis();
System.out.println("复制完成,共花费:"+(time2-time1)+"ms");
}
}
4. ByteArrayInput/OutputStream:字节输入输出流
- ByteArrayInputStream是把字节数组当成源的输入流
- ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含从流中读取的字节,内部计数器跟踪read方法要提供的下一个字节;
- 关闭ByteArrayInputStream无效,此类中的方法在关闭此流都仍可以被调用,而不会产生任何IOException。
- 构造方法:
- ByteArrayInputStream(byte[] array):array就是源字节数组
- ByteArrayInputStream(byte[] array, int start. int numBytes)
- ByteArrayOutputStream是把字节数组当做目标输出流
- ByteArrayOutputStream()实现了一个输出流,其中的数据被写入一个byte数组(缓冲区),缓冲区会随着数据的不断写入而自动增长,可使用toByteArray()和toString()获取数据;
- 关闭ByteArrayOutputStream()无效,此类中的方法在关闭此流都仍可以被调用,而不会产生任何IOException。
- 构造方法:
- ByteArrayOutputStream() :使用无参的ByteArrayOutputStream会默认把缓存中的数据输入到一个新建的byte数组中且默认数组长度32byte。
- ByteArrayOutputStream(int numBytes) :输入流带有numBytes个byte的缓冲区
- 字符输入输出和缓存都位于内存中,并没有调用底层IO,所以close方法失效。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteArrayInputOutputStreamDemo {
public static void main(String[] args) throws IOException{
String str="Hello, Shanghai!";
//用getByte()方法把字符串转化为字节数组作为输入源
ByteArrayInputStream bis=new ByteArrayInputStream(str.getBytes());
int data=-1;
while((data=bis.read())!=-1){
System.out.print((char)data);
}
//bis.close();
ByteArrayOutputStream bos=new ByteArrayOutputStream();
//wrtie()方法传入int型的数据b被强制转换成byte并存入缓冲区
bos.write(97);//字符a的ASCII码
bos.write(62);//字符A的ASCII码
bos.write("Hello,World".getBytes());
byte[] buffer=bos.toByteArray();
for(byte b:buffer){
System.out.print((char)b);
}
FileOutputStream fos=new FileOutputStream("d:\\aa.txt",true);
bos.writeTo(fos);//把ByteArrayOutputStream数据写到对应文件输出流中
fos.close();
}
}
参观源代码:
ByteArrayInputStream
package java.io;
public class ByteArrayInputStream extends InputStream {
protected byte buf[]; //ByteArrayInputStream类里面隐藏了一个缓冲区
protected int pos; //ByteArrayInputStream读到的位置,初始值是0
protected int mark = 0;
protected int count;
//传入缓冲区的构造方法
public ByteArrayInputStream(byte buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
//传入缓冲区,偏移量,字符长度的构造方法
public ByteArrayInputStream(byte buf[], int offset, int length) {
this.buf = buf;
this.pos = offset;
this.count = Math.min(offset + length, buf.length);
this.mark = offset;
}
//同步的read()方法
public synchronized int read() {
//还没有读完,就从buf中继续取数据(&0xff)并返回,否则就返回-1
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
//同步的带参read()方法
public synchronized int read(byte b[], int off, int len) {
if (b == null) {
throw new NullPointerException();//没有传入缓冲区,抛出非受查异常
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();//偏移量的长度数值违法,抛出该异常
}
if (pos >= count) {
return -1;//读指针到达末尾,结束方法
}
int avail = count - pos;
if (len > avail) {
len = avail;
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, pos, b, off, len);把buf中的数据复制到len中
pos += len;
return len;
}
public synchronized long skip(long n) {
long k = count - pos;
if (n < k) {
k = n < 0 ? 0 : n;
}
pos += k;
return k;
}
public synchronized int available() {
return count - pos;
}
public boolean markSupported() {
return true;
}
public void mark(int readAheadLimit) {
mark = pos;
}
public synchronized void reset() {
pos = mark;
}
//这里close()方法没有任何执行操作
public void close() throws IOException {
}
}
ByteArrayOutputStream
package java.io;
import java.util.Arrays;
public class ByteArrayOutputStream extends OutputStream {
protected byte buf[];//字节数组
protected int count;//字节数组中的写入的数据位
//无参的构造方法
public ByteArrayOutputStream() {
this(32); //构造一个32位的字节数组大小
}
//自定义字节数组大小的构造方法
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
//确保缓冲区一直够用
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity - buf.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = buf.length;
int newCapacity = oldCapacity << 1;
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
buf = Arrays.copyOf(buf, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//同步的无参write()方法,传入int型的数据b被强制转换成byte并存入缓冲区
public synchronized void write(int b) {
ensureCapacity(count + 1);
buf[count] = (byte) b;
count += 1;
}
//同步的带参数的write()方法,把缓冲区中的数据按照偏移量和长度写入字节数组buf中
public synchronized void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) - b.length > 0)) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(count + len);
System.arraycopy(b, off, buf, count, len);
count += len;
}
//把ByteArrayOutputStream数据写到对应文件输出流中
public synchronized void writeTo(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
public synchronized void reset() {
count = 0;
}
//创建一个新分配的byte数组
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
public synchronized int size() {
return count;
}
public synchronized String toString() {
return new String(buf, 0, count);
}
public synchronized String toString(String charsetName)
throws UnsupportedEncodingException
{
return new String(buf, 0, count, charsetName);
}
public synchronized String toString(int hibyte) {
return new String(buf, hibyte, 0, count);
}
//这里close()方法没有任何执行操作
public void close() throws IOException {
}
}
5. FilterInput/OutputStream:过滤流
- 不直接与文件,设备打交道,只是对字节流进行包装处理通常使用的过滤流都是FilterInput/OutputStream的子类
- 过滤流仅仅为底层透明地提供扩展功能的输入输出流的包装。这些流一般由普通类的方法(即过滤流的父类访问)
- FilterInputStream(InputStream is):过滤输入流的构造方法
- FilterOutputStream(OutputStream os):过滤输出流的构造方法
- 这些类提供的方法与Input/OutputStream类的方法相同
常用的过滤流
- BufferedInput/OutputStream, 需要使用已经存在的节点流构造,提供带缓冲的读写,提高了读写的效率
- DataInput/OutputStream,数据输入输出流,允许应用程序读写基本的JAVA数据类型,应用程序可以使用数据输出流写入稍后由数据输入流读取,读写顺序要一致。
过滤流常用字段:
- byte[] buf :缓冲区数组
- int count:缓冲区有效字节长度
- int marklimit:mark的最大极限
- int markpos:最后一次调用mark方法时的字节位置
- int pos:缓冲区中当前追踪字节的位置
5.1 BufferedInput/OutputStream
- BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。
- 在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
- void mark(int readlimit) 方法:记录输入流中的某个点,
- void reset(long n) 方法:将此流重新定位到最后一次调用mark方法时的位置
- BufferedOutputStream 该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedInputOutputStreamDemo {
public static void main(String[] args) {
try {
//使用读写缓冲区对让文件的读写效率大大提高
FileUtil.copyFile(new File("d:\\JAVAstudy\\JavaTest\\11.txt"), new File("d:\\JAVAstudy\\JavaTest\\111.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
class FileUtil{
public static void copyFile(File src,File dst)throws IOException{
FileInputStream fis=new FileInputStream(src);
FileOutputStream fos=new FileOutputStream(dst);
BufferedInputStream bis=new BufferedInputStream(fis);
BufferedOutputStream bos=new BufferedOutputStream(fos);
int data=0;//用来保存实际读到的字节数
long time1=System.currentTimeMillis();
//bis对象的read方法是直接从自己构造的缓冲区中读取数据,缓冲数据是从fis填充过来的
while(((data=bis.read())!=-1)){
//bos对象的write方法是从自己的缓冲区中一次性取数据后放入输出流的
bos.write(data);
}
bis.close();
bos.close();
long time2=System.currentTimeMillis();
System.out.println("复制完成,共花费:"+(time2-time1)+"ms");
}
}
5.2 DataInput/OutputStream
- 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
- 数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataInputOutputStreamDemo {
public static void main(String[] args)throws IOException {
String name="the shanghai restoration project";
int age=10;
boolean flag=true;
char sex='男';
double money=100.56;
//DataOutput
DataOutputStream dos=new DataOutputStream(new FileOutputStream("d:\\JAVAstudy\\JavaTest\\b.txt"));
dos.writeUTF(name);
dos.writeInt(age);
dos.writeBoolean(flag);
dos.writeChar(sex);
dos.writeDouble(money);
dos.close();
//DataInput
DataInputStream dis=new DataInputStream(new FileInputStream("d:\\JAVAstudy\\JavaTest\\b.txt"));
//读的顺序必须和写的顺序一致,不然会产生乱码
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
System.out.println(dis.readBoolean());
System.out.println(dis.readChar());
System.out.println(dis.readDouble());
dis.close();
}
}