一,概述
IO流是存储和读取数据的解决方案。
- I:input O:output
- 流:像水流一样传输数据
因为IO流与File是息息相关的,所以在学习IO流之前,简单回顾一下File:😄😊😊
- File表示系统中的文件或者文件夹的路径,利用File,我们可以获取文件信息(大小,文件名,修改时间),判断文件类型,创建文件/文件夹,删除文件/空文件夹...
📝注意📝:File类只能对文件本身进行操作,不能读写文件里面存储的数据。而想要读写数据必须要用到今天所学的IO流,下面详细说一下IO流的读写作用:
二,作用
用于读写数据(本地文件,网络)
IO流能做的事情主要是两点: 🍑🍑😊😊🎨
- 写出:把程序中的数据写出到本地文件中,这个动作也叫做写出数据,output
- 读取:还可以把本地文件中的数据读取到程序当中,这个动作也叫做读取数据,input
读写数据是以程序(也就是内存,因为程序运行在内存当中)为参照物的。
三,IO流的分类
1,按照流的方向分类:🧩
- 输入流(读取)
- 输出流(写出)
2,操作的类型分类:🧩🧩
- 字节流(可操作所以类型的文件,如音频、视频、图片、文本)
- 字符流( 只能操作纯文本文件)
📝📝📝什么是纯文本文件:
- Windows自带的记事本打开能读懂,txt文件,md文件,xml文件,lrc文件...
四,IO流体系
🐳字节输入流:读取数据
🐳字节输出流:写出数据
上图当中出现的类都是抽象类,不能直接创建它们的对象,需要看他们的子类(实现类),下面就以字节输出流为例:
1,FileOutputStream
- 操作本地文件的字节输出流,可以把程序中的数据写到本地文件中
🎨🎨书写步骤:🎨🎨
1)创建字节输出流对象:让程序跟文件之间产生数据传输的通道
- 细节1:参数是字符串表示的路径或者File对象都是可以的
- 细节2:如果文件不存在会自动创建一个新的文件,但是要保证父级路径时存在
- 细节3:如果文件已存在,则会清空文件里的内容
- 细节4:追加写入
2)写数据write():相当于数据在这个通道中进行传输
- 细节:write()方法的参数是整数,但实际上写到本地文件中是整数在ASCII上对应的字符
3)释放资源:相当于把这个通道给砸了
- 细节:每次使用完之后都要释放资源
代码演示:给空白文件aa,添加字符a
public class ByteStreamDemo1 {
/*
* 创建字节输出流对象
* 写数据
* 释放资源
*/
public static void main(String[] args) throws IOException,FileNotFoundException {
//给程序和这个文件之间建立一条通道
FileOutputStream stream = new FileOutputStream("aa.txt");
//传输所需数据
stream.write(97);//ASCII表对应a
//程序和文件之间的通道被砸掉
stream.close();
}
}
结果:
🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑
1.1 FileOutputStream写数据的3种方式
方法名称 | 说明 |
---|---|
void write ( int b ) | 一次写一个字节数据 |
void write ( byte[ ] b ) | 一次写一个字节数组数据 |
void write ( byte[ ] b(数组), int off(起始索引) , int len(个数)) | 一次写一个字节数组的部分数据 |
代码演示:
public class ByteStreamDemo2 {
public static void main(String[] args) throws IOException {
//创建对象
FileOutputStream stream = new FileOutputStream("aa.txt");
//输出数据
//方式1
stream.write(98);//b
//方式2
byte[] bytes={97,98,99,100,101};
stream.write(bytes);//abcde
//方式3
stream.write(bytes,1,3);//bcd
//释放资源
stream.close();
}
}
结果:
🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑
1.2 FileOutputStream写数据的两个小问题
1)换行写:🧩
- 再次写出一个换行符就可以了
- Windows:\r\n
- Linux:\n
- Mac:\r
📝细节:在Windows操作系统当中,Java对回车换行进行了优化,虽然完整的是\r\n,但 是只写一个\r或者\n,Java也可以实现换行,因为Java在底层会自动补全。
📝建议:不要省略,还是全写。
代码演示:
public static void main(String[] args) throws IOException {
FileOutputStream stream = new FileOutputStream("aa.txt");
//内容1
String s="llllla";
byte[] bytes = s.getBytes();//字符串转换成字节
stream.write(bytes);//llllla
//换行
String s1="\r\n";
byte[] bytes1 = s1.getBytes();
stream.write(bytes1);
//内容2
String s2="ye!!";
byte[] bytes2 = s2.getBytes();
stream.write(bytes2);
//释放资源
stream.close();
}
结果:
2)续写:🧩🧩
- 如果想要续写,打开续写开关即可
- 开关位置:创建对象的第二个参数
- 默认false:表示关闭续写,此时创建对象对清空文件
- 手动传递true:表示打开续写,此时创建对象不会清空文件
代码演示:
public class ByteStreamDemo4 {
/*
* 2,续写
* */
public static void main(String[] args) throws IOException {
FileOutputStream stream = new FileOutputStream("aa.txt",true);
String s="XUXIE";
byte[] bytes = s.getBytes();
stream.write(bytes);
stream.close();
}
}
结果:
2,FileInputStream
- 操作本地文件的字节输入流,可以把本地文件中的数据读取到程序之中
🎨🎨书写步骤:🎨🎨
1)创建字节输入流对象:让程序跟文件之间产生数据传输的通道
- 细节:如果文件不存在,就直接报错
Java为什么这么设计呢?🤔🤔🤔🤔
输出流:文件不存在,则会创建该文件。
把数据写到文件当中
输入流:文件不存在,而是报错呢?
因为创建出来的文件没有数据,读取什么呢?所以说就没有任何意义。
所以Java就没有设计这种无意义的逻辑,文件不存在直接报错
✏️✏️程序中最重要的是:数据
2)读数据read():把文件中的数据读取到程序当中,返回的是字节
- 细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
- 细节2:读取不到数据,read()方法返回-1
- 细节3:read()表示读取数据,而且读取一个数据就移动一次指针
3)释放资源:相当于把这个通道给砸了
- 细节:每次使用完之后都要释放资源
代码演示:读取文件input.txt中的数据abcd(暂时不考虑中文)
package day0528IODemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class ByteInputStreamDemo1 {
/*
* 字节输入流
* 1,创建对象
* 2,读数据
* 3,强制释放
* */
public static void main(String[] args) throws IOException {
//创建input.txt文件
File file = new File("input.txt");
System.out.println(file.createNewFile());
//创建字节输入流对象
FileInputStream stream = new FileInputStream("input.txt");//文件文本为abcd
//读数据 返回的是字节
int i1 = stream.read();
System.out.println(i1+"---"+(char)i1);//a
int i2 = stream.read();
System.out.println(i2+"---"+(char)i2);//b
int i3 = stream.read();
System.out.println(i3+"---"+(char)i3);//c
int i4 = stream.read();
System.out.println(i4+"---"+(char)i4);//d
int i5=stream.read();
System.out.println(i5+"---"+(char)i5);//如果在文件中读取不到数据,就会返回-1
//强制释放
stream.close();
}
}
结果:
2.1 FileInputStream循环读取
- read():表示读取数据,而且读取一个数据就移动一次指针。返回-1,表示读取完毕,没有内容 了。
📝📝只能说跟迭代器的指针一样,不会复位,每调用一次就会获得当前数据,然会指针后移一位,这也是下面必须定义第三方变量b的原因。
代码演示:读取文件input.txt中的数据abcd
package day0528IODemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ByteInputStreamDemo2 {
public static void main(String[] args) throws IOException {
//创建对象
FileInputStream stream = new FileInputStream("input.txt");//文件文本内容为abcd
//循环读取
int b=0;
while ((b=stream.read())!=-1){
System.out.print((char) b);//abcd
}
//释放资源
stream.close();
}
}
结果:
2.2 FileInputStream一次读取多个字节
public int read() | 一次读一个字节数据(返回值为字节,也就是字符对应的ASCII值) |
public int read(byte[] buffer) | 一次读一个字节数组数据(返回值为读取的长度) |
代码演示:
package day0528IODemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ByteInputStreamDemo3 {
/*
* FileInputStream一次读多个字节
*
* 注意:一次读一个字节数组数据,每次读取会尽可能把数组装满
* */
public static void main(String[] args) throws IOException {
FileInputStream stream = new FileInputStream("input.txt");
//自定义了一个长度为2的数组
byte[] bytes = new byte[2];
int len = stream.read(bytes);//将读取到的数据放入数组,返回值为读取的长度
//将数组转换成字符串,打印
String s = new String(bytes);
System.out.println(len);
System.out.println(s);
int len1=stream.read(bytes);
String s1 = new String(bytes);
System.out.println(len1);
System.out.println(s1);
int len2=stream.read(bytes);//文件内容已读取完,没有数据可读,所以返回-1
String s2 = new String(bytes);//因为没有读取到内容,所以数组里面还是上次读取的内容
System.out.println(len2);
System.out.println(s2);
stream.close();
}
}
结果:
📝📝📝因为每次读取的数据都会放到字节数组中,覆盖上一次的数据,如果读取的内容不能装满数组,就会保持上一次的数据,然后也会把上一次的数据再次转换成字符串,就出现了重复。为了避免返回重复的数据,可以只将字节数组中,从0所以开始到read(byte[] buffer)方法读取的长度(返回值)转换成字符串。
byte[] bytes = new byte[2];
int len = stream.read(bytes);//返回值为读取到的字节数,不是数组长度
String s = new String(bytes,0,len);//只将数组中0-len长度的内容转换成字符串
System.out.println(s);
3,文件拷贝
前面我们已经学习过了写出和读取。写:就是把程序中的数据写到文件当中;读取:就是把文件中的数据读取到程序当中。那我们是不是可以边读取边写出把文件进行拷贝了呢🤔🤔🤔🤔
代码演示:
package day0528IODemo;
import java.io.*;
public class CopyDemo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
//创建对象
FileInputStream input = new FileInputStream("D:\\java\\copy.mp4\\刺绣video - Trim.mp4");
FileOutputStream output = new FileOutputStream("copy1.mp4");
//边读边写
int b=0;
//方法一:一个字节一个字节的读取
/* while ((b=input.read())!=-1){
output.write(b);
}*/
//方法二:定义字节数组,一次读取一个数组的长度 更快速
byte[] bytes = new byte[1024 * 1024 * 5];
while ((b=input.read())!=-1){
output.write(bytes,0,b);
}
//释放资源
output.close();
input.close();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
结果:
查看拷贝的视频:
五,不同JDK版本的try...catch异常处理方式(了解即可)
快捷键:异常代码全选中+ctrl+alt+T
1,基本做法
package day0528IODemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class TryCatchDemo {
/*
* 利用try...catch...finally捕获拷贝文件中代码出现的异常
* 格式:
* try {
}catch (){
}finally {
}
* */
public static void main(String[] args) {
FileInputStream input =null;
FileOutputStream output=null;
try {
input = new FileInputStream("D:\\java\\copy.mp4\\刺绣video - Trim.mp4");
output = new FileOutputStream("copy1.mp4");
//边读边写
int b=0;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((b=input.read())!=-1){
output.write(bytes,0,b);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//释放资源 先定义的后释放
//非空判断 null调用任何方法都必然报错
if (output!=null) {
try {
output.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (input!=null) {
try {
input.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
2,JDK7版本
JDK7版本对try...catch异常处理做了简化方案,增加了接口:AutoCloseable ,资源用完最后自动释放
package day0528IODemo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TryCatchDemoJDK7 {
public static void main(String[] args) {
try(FileInputStream input = new FileInputStream("D:\\java\\copy.mp4\\刺绣video - Trim.mp4");
FileOutputStream output = new FileOutputStream("copy1.mp4");) {
//边读边写
int b=0;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((b=input.read())!=-1){
output.write(bytes,0,b);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
3,JDK9版本
JDK7中,在try()中创建对象阅读性底,所以JDK9可以在外面创建对象,在try()中只用写对象名就行,其余的执行逻辑都和JDK7一样
package day0528IODemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class TryCatchDemoJDK9 {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream input = new FileInputStream("D:\\java\\copy.mp4\\刺绣video - Trim.mp4");
FileOutputStream output = new FileOutputStream("copy1.mp4");
try(input;output) {
//边读边写
int b=0;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((b=input.read())!=-1){
output.write(bytes,0,b);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}