IO学习笔记之字节流
一、IO流
1.概述:IO流用来处理设备之间的数据传输(如上传文件、下载文件),java对数据的操作是通过流的方式,java用于操作流的对象都在IO包中。
导语:把大象装进冰箱分3步,1打开冰箱门,2把大象放进去,3关闭冰箱门。
2.分类:
2.1根据流向划分:
输入流:读取数据
输出流:写出数据
2.2根据流的类型划分:重点
字节流:
字节输入流:InputStream
字节输出流:OutputStream
字符流:
字符输入流:Reader
字符输出流:Writer
2.3常见问题:什么情况下使用字节流,什么时候使用字符流?
如果数据所在的文件通过windows自带的记事本打开并能读懂里面的内容,就用字符流,如果看不懂就使用字节流。字节流是万能的流,可以打开图片、视频、文档等各种格式的文件,推荐使用。
二、OutputStream类
概述:字节输出流,此抽象类是表示输出字节流的所有类的超类。
三、FileOutputStream类
3.1简介:
FileOutputStream类继承OutputStream类,文件字节输出流是用于将数据写入File
或FileDescriptor
的输出流。FileOutputStream
用于写入诸如图像数据之类的原始字节的流。从JDK1.0开始。
使用字节输出流的步骤:
1、打开文件(创建字节输出流对象)
2、写入数据
3、关闭文件(释放资源)。
3.2构造方法:
public FileOutputStream(Filefile)//通过给定的File对象创建字节输出流对象。
public FileOutputStream(File file,boolean append)//创建字节输出流对象,并可以在原有数据的末尾添加数据。
public FileOutputStream(String name)通过给定的文件字符串路径 来 创建 字节输出流对象。
public FileOutputStream(String name,boolean append)//创建字节输出流对象,并可以在原有数据的末尾添加数据。
构造方法抛出的异常
throwsFileNotFoundException 如果该文件存在,但它是一个目录,而不是一个常规文件;或者该文件不存在,但无法创建它;抑或因为其他某些原因而无法打开
构造方法测试案例:
public classFileOutputStreamDemo {
public static void main(String[] args) throwsFileNotFoundException {
//指定文件路径,创建文件对象,
File file = new File("d:\\lee\\123.txt");
//根据文件对象,创建字节输出流对象。打开文件,如果文件不存在,创建后,打开
FileOutputStreamfos =new FileOutputStream(file);
//根据文件路径,创建字节输出流对象。打开文件,如果文件不存储,创建后,打开
//推荐使用
FileOutputStreamfos2 =new FileOutputStream("d:\\lee\\111.txt");
}
}
输出结果:如图1所示
图1
3.3普通方法:
父类OutputStream中的方法:
public void write(int b)//写入一个字节
public void write(byte[] buffer)//写入一个字节数组
public void write(byte[] buffer, int start,int len)//写入一个字节数组的一部分
public void close()//释放与流对象相关的资源
public void
flush()
//刷新此输出流并强制写出所有缓冲的输出字节。
测试案例1:写数据
public class FileOutputStreamDemo2 {
public static void main(String[] args) throws IOException {
//根据文件路径,创建文件字节输出流对象,1打开文件
FileOutputStreamfos = new FileOutputStream("d:\\lee\\123.txt");
//2写数据
fos.write(97);//写一个字节,小a
//写一个字节数组,String中的getBytes()方法,把字符串转换成字节数组
fos.write("hello, Java".getBytes());
//写字节数组的一部分
byte[]bys ={97,98,99,100};
fos.write(bys, 1, 2);
//刷新,文件较短的话可以不用刷新,当写入数据较大时推荐使用刷新功能
fos.flush();
//3关闭文件
fos.close();
}
}
输出结果如图2所示。
图2
测试案例2:追加模式写数据,
public classFileOutputStreamDemo3 {
public static void main(String[] args) throws IOException {
//1打开文件
//打开文件,并开启追加模式
FileOutputStreamfos = new FileOutputStream("d:\\lee\\123.txt",true);
//2写数据
fos.write('a');
//写字符串数组,\r\n:表示换行
fos.write("bcdeef\r\n".getBytes());
fos.write("ghijkl\r\n".getBytes());
//3关闭文件
fos.close();
}
}
输出结果如图3所示。
图3
注意:在eclipse中数据实现换行和追加写入:
Windows :\r\n
Unix Linux :\n
Mac OS :\r
测试案例3:实际开发中FileOutputStream类的异常处理。
public classFileOutputStreamDemo4 {
public static void main(String[] args) {
FileOutputStreamfos = null;//把文件放在外面
try {
//1打开文件
fos = newFileOutputStream("fos.txt");
//2写数据
fos.write("Java".getBytes());
} catch(FileNotFoundExceptione) {
e.printStackTrace();//打印错误信息
} catch (IOExceptione) {
e.printStackTrace();//打印错误信息
} finally {
try {
//3关闭文件
fos.close();
} catch (IOExceptione) {
e.printStackTrace();
}
}
}
}
注意:在实际开发中常用案例3中的异常处理方法,在学习过程中一般使用throws抛出异常,这种异常处理方式让代码显得简洁,但实际并没有处理异常,而是抛给了Java虚拟机解决。
四、InputStream类
概述:此抽象类是表示字节输入流的所有类的超类,从JDK1.0开始。
构造方法:
public InputStream()
常用方法:
public void close()//
关闭此输入流并释放与该流关联的所有系统资源。
publicabstract int read()//从输入流中读取数据的下一个字节。读到流末尾而没有可用的字节,则返回值-1
。
public int read(byte[]b)//从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
public void read(byte[]b, int off, int len)//将输入流中最多 len 个数据字节读入 byte 数组。读到流末尾而没有可用的字节,则返回值-1
。
public void reset()//将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
五、FileInputStream类
5.1简介:
FileInputStream继承InputStream,从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream
用于读取诸如图像数据之类的原始字节流。
使用字节输入流的步骤:
1、打开文件(创建字节输入流对象)
2、读取数据
3、关闭文件(释放资源)
5.2构造方法:
public FileInputStream (Filefile) throwsFileNotFoundException //通过给定的File对象,创建一个字节输入流对象
public FileInputStream(Stringname) throwsFileNotFoundException //通过给定的文件名路径,创建一个字节输入流对象,推荐使用
5.3普通方法:
publicint read()//读取一个字节,读取到了流的末尾,返回-1。
publicint read(byte[] buffer)//读取一个字节数组,返回实际读取到的字节数量
测试案例1:用read()方法把某路径下的文件复制到指定路径下。一次读取一个字节,写入一个字节数据。
分析:
1:封装数据源与目的地
2: 从数据源中读数据
3: 把数据写到目的地
4: 关闭流
图4为程序流程示意图
图4
public classFileInputStreamTest2 {
public static void main(String[] args) throws IOException {
//1封装数据源
FileInputStream fis =new FileInputStream("d:\\lee\\psb.jpg");//可以是任意类型的文件
//封装目的地数据
FileOutputStream fos =newFileOutputStream("c:\\lee\\222.jpg");
//定义一个变量保存读到的数据,该数据初始化为-1,表示默认文件为空。
intch = -1;
//2读取一个字节数据,当读到的数据不为空时(ch=-1),开始写数据
while((ch =fis.read()) != -1){
//3写一个字节数据
fos.write(ch);
}
//关闭流
fis.close();
fos.close();
}
}
注意:java中Unicode编码的汉字占2个字节,由2个负数组成。
Utf-8的汉字占3个字节,
Gbk的汉字占2个字节
Unicode编码的汉字测试代码:
public class StringCode {
public static void main(String[] args) {
String str = "你好";
byte[]bys =str.getBytes();//把字符串转成字节数组
System.out.println(Arrays.toString(bys));
}
}
输出结果:
[-60, -29, -70, -61]
测试案例2:用read(byte[] buffer)方法把某路径下的文件复制到指定路径下。一次读取一个数组字节,写入一个数组字节数据。
public classFileIntputStreamTest {
public static voidmain(String[] args) throws IOException {
//1、封装数据源
FileInputStreamfis = new FileInputStream("d:\\lee\\可爱颂.mp4");//可以是任意类型的文件
//封装目的地
FileOutputStreamfos = new FileOutputStream("c:\\lee\\ke.mp4");
//定义一个数组,一次读写一个数组
byte buffer[] = new byte[100];//byte范围:-128~127
//定义一个用来保存每一次读到新数据的个数,默认文件为空。
int count = -1;
//2、读一个数组数据,read(buffer)方法返回实际读到字节的个数
while ((count=fis.read(buffer))!=-1) {
//3、写一个数组数据,
//注意:读取多少个数据,就写入多少个数据
fos.write(buffer, 0,count);
}
//4、关闭流
fis.close();
fos.close();
}
}
注意:同过对比表明,一次读写一个数组的方式复制文件时速度比一次读写一个字节数据时快很多。
六、BufferedOutputStream类
概述:字节缓冲输出流,写出数据。该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。间接继承OutputStream类。从JDK1.0开始。
构造方法:
public BufferedOutputStream(OutputStream os) //把一个基本的字节输出流,包装成一个高效的字节缓冲输出流对象。默认缓冲区大小为8K,8192个字节。
public BufferedOutputStream(OutputStream os,int size) //创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
常用方法:
public void
flush()
//刷新此缓冲的输出流
public void
write(int b)
//写一个字节的数据。效率低
public void
write(byte[] b, int off, int len)
//写一个字节数组的一部分。效率高。
使用字节缓冲输出流的步骤:
1、打开文件(创建字节缓冲输出流)
2、写取数据
3、关闭文件(释放资源)
测试案例:
public classBufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
//1、创建一个流对象
//OutputStream os = new FileOutputStream("d:\\222.txt ");
//BufferedOutputStream bos = newBufferedOutputStream(os);
//把基本的输出流对象包装成一个高效的缓冲输出流对象。
BufferedOutputStream bos =newBufferedOutputStream(new FileOutputStream("d:\\lee\\222.txt"));//匿名对象方式,推荐使用
//2、写数据
bos.write("hello java".getBytes());
//3、关闭流
bos.close();
}
}
输出结果,如图
图5
七、BufferedInputStream类
概述:字节缓冲输入流,读取数据。间接继承InputStream类。BufferedInputStream
为另一个输入流添加一些功能,即缓冲输入以及支持mark
和reset
方法的能力。从JDK1.0开始。
构造方法:
public BufferedInputStream(InputStream os) //把一个基本的字节输入流,包装成一个高效的字节缓冲输入流对象
public BufferedInputStream(InputStream os,int size)
常用方法:
public int read()//读一个字节,如果到达流末尾,则返回 -1
public int read(byte[] b,int off,int len)//读一个字节数组的一部分,如果读到流末尾返回-1
。否则此方法返回实际读取的字节数。
public void close() //关闭此输入流并释放与该流关联的所有系统资源。
使用字节缓冲输入流的步骤:
1、打开文件(创建字节缓冲输入流)
2、读取数据
3、关闭文件(释放资源)
4.测试案例
public classBufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
//1、创建流对象
BufferedInputStream bis =newBufferedInputStream(new FileInputStream("d:\\lee\\222.txt"));//匿名对象方式
//2、读数据
/*
//方式一:一次一个字节
intch = -1;//记录每次读的字节
while((ch = bis.read()) != -1 ) {
//3、写数据,直接打印到控制台
System.out.print((char)ch);
}
*/
//方式二:一次一个字节数组,推荐使用
byte[]buffer =newbyte[100];
intlen = -1;//用来记录每次读的新字节的个数
while ((len =bis.read(buffer)) != -1) {
//3、写数据,直接打印到控制台
//读到多少个字节数据,就写多少个字节数据
System.out.println(new String(buffer, 0,len) );
//把数组封装成一个对象,打印该对象信息
}
//4、关闭流
bis.close();
}
}
输出结果:hello java
5.综合练习:
用字节流复制一个视频文件(可以是任意格式的文件)的4种方式
4种方式
基本字节流,一次一个字节
基本字节流,一次一个字节数组
高效字节流,一次一个字节
高效字节流,一次一个字节数组
测试代码:
public class CopyVideo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();//起始时间
method1("d:\\lee\\TED.mp4","c:\\lee\\1.mp4");
//method2("d:\\lee\\TED.mp4","c:\\lee\\2.mp4");
//method3("d:\\lee\\TED.mp4","c:\\lee\\3.mp4");
//method4("d:\\lee\\TED.mp4","c:\\lee\\4.mp4");
long end = System.currentTimeMillis();//结束时间
System.out.println("共耗时 " + (end - start) + "毫秒");
}
//方式4:高效的字节流,一次一个字节数组
public static void method4(String src, String dest) throws IOException {
//1、数据源
BufferedInputStreambis =new BufferedInputStream(new FileInputStream(src));
//目的地
BufferedOutputStreambos =new BufferedOutputStream(new FileOutputStream(dest) );
byte[]buffer =newbyte[1024];
int len = -1;
//2、读一个字节数组数据
while ((len =bis.read(buffer)) != -1) {
//3、写一个字节数组数据
bos.write(buffer, 0,len);
}
//4、关闭流
bis.close();
bos.close();
}
//方式3:高效的字节流,一个一个字节
public static void method3(String src, String dest) throws IOException {
//1、数据源
BufferedInputStreambis =new BufferedInputStream(new FileInputStream(src));
//目的地
BufferedOutputStreambos =new BufferedOutputStream(new FileOutputStream(dest));
int ch = -1;
//2、读一个字节数据
while ((ch =bis.read()) != -1) {
//3、写一个字节数据
bos.write(ch);
}
//4、关闭流
bos.close();
bis.close();
}
//方式2:基本的字节流,一次一个字节数组
public static void method2(String src, String dest) throws IOException {
//1、数据源
FileInputStreamfis = new FileInputStream(src);
//目的地
FileOutputStreamfos = new FileOutputStream(dest);
byte[]buffer =newbyte[1024];//1kb = 1024字节
//2、读一个字节数组数据
int len = -1;
while ((len =fis.read(buffer)) != -1) {
//3、写一个字节数组数据
fos.write(buffer, 0,len);
}
//4、关闭流
fos.close();
fis.close();
}
//方式1:基本的字节流,一次一个字节
public static void method1(String src, String dest) throws IOException {
//1、数据源
FileInputStreamfis = new FileInputStream(src);
//目的地
FileOutputStreamfos = new FileOutputStream(dest);
int ch = -1;
//2、读一个字节数据
while ((ch =fis.read()) != -1) {
//3、写一个字节数据
fos.write(ch);
}
//4、关闭
fis.close();
fos.close();
}
}
分4次运行,输出结果:
方式1:共耗时143448毫秒
方式2:共耗时227毫秒
方式3:共耗时860毫秒
方式4:共耗时74毫秒
源文件大小:
目的地文件:
通过对比发现,高效缓冲流一次读写一个字节数组的方式在拷贝文件时数度最快,推荐使用。