/**
*@author StormMaybin
*Date 2016-07-29
*/
总有人要赢的,那个人为什么不能是我?
继续昨天的IO流总结,今天是第二篇,在上一篇说会分为两篇来总结IO,可是今天看了看,貌似还有一些知识点没复习到,所以会增加一篇,另外以后还会不定时的分享一些其他知识点总结,温故而知新嘛,哈哈,废话不多说了,开始吧!
上一篇是从字符流开始的,这一篇我们来看看字节流,转换流和一些小的知识点,以及字节流的运用(复制MP3文件或者图片)!
基本概念
和字符流一样,字节流也有两个抽象类作为其他类的父类,一个是输出字节流InputStream,另外一个就是OutputStream。其他类都是这两个类的拓展!
- InputStream
- InputStream 是所有的输入字节流的父类,它是一个抽象类。
- ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
- ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
- OutputStream
- OutputStream 是所有的输出字节流的父类,它是一个抽象类。
- yteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据。
- ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
- 字节输入流和输出流的对应
- 图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。从上面的图中可以看出Java IO 中的字节流是极其对称的。“存在及合理”我们看看这些字节流中不太对称的几个类吧!
- LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部 分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行 号,看起来也是可以的。好像更不入流了。
- PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
- StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
- SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
- PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例!
小案例
字节流的概念就先看到这,下面我们先用一个小案例来引出今天的知识点(复制图片文件)
package com.stormma.FileStream;
import java.io.*;
/***
* 复制 一个图片
* 思路:
* 1、字节读取流对象和图片文件进行关联
* 2、用字节写入流创建一个图片文件,用于存储获取到的图片数据
* 3、通过循环读写,完成数据的存储
* 4、关闭资源
* @author StormMaybin
*
*/
public class CopyPic
{
/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream ("F:\\2.jpg");
fis = new FileInputStream ("F:\\1.jpg");
byte [] buf = new byte [1024];
int len = 0;
while ((len = fis.read(buf)) != -1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (fis != null)
{
try
{
fis.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (fos != null)
{
try
{
fis.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
复制结果:
可以看到我们成功的复制了1.png这个图片资源!
自定义输入字节流缓冲区
package com.stormma.FileStream;
import java.io.*;
/***
* 需求:
* 自定义字节流的缓冲
* @author StormMaybin
*
*/
public class MyBufferedInputStream
{
private InputStream in;
private byte [] buf = new byte [1024];
private int pos = 0;
private int count = 0;
public MyBufferedInputStream (InputStream in)
{
this.in = in;
}
public int myRead()
{
//通过in对象读取硬盘中的数据,存储到buf中去
//如果是count=0,那么说明byte数组是空的,所以开始读取数据
if (count == 0)
{
try
{
count = in.read(buf);
//标记位
pos = 0;
}
catch (Exception e)
{
e.printStackTrace();
}
//通过下标找到数据
byte b = buf [pos];
//byte数组的个数递减
count --;
//移动标记位
pos ++;
/***
* 至于这个为什么是返回b&255,原因很简单就是
* 一个字节是8位,比如00000000 00000000 00000000 11111111
* 这个转换成int类型是-1 ,这样会导致复制意外结束
*/
return b&255;
}
//如果byte数组中还有数据继续往出取!
else if (count > 0)
{
byte b = buf [pos];
count --;
pos ++;
return b&255;
}
return -1;
}
//关闭流方法
public void myClose()
{
try
{
in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
这样,就完成了输入字节流的自定义!
转换流
- 转换流的特点:
- 转换流是连接字节流和字符流的桥梁。
- 可对读取到的字节数据经过指定编码转换成字符。
- 可对读取到的字符数据经过指定编码转换成字节。
何时使用转换流?
- 当字节和字符之间有转换动作时。
- 流操作的数据需要编码或解码时。
具体的对象体现:
- InputStreamReader:字节到字符的桥梁。
- OutputStreamWriter:字符到字节的桥梁
我们来看看java API是怎么解释的
这样说起来很苍白,我们还是来用代码演示一下!
演示代码之前,先看一个小技巧
/**
* 流操作的基本规律:
* 1、明确源和目的
* --源:输入流:InputStream,Reader
* --目的:输出流:OutputStream,Writer
* 2、操作的数据是否是纯文本
* --是纯文本:字符流
* --不是纯文本:字节流
* 3、当体系明确之后,然后明确用哪个具体的对象
* 通过设备来区分
* 源设备:内存,磁盘,键盘
* 目的设备:内存,硬盘,控制台
*/
现在我们来实现一个功能,把一个文件的内容输出到控制台上!
package com.stormma.FileStream;
import java.io.*;
/***
1. 源:文件
2. 目的:控制台
3. 需求:把文件内容输出到控制台上
4. @author StormMaybin
5. 分析:
6. 因为源是文本文件,那么选择使用Reader
7. 目的是控制台,对应的对象是System.in,System.in操作字节流
8. 但是为了操作更见方便,这样用转换流把字节流转换成字符流
9. InputStreamReader isr = new InputStreamReader(System.in);
10. 然后看看是否要提高效率,如皋需要的话,那么就选择使用缓冲区
11. BufferedReader bufr = new BufferedReader(isr);
12. 合并为一句就是BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
13. ***********
14. 这里如果需要编码转换的话
15. 可以用转换流进行编码格式转化
16. 参数(String encoding)
*/
public class FileStreamDemo2
{
/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
//创建读取和写入对象
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
/***
* FileInputStream fis = new FileInputStream ("copy_bufwriter.txt");
* InputStreamReader isr = new InputStreamReader(fis);
* BufferdeReader bufr = new BufferedReader(isr);
*
*/
bufr = new BufferedReader(new InputStreamReader(new FileInputStream("copy_bufwriter.txt")));
bufw = new BufferedWriter(new OutputStreamWriter(System.out));
}
catch (FileNotFoundException e)
{
System.out.println("找不到文件了");
}
String lines = null;
try
{
while ((lines = bufr.readLine()) != null)
{
bufw.write(lines);
bufw.newLine();
bufw.flush();
}
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
if (bufr != null)
{
try
{
bufr.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (bufw != null)
{
try
{
bufw.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
运行结果:
现在问题又出来了,这怎么回事,打印结果里面怎么有乱码!其实很简单,源文件copy_bufwriter.txt这个的编码格式是utf-8,而在上面的代码中默认是gbk解码和编码的!编码:字符–>字节,解码:字节–>字符,既然源文件的编码格式是utf-8,那我们应该也用utf-8来编码和解码,不然肯定会产生乱码的!所以我们来修改一下源文件来解决这个问题!
package com.stormma.FileStream;
import java.io.*;
public class FileStreamDemo2
{
/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
/***
* FileInputStream fis = new FileInputStream ("copy_bufwriter.txt");
* InputStreamReader isr = new InputStreamReader(fis);
* BufferdeReader bufr = new BufferedReader(isr);
*
*/
bufr = new BufferedReader(new InputStreamReader(new FileInputStream("copy_bufwriter.txt"),"utf-8"));
bufw = new BufferedWriter(new OutputStreamWriter(System.out,"utf-8"));
}
catch (FileNotFoundException e)
{
System.out.println("找不到文件了");
}
catch (UnsupportedEncodingException e)
{
System.out.println("不支持这种编码格式");
}
String lines = null;
try
{
while ((lines = bufr.readLine()) != null)
{
bufw.write(lines);
bufw.newLine();
bufw.flush();
}
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
if (bufr != null)
{
try
{
bufr.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (bufw != null)
{
try
{
bufw.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
现在上面那一行乱码中文注释解决了!
LineNumberReader
现在我们看一下LineNumberReader这个类!本来这个类应该是上一篇文章中的,但是由于疏忽,哈哈哈,我们先来看一下java API对LineNumbetReader的介绍吧
![]()
大致了解一下,下面我们自己来定义一个MyLineNumberReader类来实现LineNumberReader类的功能。
package com.stormma.FileReaderWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class MyLineNumberReader
{
private Reader r;
private int lineNumber = 0;
/***
* @author StormMaybin
* @param r
*/
public MyLineNumberReader(Reader r)
{
this.r = r;
}
public String myReadLine()throws IOException
{
//标记行号
lineNumber++;
StringBuilder sb = new StringBuilder();
int ch = 0;
//因为这里的read方法有可能会抛出异常
//所以这个函数申明可抛出异常
while ((ch = r.read()) != -1)
{
if (ch == '\r')
{
continue;
}
else if (ch == '\n')
{
return sb.toString();
}
else
{
sb.append((char)ch );
}
}
//避免丢失最后一行
if (sb.length() != 0)
{
return sb.toString();
}
return null;
}
/***
* myClose方法
* 实现关闭流功能
* @throws IOException
*/
public void myClose()throws IOException
{
r.close();
}
public int getLineNumber()
{
return lineNumber;
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
}
/***
* 演示类
* @author StormMaybin
*
*/
class MyLineNumberReaderDemo
{
public static void main (String [] args)throws IOException
{
FileReader fr = new FileReader("copy_bufwriter.txt");
MyLineNumberReader mbufr = new MyLineNumberReader(fr);
mbufr.setLineNumber(100);
String lines = null;
while ((lines = mbufr.myReadLine()) != null)
{
System.out.println(mbufr.getLineNumber()+lines);
}
mbufr.myClose();
}
}
结果
好了,java IO总结篇(二)到这就结束了,期待第三篇,哈哈哈!