文章目录
IO流
概述
- IO 输入\输出(Input/Output)
- 流:是一种抽象概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输
- IO流就是用来处理设备间数据传输问题的,常见的应用有:文件复制、文件上传、文件下载。
- 一般来说,我们说的IO流的分类是按照数据类型来分的。
- 如果数据通过Windows自带的记事本软件打开,还可以读懂里面的内容,就使用字符流;否则使用字节流。如果不知道采用哪种类型的流,就使用字节流。
IO流分类
分类标准 | 具体内容 |
---|---|
按照数据的流向 | 输入流:读数据 输出流:写数据 |
按照数据类型来分 | 字节流(字节输入流;字节输出流) 字符流(字符输入流;字符输出流) |
字节流
概述
说明 | 字节输入流 | 字节输出流 |
---|---|---|
名称 | InputStream类 | OutputStream类 |
所属包 | java.io包,需要导包 | java.io包,需要导包 |
类声明 | public abstract class InputStream extends Object implements Closeable 是个抽象类,继承自Object类,实现了Closeable接口 | public abstract class OutputStream extends Object implements Closeable,Flushable 是个抽象类,继承自Object类,实现了Closeable,Flushable类 |
表示 | 表示输入字节流的所有类的超类(父类) | 表示输出字节流的所有类的超类 |
子类特点 | 都是以InputStream结尾的 | 都是以OutputStream结尾的 |
字节流写数据FileOutputStream
使用FileOutputStream对象:文件输出流用于将数据写入File
构造方法
方法名 | 说明 |
---|---|
FileOutputStream(String name) | 创建文件输出流 以指定的名称将数据写入文件(name路径) |
public FileOutputStream( String name,boolean append) | 创建文件输出流以指定的名称写入文件。 如果第二个参数为true,则字节将写入文件的末尾而不是开头。 |
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream(".\\fos.txt");
做了三件事情:
①调用系统功能创建了文件
②创建了字节输出流对象
③让字节输出流对象指向创建好的文件
使用字节输出流写数据的步骤:
1 创建字节输出流对象(调用系统功能创建了文件、创建字节输出流对象、让字节输出流对象指向文件)
FileOutputStream fos = new FileOutputStream(path)
2 调用字节输出流对象的写数据方法
fos.write();
3 释放资源(关闭此文件输出流并释放与此相关联的任何系统资源)
fos.close();
代码:
// 35-FileOutputStreamDemo
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream(".\\fos.txt");
* 说明:
/*做了三件事情:
* ①调用系统功能创建了文件
* ②创建了字节输出流对象
* ③让字节输出流对象指向创建好的文件
* */
// void write(int b):将指定的字节写入此文件输出流
fos.write(97);
* 说明:
// 这里需要抛出异常,之前FileOutputStream创建字节流对象时抛出的异常是 throws FileNotFoundException ;
// 当write方法抛出异常 throws IOException 后,上述异常就没有了,因为throws FileNotFoundException 是 此异常的子类异常。
fos.write(97);
* 说明:
// 写入文件后,文件显示是a 因为虽然底层写入是97,但是使用记事本打开之后会显示字符。
* 说明:
// 要想显示97,需要分别输入9 和 7
fos.write(57);
fos.write(55);
// 这里书写的是 字符 9 和 7
* 说明:
// void close():关闭此文件输出流并释放与此流相关联的任何系统资源。
fos.close();
}
}
字节流写数据的方法write(参数是 int 或 byte[])
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流 依次写入一个字节数据 |
void write(byte[] b) | 将b.length字节从指定的字节数据写入此文件输出流 一次写一个字节数组数据 |
void write(byte[] b,int off,int len) | 将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,off也就是索引 一次写入一个字节数组的部分数据 |
案例涉及一个String的方法:String对象.getByte() 返回字符串对应的字节数据。
案例:
// 35-FileOutputDemo1
public class FileOutputDemo1 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream(".\\fos.txt");
// 实际上 采用上述形式 类FileOutputStream底层也是进行了new File操作
// FileOutputStream fos1 = new FileOutputStream(new File(".\\fos.txt"));
/*
this(name != null ? new File(name) : null, false);
*/
# //void write(int a):将指定的字节写入此文件输出流
/* fos.write(97);
fos.write(98);
fos.write(99);
fos.write(100);
fos.write(101);
*/
#//void write(byte[] b):将b.length字节从指定的字节数组写入此文件输出流
byte[] bys = {101,100,99,98,97};
// byte[] getBytes():返回字符串对应的字节数组 这个方法是String的
byte[] bys1 = "ab".getBytes();
// fos.write(bys);
// fos.write(bys1);
fos.write(bys, 1, 2);
}
}
字节流写数据的两个小问题
字节流写数据如何实现换行?
不同操作系统的换行符不同。
window是\r\n,linux是\n,mac是\r
字节流写数据如何实现追加写入呢?
使用FileOutputStream的另一个构造方法实现。
public FileOutputStream( String name,boolean append) 创建文件输出流以指定的名称写入文件。如果第二个参数为true,则字节将写入文件的末尾而不是开头。
案例:字节流追加写入
// 35-FileOutputDemo2
public class FileOutputDemo2 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream(".\\fos.txt",true);
for(int i = 0 ; i < 10;i++){
fos.write("world".getBytes());
// 换行可以采用\n的形式
fos.write("\n".getBytes());
// 视频中讲解说明:
// windows系统使用\n不行,idea软件打开是有换行的,但是系统本身的记事本打开没有换行;
// 实际操作之后:
// idea软件和系统记事本 打开都是有换行的。
}
fos.close();
}
}
字节流写数据加异常处理
finally
特点:被finally控制的语句一定会执行,除非JVM退出。
应用场景介绍
常见应用场景:try…catch…finally,不管程序执行try还是catch,finally都会执行。
// idea_face-finallyPac-FinallyTest
public class FinallyTest {
public static void main(String[] args) {
// 常见finally运行情况
try{
说明 1:
System.out.println("11");
说明 2:
System.out.println(args[0]);
System.out.println("nomal");
}catch(Exception e){
System.out.println("exception");
}finally{
System.out.println("finally");
}
}
}
说明 1:
// 输出 说明当执行try选择的时候,会执行finally
// 11
//nomal
//finally
说明 2:
//输出 说明当执行catch选择的时候,会执行finally
// exception
//finally
情景1:在try、catch中添加return操作,finally块是会被执行的。
// idea_face-finallyPac-FinallyTest2
public class FinallyTest2 {
public static void main(String[] args) {
// try、catch加return的情况
try{
说明 1:
System.out.println("11");
说明 2:
System.out.println(args[0]);
System.out.println("nomal");
return;
}catch(Exception e){
System.out.println("exception");
return;
}finally{
System.out.println("finally");
}
}
}
说明 1:
// 输出 说明在try中添加return后,finally块还是会执行。
// 11
//nomal
//finally
说明 2:
// 输出 说明在catch中添加return后,finally块还是会执行。
// exception
//finally
情景2:在try、catch中添加System.exit()操作,会导致finally块不执行,因为System.exit()会将JVM终止掉。
// idea_face-finallyPac-FinallyTest3
public class FinallyTest3 {
public static void main(String[] args) {
// try、catch加System.exit(0)的情况
try{
说明 1:
System.out.println("11");
说明 2:
System.out.println(args[0]);
System.out.println("nomal");
System.exit(0);
}catch(Exception e){
System.out.println("exception");
System.exit(0);
}finally{
System.out.println("finally");
}
}
}
说明 1:
// 输出 表示执行try块中的System.exit()导致finally块不执行
// 11
//nomal
说明 2:
//输出 表示执行catch块中的System.exit()导致finally块不执行
// exception
具体应用1:在异常处理时提供finally块来执行所有清除操作。比如说IO流中的释放操作
finally和try…catch配合使用:
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}
案例:
// 35-FileOutputDemo3
public class FileOutputDemo3 {
public static void main(String[] args){
/* FileOutputStream fos = new FileOutputStream(".\\fos.txt");
fos.write("hello");
fos.close();*/
# 说明 1:
// 这里报错的原因是 有异常没有被处理。之前都是在main方法声明上通过throws处理IOException
# 说明 2:
// 本次通过try..catch处理。
/* FileOutputStream fos = null;
try{
fos = new FileOutputStream(".\\fos.txt");
fos.write("hello".getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}*/
# 说明 3:
// 为了确保在任何情况下 都实现资源释放,即执行 字节流输出流对象.close方法
/* FileOutputStream fos = null;
try{
fos = new FileOutputStream(".\\fos.txt");
fos.write("hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
# 说明 4:
// 上述代码中,当fos = new FileOutputStream(".\\fos.txt"); 采用已存在的文件不会有问题,当采用的路径是不存在的文件时 会报错
FileOutputStream fos = null;
try{
fos = new FileOutputStream("Z:\\fos.txt");
fos.write("hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
# 说明 5:
// 因为fos有问题是空,则这里的就是null。 null调用close会出错 null调用close是空指针异常:NullPointerException,所以需要加 fos为null值的判断。
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
# 说明 6:
// 上述执行后最终报错:FileNotFoundException 系统找不到指定路径 说明 fos = new FileOutputStream("Z:\\fos.txt");这里路径有问题。
}
}
字节流读数据FileInputStream
- public class FileInputStream extends InputStream 继承自InputStream类。表示:从文件中获取输入字节。
构造方法
方法名 | 说明 |
---|---|
FileInputStream(String name) | 通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name 命名 |
字节输入流读数据的步骤
1 创建字节流读数据对象
FileInputStream fis = new FileInputStream(路径)
2 字节流读数据对象调用读数据方法 对象.read
// 一次读一个字节
int by ;
while((by = fis.read()) != -1){
System.Output.println((char)by);
}
// 一次读一个字节数组
int[] bytes = new int[1024];
int len;
while((len = fis.read(bytes) != -1){
System.OutPut.println(new String(bytes,0,len));
}
3 释放内存空间 对象.close
fis.close();
字节流读数据方法
方法名 | 说明 |
---|---|
int read(int b) | 从该输入流读取一个字节的数据 返回值是实际读取字节的长度 |
int read (byte[] b) | 从该输入流读取最多b.length个字节的数据到一个字节数组 返回值是实际读取字节的长度 |
案例:字符流读数据(一次读一个字节)
// 35- FileInputStreamDemo
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream(".\\fos.txt");
// 调用字节输入流对象的读数据方法
# 说明 1:
int read():从该输入流读取一个字节的数据
/* int by = fis.read(); // 读取第一个字符
System.out.println(by);//这样输出的是数字
System.out.println((char)by);//这样输出的是字符
by = fis.read();//读取第二个字符
System.out.println(by);//这样输出的是数字
System.out.println((char)by);//这样输出的是字符
*/
说明 2:
// 要想读取整个文件内容,可以使用循环,但是循环结束的条件是什么?如果达到文件的尾部,获取的是-1
/* int by = fis.read();
while (by != -1) {
System.out.println((char)by);
by = fis.read();
}*/
说明 3:
//优化上面的程序
int by;
while((by = fis.read()) != -1){
System.out.println((char)by);
}
// 释放资源
fis.close();
}
}
案例:字节流复制文本文件(一次读写一个字节数据)
需求:把“E:\222\xxx.txt”复制到模块目录下的"xxx.txt"
// 35-InputOutputDemo
public class InputOutputDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("E:\\222\\test.txt");
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream(".\\test.txt");
int by;
while((by = fis.read()) != -1){
fos.write(by);
}
// 释放内存空间
fos.close();
fis.close();
}
}
案例:字节流读数据(一次读一个字节数组数据)
需求:把文件fos.txt中的内容读取出来在控制台输出
字节数组在控制台输出,采用的是String类中的方法
方法名 | 说明 |
---|---|
String (byte[] byte) | 把字节数组转化为字符串 |
String(btye[] byte,int offset,int len) | 把字节数组从offset索引开始len长度数组转换成字符串 |
// 35-FileInputStreamDemo2
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
//创建字节输入流对象
FileInputStream fis = new FileInputStream(".\\fos.txt");
* 说明:
// 调用字节输入流对象的读数据方式
// int read(byte[] b) :从该输入数据流读取最多b.length个字节的数据到一个字节数组
byte[] bys = new byte[5];
//第一次读取数据
int len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys));
// String(byte[] byte):将字节数组转为String类型
//第二次读取数据
len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys));
// String(byte[] byte):将字节数组转为String类型
//第三次读取数据
len = fis.read(bys);
System.out.println(len);
// 这个len表示的是读取到的字符的个数
// System.out.println(new String(bys));
* 说明:
// 如果最后一次读取数据没有5个 但是却都转换了 这样不合理
// String(byte[] bytes,int offset,int length)方法
System.out.println(new String(bys,0,len));
/*
5
hello
5
wor
2
ld
输出结果是上述内容。
分析:(文件中是这样显示)和视频中讲解一致
hello\r\n
world\r\n
由于字节数组长度是5,每次取五个字节内容:
第一次读取:hello
第二次读取:\r\nwor,取的\r\n会在控制台显示换行的空行;
第三次读取:ld
输出结果ld后的换行是System.out.println导致的。
因为:read返回的长度是2,说明没有读取后面的\r\n 字符
*/
// 改进:
byte[] bys = new byte[1024];//1024及其整数倍
int len;
while((len = fis.read(bys)) != -1){
System.out.println(new String(bys,0,len));
// 释放空间
fis.close();
}
}
注意
2022/4/14 |
---|
字节输入流对象.read(bytes[] bys) byte[] bys = new byte[1024] 其中的字节数组长度是1024及其倍数 |
案例:复制图片(一次读写一个字节数组数据)
需求:把“E:\xxx.jpg”复制到模块目录下的“xxx.jpg”
// 35-Demo1
public class Demo1 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:\\222\\4.jpg");
FileOutputStream fos = new FileOutputStream(".\\4.jpg");
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
fos.write(bys,0,len);
}
fos.close();
fis.close();
}
}
字节缓冲流
概述
说明 | 输出字节缓冲流 | 输入字节缓冲流 |
---|---|---|
名字 | BufferedOutputStream | BufferedInputStream |
所在包 | 在java.io包下,使用需要导包的 | 在java.io包下,使用需要导包的 |
父类 | OutputStream,说明它是字节输出流 | InputStream,说明它是字节输入流 |
解释 | 该类实现缓冲输出流,通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节都进行底层系统的调用, 这句话的意思是:FileOutputStream写字节会调用底层系统;而BufferedOutputStream可以向FileOutputStream这样的输出流写字节 | 创建BufferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节 |
BufferedInputStream和BufferedOutputStream的构造方法
方法名 | 说明 |
---|---|
BufferedInputStream(InputStream in) | 字节缓冲输入流 |
BufferedOutputStream(OutputStream out) | 字节缓冲输出流 |
案例:
// 36-BufferedStreamDemo1
public class BufferedStreamDemo1 {
public static void main(String[] args) throws IOException {
//创建字节缓冲输出流
/* BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(".\\bos.txt"));
// BufferedOutputStream 底层是创建一个长度是8192的字节数组
// buf = new byte[size];
// 写数据
bos.write("hello\r\n".getBytes());
bos.write("java\r\n".getBytes());
// 释放内存
bos.close();*/
// 字节缓冲输入流:BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(".\\bos.txt"));
* 说明:
// 一次读取一个字节数据
/* int by;
while((by = bis.read()) != -1){
System.out.print((char)by);
}
*/
* 说明:
// 一次读取一个字节数组数据
byte[] bys = new byte[1024];
int len;
// read(byte[] b):从输入字节流中最多读取b.length长度的字节数据到一个字节数组
while((len = bis.read(bys)) != -1){
System.out.print(new String(bys,0,len));
}
//释放资源
bis.close();
}
}
案例:复制视频
- 四种方式实现复制视频,并记录每种方式复制视频的时间
最节省时间的是:采用字节缓冲流,且一次读写一个字节数组的数据。
1 方式一:基本字节流一次读写一个字节
FileInputStream fis = new FileInputStream(源地址);
FileOutputStream fos = new FileOutputStream(目的地址);
int by;
while((by = fis.read()) != -1){
fos.write(by);
}
fis.close();
fos.close();
2 方式二:基本字节流一次读写一个字节数组
FileInputStream fis = new FileInputStream(源地址);
FileOutPutStream fos = new FileOutputStream(目的地址);
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
fos.write(bys,0,len);
}
fis.close();
fos.close();
3 方式三:字节缓冲流一次读写一个字节
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(源地址));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream (目的地址));
int by;
while((by = bis.read()) != -1){
bos.write((char)by);
}
bos.close();
bis.close();
4 方式四:字节缓冲流一次读写一个字节数组
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(源地址));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream (目的地址));
byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
fis.close();
fos.close();
代码:
// 36-Demo1
public class Demo1 {
public static void main(String[] args) throws IOException {
// 记录开始时间
long startTime = System.currentTimeMillis();
// 复制视频
// method1(); // 共耗时:134811毫秒
// method2();//共耗时:326毫秒
// method3();//共耗时:682毫秒
method4();//共耗时:96毫秒
** 根据四种复制视频方式所需要的时间进行选择,最好的方式是采用字节缓冲流,一次读取写入一个字节数组的数据。
// 记录结束时间
long endTime = System.currentTimeMillis();
System.out.println("共耗时:" + (endTime - startTime) + "毫秒");
}
// 基本字节流一次读写一个字节
public static void method1() throws IOException {
FileInputStream fis = new FileInputStream("E:\\222\\xhj.mp4");
FileOutputStream fos = new FileOutputStream(".\\xhj.mp4");
int by;
while((by = fis.read()) != -1){
fos.write(by);
}
fis.close();
fos.close();
}
// 基本字节流一次读写一个字节数组
public static void method2() throws IOException {
FileInputStream fis = new FileInputStream("E:\\222\\xhj.mp4");
FileOutputStream fos = new FileOutputStream(".\\xhj.mp4");
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
fos.write(bys,0,len);
}
fis.close();
fos.close();
}
//字节缓冲流一次读写一个字节
public static void method3() throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\222\\xhj.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(".\\xhj.mp4"));
int by;
while((by = bis.read()) != -1){
bos.write(by);
}
bis.close();
bos.close();
}
//字节缓冲流一次读写一个字节数组
public static void method4() throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\222\\xhj.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(".\\xhj.mp4"));
byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bis.close();
bos.close();
}
}
xhj小结
文件输入输出流FileInputStream、FileOutputStream参数是 文件的路径字符串 (File对象) 。
输入输出缓冲流BufferedInputStream、BufferedOutputStream参数是 InputStream、OutputStream 也就是可以是FileInputStream、FileOutputStream。