-----------android培训、java培训、java学习型技术博客、期待与您交流! ------------
<一>IO流
一、IO流:即Input Output的缩写。数据流是一串连续不断的数据的集合,就像水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流.
数据写入程序可以使一段一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流.
二、特点:
1)IO流用来处理设备间的数据传输。
2)Java对数据的操作是通过流的方式。
3)Java用于操作流的对象都在IO包中。
4)流按操作数据分为两种:字节流和字符流。
5)流按流向分为:输入流和输出流。
注意:流只能操作数据,而不能操作文件。
三、IO流的常用基类:
1)字节流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;如InputStream子类FileInputStream和Reader子类FileReader
所有流都继承于以下四种抽象流类型的某一种:(抽象流)
四.字节流与字符流区别
二者仅仅是操作单位不一样。
InputStream和Reader是所有输入流的基类,他们都是抽象类,本身不能创建实例,但是他们是所有输入流的模板。
一般来说处理字符或字符串时使用字符流,处理字节或二进制对象时应使用字节流;
备注:字符流必须关闭资源,因为它中间有缓冲区!而字节流不需要!但是一般都会(最后)关闭资源!
字节流
字节流主要是操作byte(字节)的类型数据:
字节输出流:OutputStream
字节输入流:InputStream
字符流
Java中的字符是Unicode编码,是双字节的,1个字符等于 2个字节;
使用字节来处理字符文本就不太方便了,此时可以考虑使用字符流;
字符流主要是操作char的类型数据:
字符输出流:Writer
字符输入流:Reader
字节流和字符流在使用上的代码结构都是非常类似的,但是其内部本身也是有区别的,因为在进行字符流操作的时候会使用到缓冲区(内存中),而字节流操作的时候是不会使用到缓冲区的。
在输出的时候,OutputStream类即使最后没有关闭内容也可以输出。但是如果是Writer的话,则如果不关闭,最后一条内容是无法输出的,因为所有的内容都是保存在了缓冲区之中,每当调用了close()方法就意味着清空缓冲区了。那么可以证明字符流确实使用了缓冲区:
字节流:程序 →文件
字符流:程序 →缓冲区(内存中) →文件
如果现在字符流即使不关闭也可以完成输出的话,则必须强制性清空缓冲区:
方法:public void flush() throws IOException
我的总结:
两者相比,肯定使用字节流更加的方便,而且在程序中像图片、MP3等都是采用字节的方式的保存,那么肯定字节流会比字符流使用的更广泛。
但是需要说明的是,但是如果要是想操作中文的话,字符流肯定是最好使的。(字节流的话可能会出现乱码(一个汉字分成了两份)!)
<二>字节流
一、概述:
1、字节流和字符流的原理是相似的,只不过字节流可以对媒体进行操作。
2、由于媒体数据中都是以字节存储的,所以,字节流对象可直接对媒体进行操作,而不用再进行刷流动作。
3、读写字节流:InputStream ---> 输入流(读)
OutputStream ---> 输出流(写)
4、为何不用进行刷流动作:
因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。
5、特有方法:
int available() ---> 放回数据字节的长度,包含终止符
在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()] (fos为字节流对象)
但是,对于这个方法要慎用,如果字节过大(几个G),那么如此大的数组就会损坏内存,超过jvm所承受的大小(指定内存为64M)。
二、拷贝文件例子:
1、思路:
1)用字节流读取流对象和媒体文件相关联
2)用字节写入流对象,创建一个媒体文件,用于存储获取到的媒体文件数据
3)通过循环读写,完成数据的存储
4)关闭资源
Ex.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
File file=new File("templist//fis.txt");
if(!file.exists()){
file.createNewFile();
}
FileInputStream fis=new FileInputStream(file);
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fis.close();
}
}
01.import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
//创建流对象引用
FileOutputStream fos = null;
FileInputStream fis = null;
try{
//创建读写流对象
fos = new FileOutputStream("2.gif");
fis = new FileInputStream("1.gif");
int len = 0;
//定义字节数组,存储读取的字节流
byte[] arr = new byte[1024];
//循环读写流,完成数据存储
while((len=fis.read(arr))!=-1){
fos.write(arr,0,len);
}
}catch (IOException e){
throw new RuntimeException("复制图片失败");
}
//最终关闭资源
finally{
if(fos!=null){
try{
fos.close();
}catch (IOException e){
throw new RuntimeException("写入流关闭失败");
}
}
if(fos!=null){
try{
fis.close();
}catch (IOException e){
throw new RuntimeException("读取流关闭失败");
}
}
}
}
}
三、字节流缓冲区:
1、读写特点:
read():会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
2、原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区
2)循环这个动作,知道最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增
4)取出的时候,数组中的元素再减少,取出一个,就减少一个,直到减到0即数组取完
5)到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了
3、示意图:
4、自定义字节流缓冲区:
思路:
1、定义一个固定长度的数组
2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0
3、每次将字节数据存入元素要先将数组中的元素取完
注:取出的是byte型,返回的是int型,这里存在提升的动作,
当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,,read循环条件就结束了
变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。
如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
那么要如何做呢?
这就需要将提升为int的数据和前24位为0,后八位仍为原字节数据的这个值做与运算。即和255做与运算即可
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
byte[] by = new byte[1024*4];
private int pos=0,count=0;
//传入加强的类
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//自定义读取方法
public int myRead()throws IOException
{
//先判断计数器
if(count==0)
{
//计数器为0则存入数据
count = in.read(by);
//计数器为负则返回-1,说明结束数据读取
if(count<0)
return -1;
//每次从数组中读取数据完,指针要归零,重新移动指针
pos = 0;
//获取存入数组的元素,并需要让指针和计数器相应变化
byte b = by[pos];
count--;
pos++;
//返回读取的值,需要与运算
return b&255;
}
//计数器大于零时,不需要存数据,只需读取
else if(count>0)
{
byte b = by[pos];
count--;
pos++;
return b&0xff;
}
//为-1时即到数据结尾
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}