/*
@author StormWangxhu
@date 2017/11/1
*/
面对压力,我可以挑灯夜战,不眠不休。面对挑战,我愿意迎难而上,永不退缩!
昨天我们总结了字符的输入流,主要进行读写的功能。今天来看一下字节流InputStream和OutputStream.
字节流基本框架体系图
整体体系:
InputStream:
基本概念
和字符流一样,字节流也有两个抽象基类作为其他类的父类。一个是InputStream,一个是OutPutStream.其他的类都是这两个类的拓展类。
(1)特点:
字节流可以操作任何数据。
(2)注意:
字节流使用的是字节数组 byte[]
字符流使用得是字符数组 char[]
- inputStream
(1)、inputStream是所有字节输入流的父类,是一个抽象类。
(2)ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
(3)ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。 - outputStream
(1)、OutputStream 是所有的输出字节流的父类,它是一个抽象类。
(2)、B
yteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据。
(3)、ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。 - 其他
(1)、LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部 分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行 号,看起来也是可以的。好像更不入流了。
(2)、PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
(3)、SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
(4)、PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例!
下面我们通过案例来分别讲解一下这些字节流的使用。
首先讲解一下FileInputStream:
案例一:利用字节流读取或写入文本内容到控制台或者文本文件中
package com.StormWang.InputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author StormWangxhu
* @version 创建时间:2017年11月1日 上午11:19:03
*
*/
class FileInStream{
//读文件,一个一个的读,读一个存一个
public static void readFile_1(){
//先写一个文件
FileWriter fileWriter = null ;
FileInputStream fileInputStream = null ;
try {
fileWriter = new FileWriter("input.txt");
fileWriter.write("FileInputStream,success.");
fileWriter.flush();
fileInputStream = new FileInputStream("input.txt");
int ch = 0 ;
while ((ch= fileInputStream.read())!=-1) {
System.out.print((char)ch);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
if (fileWriter != null) {
try {
fileWriter.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
}
}
/*
//读方法二(读完之后,一起存起来。)
public static void readFile_2() {
FileInputStream fileInputStream = null ;
try {
fileInputStream = new FileInputStream("E:\\eclipse-workspace\\day1101\\input.txt");
byte[] buf = new byte[1024];
int len = 0 ;
while ((len = fileInputStream.read()) != -1) {
System.out.println("len是:"+len);
//System.out.print(new String(buf,0,len));
System.out.println(buf);
}
System.out.println("buf数组内容:"+buf);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println("close:"+e2.toString());
}
}
}
}*/
}
public class FileInputStreamDemo {
public static void main(String[] args) {
FileInStream fileInStream = new FileInStream() ;
fileInStream.readFile_1();
}
}
我们来看一下结果:
再来看一个实例:实现对一个图片文件的复制
案例二:
package com.StormWang.InputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author StormWangxhu
* @version 创建时间:2017年11月1日 下午4:53:32
*
*/
public class CopyPicture {
/*
* 实现功能: 实现图片文件的复制
* 思路:
* 1、字节输入流和图片文件相关联
* 2、再用字节输入流将图片文件数据写入到缓冲区中,即字节数组中
* 3、通过循环读写,完成数据的存储
* 4、关闭流资源。
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream =null ;
try {
fileInputStream = new FileInputStream("F:\\2016\\1.jpg");
//测试代码
System.out.println("已过!");
fileOutputStream = new FileOutputStream("F:\\2.JPG");
byte[] buff = new byte[1024];
int ch = 0 ;
//此处的read()方法可能会抛出IOException异常,我们catch一下。
while ((ch= fileInputStream.read(buff)) !=-1) {
/*
*write方法说明:
*write(byte[] b, int off, int len)
* 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
*
* */
fileOutputStream.write(buff, 0, ch);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
问题:在输入流和图片文件关联时,系统找不到指定文件。
F盘文件如下所示,不知道为什么会找不到文件呢?
待解决:。。。。。。。
自定义缓冲字节流
package com.StormWang.InputStream;
/**
* @author StormWangxhu
* @version 创建时间:2017年11月1日 下午6:44:40
*
*/
import java.io.IOException;
import java.io.InputStream;
public class MyBuffereedInputStream {
private InputStream iStream ;
private byte[] buff = new byte[1024];
private int pos = 0,count = 0 ;
public MyBuffereedInputStream(InputStream iStream) {
// TODO Auto-generated constructor stub
this.iStream = iStream ;
}
public int myRead() throws IOException{
//通过iStream读取硬盘中的数据,存储到buff
//如果count=0,说明byte数组是空的,所以开始读取数据
if (count == 0) {
count = iStream.read(buff);
//标记位
pos = 0;
//通过下标找到数组
byte bt = buff[pos];
//byte数组个数递减
count--;
//移动标记位
pos++;
return bt&255;
}
//如果字节数组中还有数据,则继续往出取
else if (count>0) {
byte bt = buff[pos];
count--;
pos++;
return bt & 255 ;
}
return -1 ;
}
//关闭流方法
public void myClose() {
try {
iStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这样就完成了自定义字节流的定义。下面我们来应用一下:
package com.StormWang.InputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author StormWangxhu
* @version 创建时间:2017年11月1日 下午7:08:36
*
*/
public class myBufferedInputStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fInStream = null;
MyBuffereedInputStream myBuffereedInputStream = null ;
BufferedOutputStream bufferedOutputStream = null;
try {
fInStream = new FileInputStream("E:\\eclipse-workspace\\day1101\\src\\com\\StormWang\\InputStream\\CopyPicture.java");
myBuffereedInputStream = new MyBuffereedInputStream(fInStream);
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("myBuff.txt"));
int ch = 0;
while ((ch=myBuffereedInputStream.myRead())!= -1) {
bufferedOutputStream.write(ch);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (myBuffereedInputStream!= null) {
myBuffereedInputStream.myClose();
}
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (fInStream != null) {
try {
fInStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
我们来看一下结果:
读写成功!
(待回头再思考。。。。。。)
**注意:**
字节流的read()方法读取一个字节。为什么返回的不是byte类型的,而是int 类型的呢?
因为read方法读到末尾时返回的是-1.而在所操作的数据中的很容易出现连续多个1的情况,而连续读到8个1,就是-1.导致读取会提前停止。
所以将读到的一个字节给提升为一个int类型的数值,但是只保留原字节,并在剩余二进制位补0.
具体操作是:byte&255 or byte&0xff
对于write方法,可以一次写入一个字节,但接收的是一个int类型数值。只写入该int类型的数值的最低一个字节(8位)。即对于write()方法实际上进行了强制转化的动作。
简单说:read方法对读到的数据进行提升。write对操作的数据进行转换。**
转换流
- 特点:
1,是字节流和字符流之间的桥梁。
2,该流对象中可以对读取到的字节数据进行指定编码表的编码转换。 - 什么时候使用呢?
1,当字节和字符之间有转换动作时。
2,流操作的数据需要进行编码表的指定时。
具体的对象体现:这两个流对象是字符流体系中的成员。
1,InputStreamReader:字节到字符的桥梁。
2,OutputStreamWriter:字符到字节的桥梁。
这两个流对象是字符流体系中的成员。那么它们有转换作用,而本身又是字符流。所以在构造的时候,需要传入字节流对象进来。
构造函数:
InputStreamReader(InputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK。
InputStreamReader(InputStream,StringcharSet):通过该构造函数初始化,可以指定编码表。
OutputStreamWriter(OutputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK。
OutputStreamWriter(OutputStream,StringcharSet):通过该构造函数初始化,可以指定编码表。
我们来看看API是怎么解释的:
我们通过代码来演示一下:
小技巧:
1.
* 源:键盘录入
* 目的:控制台
* @author StormWangxhu
* 2.改变需求:想把键盘录入的数据存储到一个文件中
* 源:键盘
* 目的:文件
*
* 3.改变需求:想要将一个文件数据打印到控制台上
* 源:文件
* 目的:控制台
*
* ######################流操作的基本规律:##############
* 最痛苦的就是流对象有很多,不知道该用哪一个?
* 通过三个明确来完成:
* 1.明确源和目的
* 源:输入流. InputStream Reader
* 目的:输出流. OutputStream Writer
* 2.明确操作的数据是否是是纯文本
* 是纯文本:用字符流
* 不是纯文本:用字节流
* 3.当体系明确后,再明确要使用哪个具体的对象
* 通过设备来进行区分:
* 源设备:内存、硬盘、键盘
* 目的设备:内存、硬盘(文件)、控制台
* ----------------------3个需求之一-----------------------
* 1.将一个文本文件中的数据存储到另一个文件中 ,也就是复制文件
* 源:因为是源,所以使用读取流:InputStream Reader
* a.是不是操作文本文件?
* 是:这就可以选择Reader
* 不是:可以选择InputStream
* 这样下来,所属体系就明确了。
*
* b.接下来就要明确要使用体系中的哪个对象?
* 明确设备:硬盘上的一个文件
* Reader体系中可以操作文件的对象是FileReader
* c.是否需要提高效率?
* 需要:加入Reader体系的缓区:BufferedReader
*
* 接下来就是:
* 1) FileReader fr =new FileReader("a.txt");
2) BufferedReader br = new BufferedReader(fr);
* 目的:文件 OutputStream Writer
* a.目的是否是纯文本的?
* 是:Writer
* 不是:OutputStream
* b.明确设备:硬盘上的一个文件
* Writer体系中可以操作一个文件的对象是FileWriter
*
* c.是否需要提高效率?
* 需要:加入Writer体系的缓冲区:BufferedWriter
*
* 接下来就是:
* 1) FileWriter fw = new FileWriter("b.txt");
2) BufferedWriter bw = new BufferedWriter(fw);
*
* 练习: 将一个图片文件中的数据存储到另一个图片中 ,即为copy图片,要按照以上的格式去写
* 源(是图片):因为是源, 所以使用读取流:InputStream Reader
* a.是不是操作的纯文本文件?
* 不是:这就可以选择InputStream
* 是:这就可以选择 Reader
* b.接下来就要明确要使用体系中的哪个对象?
* 明确设备:硬盘上的一个图片
* InputStream体系中可以操作图片的对象时FileInputStream
* c.是否需要提高效率?
*
* 需要:加入InputStream体系的缓冲区:BufferedInputStream
*
* 接下来就是:
* FileInputStream fis = new FileInputStream("1.jpg"); BufferedInputStream bis = new BufferedInputStream(fis);
* 目的:文件 OutputStream Writer
* a.是否是纯文本的文件?
* 不是:OutputStream
* 是:writer
* b.明确设备:硬盘上的一个图片
*
* OutputStream体系中可以操作一个文件的对FileOutputStream
* c.是否需要提高效率?
*
* 需要:加入OutputStream体系的缓冲区:BufferedOutputStream
*
FileOutputStream fos = new FileOutputStream("2.jpg");
*
* BufferedOutputStream bos = BufferedOutputStream(fos);
*
我们来实现一个功能,把一个 文件的内容 输出到 控制台 上
package com.StormWang.InputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* @author StormWangxhu
* @version 创建时间:2017年11月1日 下午8:04:29
*
*/
/*
* 实现将一个文件中的内容输出到控制台上
* */
public class FileStreamDemo_2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建读取和写入对象
BufferedReader bReader = null ;
BufferedWriter bWriter = null ;
try {
/**
* FileinputStream fs = new FileInutStream("E:\\eclipse-workspace\\day1101\\myBuff.txt");
* InputStreamReader isr = new InputStreamReader(fs);
* BufferedReader br = new BufferedReader(isr);
* 以上三句合并为一句如下:
*/
bReader = new BufferedReader(
new InputStreamReader(//将字节流转换为字符流。
new FileInputStream("E:\\eclipse-workspace\\day1101\\myBuff.txt")));
bWriter = new BufferedWriter(
//将字符流转化为字节流
//并输出到控制台上
new OutputStreamWriter(System.out));
//循环读取
String lines = null ;
/**
* readLine()方法的原理:
* 其实缓冲区中的readLine()方法,用的还是与缓冲区关联的流对象的read()方法 。
* 只不过,每一次读到一个字符,先不进行具体的操作,先进行临时存储。
* 当读到回车标记时,将临时容器中存储的数据一次性的返回。
*
* */
while ((lines = bReader.readLine())!= null) {
//读一行,写一行,把每一行都写到输出流中
bWriter.write(lines);
//写一行后换一行
//newLine()方法为跨平台换行。。
bWriter.newLine();
//写一行刷新一行
bWriter.flush();
}
} catch (FileNotFoundException e) {
System.out.println("找不到文件了!");
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (bReader!= null) {
try {
bReader.close();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (bWriter!= null) {
try {
bWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
看一下结果:
输出成功!
好了,就先总结到这儿吧。如有错误,还望指正!