一、概述
IO流的分类
总结流的四大类
字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。
IO流的作用?
读写文件数据的
二、字节流的使用
1、字节流的第一种输入方式(一个个的来)
目标:字节输入流的使用。 IO流的体系: 字节流 字符流 字节输入流 字节输出流 字符输入流 字符输出流 InputStream OutputStream Reader Writer (抽象类) FileInputStream FileOutputStream FileReader FileWriter(实现类,可以使用的) 文件字节输入流:FileInputStream -- 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。 按照字节读文件数据到内存中。 -- 构造器: public FileInputStream(File file):创建字节输入流管道与源文件对象接通 public FileInputStream(String pathname):创建字节输入流管道与源文件路径接通。 -- 方法: public int read(): 每次读取一个字节返回,读取完毕返回-1。 小结: 一个一个字节读取中文数据输出其实是被淘汰的,性能极差! 一个一个字节读取中文数据输出,会出现截断中文字节的情况,无法避免读取中文输出乱码的问题。
public static void main(String[] args) throws Exception {
//1、创建一个文件字节输入流管道与源文件接通
//InputStream is = new FileInputStream(new File("src\\data02.txt"));
// 简化写法
InputStream is = new FileInputStream("src\\data02.txt");
/**
//2、读取一个字节返回
int b1 = is.read();//默认从前往后依次读取
System.out.println((char)b1);
int b2 = is.read();
System.out.println((char)b2);
int b3 = is.read();
System.out.println((char)b3);
int b4 = is.read();//读取完毕后返回-1
System.out.println((char)b4);
*/
//IO流是读缺一个就少一个,不回头的,所以为了演示接下来的别的方法,就把上面的式子关掉
//3、使用循环改进
//定义一个变量记录每次读取的字节 a b c 爱
// o o o [ooo]
int b;
while((b = is.read()) != -1){
System.out.println((char)b);
}//崩溃了,一个个读取,但中文字符占3个字节,强行拆开,导致解析失败
}
2、字节流输入的第二种方式(以字节数组的形式)
public static void main(String[] args) throws Exception {
//1、创建一个文件字节输入流管道与源文件接通
InputStream is = new FileInputStream("src/data02.txt");
//2、定义一个字节数组,用于读取字节数组
//byte[] buffer = new byte[3];
//int len1 = is.read(buffer);
//System.out.println("读取了几个字节:" + len1);
//String rs1 = new String(buffer);
//System.out.println(rs1);
//int len2 = is.read(buffer);
//System.out.println("读取了几个字节:" + len2);
//String rs2 = new String(buffer);
//System.out.println(rs2);//abc
/**
* int len3 = is.read(buffer);
System.out.println("读取了几个字节:" + len3);
String rs3 = new String(buffer);
System.out.println(rs3);
*/ //cdc
//问题:虽然最后一组只有两个字母,却输出了3个字母
//原因:就像上战场一样,后一排的战士上来后前一排的战士会上前一步,
// 但最后一组时只有两个战士,导致没有人去第三个位置,
// 所以第三个战士就没有被顶替,还在原位置,随着最后一次的输出出去
//改进方法
/**
* int len4 = is.read(buffer);
System.out.println("读取了几个字节:" + len4);
String rs4 = new String(buffer,0,len4);//读多少,倒多少
System.out.println(rs4);//cd
int len5 = is.read(buffer);
System.out.println(len5);//-1,读取完毕
*/
//3、再次改进,使用循环,每次读取一个字节数组
byte[] buffer = new byte[3];
int len;//记录每次读取的字节数
while((len = is.read(buffer)) != -1){
//读多少就倒多少
System.out.print(new String(buffer,0,len));
//无法避免中文字符的乱码问题: “a我”为一组时,崩溃
}
}
3、字节流输入的第三种方式(一次性读完全部字节)(重点)
public static void main(String[] args) throws Exception {
//1、创建一个文件字节输入流管道与源文件接通
File f = new File("src/data02.txt");
InputStream is = new FileInputStream(f);
//2、定义一个字节数组与文件的大小刚刚好,一样大
byte[] buffer = new byte[(int)f.length()];
int len = is.read(buffer);
System.out.println("读取了多少个字节:" + len);
System.out.println("文件大小:" + f.length());
System.out.println(new String(buffer));//自己编的
//读取全部字节数组
//byte[] buffer = is.readAllBytes();
//System.out.println(new String(buffer));‘
//官方提供的API。从JDK9开始支持
}
4、字节流的输出方式
在创建文件对象时,在后面加上一个true,这样可以追加数据
只有输出流才支持!
/**目标:字节输出流的使用。 IO流的体系: 字节流 字符流 字节输入流 字节输出流 字符输入流 字符输出流 InputStream OutputStream Reader Writer (抽象类) FileInputStream FileOutputStream FileReader FileWriter (实现类) a.FileOutputStream文件字节输出流。 -- 作用:以内存为基准,把内存中的数据,按照字节的形式写出到磁盘文件中去。 简单来说,把内存数据按照字节写出到磁盘文件中去。 -- 构造器: public FileOutputStream(File file):创建一个字节输出流管道通向目标文件对象。 public FileOutputStream(String file):创建一个字节输出流管道通向目标文件路径。 public FileOutputStream(File file , boolean append):创建一个追加数据的字节输出流管道通向目标文件对象。 public FileOutputStream(String file , boolean append):创建一个追加数据的字节输出流管道通向目标文件路径。 -- 方法: public void write(int a):写一个字节出去 。 public void write(byte[] buffer):写一个字节数组出去。 public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。 参数一,字节数组;参数二:起始字节索引位置,参数三:写多少个字节数出去。 小结: 记住。 换行: os.write("\r\n".getBytes()); // 换行 追加数据管道: OutputStream os = new FileOutputStream("day10_demo/out01.txt" , true); // 追加管道!! */public static void main(String[] args) throws Exception { //1、创建一个文件字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream("src/out04.txt",true);//true:用来追加数据 // 就是在第一次写完数据后,还可以继续接着写 // 不然的话,每次写数据时,都会把之前的数据清空 //2、写数据进去 //a.public void write(int a):写一个字节进去 os.write('a'); os.write(98); //os.write('许');//[ooo] os.write("\r\n".getBytes());//换行,转成字节数组输出 //b.public void write(byte[] buffer):写一个字节数组出去 byte[] buffer1 = {'a',97,98,99}; os.write(buffer1); os.write("\r\n".getBytes()); byte[] buffer2 = "我是中国人".getBytes(); //byte[] buffer2 = "我是中国人".getBytes("GBK"); os.write(buffer2); os.write("\r\n".getBytes()); //c.public void write(byte[] buffer, int pos, int len):写一个字节数组的一部分出去 byte[] buffer3 = {'a',97,98,99}; os.write(buffer3); os.write("\r\n".getBytes()); //os.flush();//写数据必须,刷新数据,可以继续使用流 os.close(); //稀释资源,包含了刷新的!关闭后流不可以继续使用 //byte[] buffer4 = {99}; //os.write(buffer4);//无法执行了,因为流已经关闭了 }
5、文件拷贝(输入流和输出流的综合使用)
try {
//1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("D:\\Desktop\\PSCC【做图软件】64位\\字节跳动.mp4");
//2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("D:\\Desktop\\字节跳动.mp4");
//3、定义一个字节数组转移数组数据
byte[] buffer1 = new byte[1024];
int len;//记录每次读取的字节数
while ((len = is.read()) != -1) {
os.write(buffer1,0,len);
}
System.out.println("复制完成了!");
//4、关闭流
os.close();
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
三、资源稀释的方式
1、try-catch-finally
finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源
特点:被finally控制的语句最终一定会执行,除非JVM退出
异常处理标准格式:try….catch…finally
public static void main(String[] args) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
//1、创建一个字节输入流管道与原视频接通
is = new FileInputStream("D:\\Desktop\\PSCC【做图软件】64位\\字节跳动.mp4");
//2、创建一个字节输出流管道与目标文件接通
os = new FileOutputStream("D:\\Desktop\\字节跳动.mp4");
//3、定义一个字节数组转移数组数据
byte[] buffer1 = new byte[1024];
int len;//记录每次读取的字节数
while ((len = is.read()) != -1) {
os.write(buffer1,0,len);
}
System.out.println("复制完成了!");
//System.out.println(10/0);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally{
//无论代码是正常结束,还是出现异常都要最后执行这里
System.out.println("==================finally============");
//4、关闭流
try {
if(os != null)
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(os != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(test(10, 2));
}
public static int test(int a , int b){
try {
int c = a / b;
//System.exit(0);//干掉JVM虚拟机
return c;
}catch (Exception e){
e.printStackTrace();
return -111111; // 计算出现bug.
}finally {
System.out.println("--finally--");
// 哪怕上面有return语句执行,也必须先执行完这里才可以!
// 开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
return 100;
}
}
如果在finally里面return的话,就跟前面的内容没有关系了
2、try-with-resource
(1)JDK7的方式
public void main(String[] args) {
try (
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("file-io-app/src/out04.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");
// int age = 23; // 这里只能放资源
MyConnection connection = new MyConnection(); // 最终会自动调用资源的close方法
)
{
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}
}
class MyConnection implements AutoCloseable{
@Override
public void close() throws IOException {
System.out.println("连接资源被成功释放了!");
}
}
(2)JDK9的方式
public static void main(String[] args) throws Exception {
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("file-io-app/src/out04.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");
/**try ( is ; os ) {
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}*/
}
四、字符流的使用
1、文件字符输入流-一次读取一个字符
/** 目标:字符输入流的使用。 IO流的体系: 字节流 字符流 字节输入流 字节输出流 字符输入流 字符输出流 InputStream OutputStream Reader Writer (抽象类) FileInputStream FileOutputStream FileReader FileWriter (实现类) c.FileReader:文件字符输入流。 -- 作用:以内存为基准,把磁盘文件的数据以字符的形式读入到内存。 简单来说,读取文本文件内容到内存中去。 -- 构造器: public FileReader(File file):创建一个字符输入流与源文件对象接通。 public FileReader(String filePath):创建一个字符输入流与源文件路径接通。 -- 方法: public int read(): 读取一个字符的编号返回! 读取完毕返回-1 public int read(char[] buffer):读取一个字符数组,读取多少个字符就返回多少个数量,读取完毕返回-1 小结: 字符流一个一个字符的读取文本内容输出,可以解决中文读取输出乱码的问题。 字符流很适合操作文本文件内容。 但是:一个一个字符的读取文本内容性能较差!! */
public static void main(String[] args) throws Exception { //目标:每次读取一个字符 //1、创建一个字符输入流管道与源文件接通 Reader fr = new FileReader("src/data06.txt"); //2、赌气而一个字符返回,没有读取的字符时返回-1 /**int code1 = fr.read(); System.out.println((char)code1);//code打出的是编号,通过“(char)”转换成字符 int code2 = fr.read(); System.out.println((char)code2); */ //3使用循环读取字符 int code; while((code = fr.read()) != -1){ System.out.print((char)code); } }
2、文件字符输入流-一次读取一个字符数组
/** 目标:字符输入流的使用-按照字符数组读取。 IO流的体系: 字节流 字符流 字节输入流 字节输出流 字符输入流 字符输出流 InputStream OutputStream Reader Writer (抽象类) FileInputStream FileOutputStream FileReader FileWriter (实现类) c.FileReader:文件字符输入流。 -- 作用:以内存为基准,把磁盘文件的数据以字符的形式读入到内存。 简单来说,读取文本文件内容到内存中去。 -- 构造器: public FileReader(File file):创建一个字符输入流与源文件对象接通。 public FileReader(String filePath):创建一个字符输入流与源文件路径接通。 -- 方法: public int read(): 读取一个字符的编号返回! 读取完毕返回-1 public int read(char[] buffer):读取一个字符数组, 读取多少个字符就返回多少个数量,读取完毕返回-1 小结: 字符流按照字符数组循环读取数据,可以解决中文读取输出乱码的问题,而且性能也较好!! */
public static void main(String[] args) throws Exception {
//1、创建一个文件字符输入流与源文件接通
Reader fr = new FileReader("src/data07.txt");
//2
char[] buffer = new char[1024];//1k字符
int len;
while((len = fr.read(buffer)) != -1){
String rs = new String(buffer, 0, len);//把桶里的数据转化成字符串
System.out.print(rs);
}
}
3、文件字符输出流
/** 目标:字符输出流的使用。 IO流的体系: 字节流 字符流 字节输入流 字节输出流 字符输入流 字符输出流* InputStream OutputStream Reader Writer (抽象类) FileInputStream FileOutputStream FileReader FileWriter (实现类) d.FileWriter文件字符输出流的使用。 -- 作用:以内存为基准,把内存中的数据按照字符的形式写出到磁盘文件中去。 简单来说,就是把内存的数据以字符写出到文件中去。 -- 构造器: public FileWriter(File file):创建一个字符输出流管道通向目标文件对象。 public FileWriter(String filePath):创建一个字符输出流管道通向目标文件路径。 public FileWriter(File file,boolean append):创建一个追加数据的字符输出流管道通向目标文件对象。 public FileWriter(String filePath,boolean append):创建一个追加数据的字符输出流管道通向目标文件路径。 -- 方法: a.public void write(int c):写一个字符出去 b.public void write(String c)写一个字符串出去: c.public void write(char[] buffer):写一个字符数组出去 d.public void write(String c ,int pos ,int len):写字符串的一部分出去 e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去 小结: 字符输出流可以写字符数据出去,总共有5个方法写字符。 覆盖管道: Writer fw = new FileWriter("Day10Demo/src/dlei03.txt"); // 覆盖数据管道 追加数据管道: Writer fw = new FileWriter("Day10Demo/src/dlei03.txt",true); // 追加数据管道 换行: fw.write("\r\n"); // 换行 结论:读写字符文件数据建议使用字符流。复制文件建议使用字节流。 */
public static void main(String[] args) throws Exception {
// 1、创建一个字符输出流管道与目标文件接通
// Writer fw = new FileWriter("file-io-app/src/out08.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("file-io-app/src/out08.txt", true); // 覆盖管道,每次启动都会清空文件之前的数据
// a.public void write(int c):写一个字符出去
fw.write(98);
fw.write('a');
fw.write('徐'); // 不会出问题了
fw.write("\r\n"); // 换行
// b.public void write(String c)写一个字符串出去
fw.write("abc我是中国人");
fw.write("\r\n"); // 换行
// c.public void write(char[] buffer):写一个字符数组出去
char[] chars = "abc我是中国人".toCharArray();
fw.write(chars);
fw.write("\r\n"); // 换行
// d.public void write(String c ,int pos ,int len):写字符串的一部分出去
fw.write("abc我是中国人", 0, 5);
fw.write("\r\n"); // 换行
// e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
fw.write(chars, 3, 5);
fw.write("\r\n"); // 换行,不需要像字节输出流那样,转化成数组才可以输出
// fw.flush();// 刷新后流可以继续使用
fw.close(); // 关闭包含刷线,关闭后流不能使用
}
五、整体总结