1.字节输出流的数据追加和换行(掌握)
我们前已经学习对于字节输出流FileOutputStream的构造方法特点:如果指定文件存在,则会覆盖之前文件中的数据,注意我们之前使用的构造方法是:
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。
如果我们调用构造方法不想覆盖之前文件中的内容,而是进行新的数据追加到原来数据的后面,我们使用其他构造方法:
1.FileOutputStream(File file, boolean append)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
2.FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。
参数:
name:表示操作文件的路径
append:值如果是true,表示追加,如果是false,表示是覆盖,默认是false
代码演示:
package com.itheima.sh.outputstream_01;
import java.io.FileOutputStream;
/*
数据的追加和换行:
1.FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。
参数:
name:表示操作文件的路径
append:值如果是true,表示追加,如果是false,表示是覆盖,默认是false
数据的换行:要求是跨平台的
在windows系统中换行符是:\r\n
//获取当前系统的行分隔符
String lineSeparator = System.lineSeparator();
*/
public class OutputStreamDemo01 {
public static void main(String[] args) throws Exception{
//1.创建字节输出流对象 true表示数据的追加
FileOutputStream fos = new FileOutputStream("1.txt",true);
//2.写数据
//获取当前系统的行分隔符
String lineSeparator = System.lineSeparator();
fos.write((lineSeparator+"传智播客").getBytes());
fos.write((lineSeparator+"黑马程序员").getBytes());
//3.关闭资源
fos.close();
}
}
小结:
1.FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。
参数:
name:表示操作文件的路径
append:值如果是true,表示追加,如果是false,表示是覆盖,默认是false
2.获取当前系统的行分隔符
String lineSeparator = System.lineSeparator();
2.字节输入流【InputStream】(掌握)
-
就是将持久设备上的字节数据使用输入流读取到内存中,供使用。
-
父类:InputStream属于抽象类,不能创建对象,使用子类FileInputStream创建对象,使用子类的构造方法:
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 FileInputStream(String name) ****** 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。 参数:name表示要读取的文件路径 注意:由于这里是输入流,那么要求关联的文件一定要存在,否则就会报异常
-
方法:
-
close()关闭资源
-
int read()每调用一次read方法就会读取一个字节数据,将读取的字节数据返回给返回值int,读取到文件末尾返回-1
package com.itheima.sh.inputstream_02; import java.io.FileInputStream; /* 字节输入流: 1.构造方法: FileInputStream(String name) ****** 参数:name表示要读取的文件路径 注意:由于这里是输入流,那么要求关联的文件一定要存在,否则就会报异常: java.io.FileNotFoundException: 2.txt (系统找不到指定的文件。) 2.方法: 1)close()关闭系统资源 2)int read() 每调用一次read方法就会读取一个字节数据, 将读取的字节数据返回给返回值int,读取到文件末尾返回-1 */ public class InputStreamDemo01 { public static void main(String[] args) throws Exception { //1.创建字节输入流对象关联要读取的文件 FileInputStream fis = new FileInputStream("2.txt"); //2.读取字节数据 /* int i1 = fis.read(); System.out.println((char)i1);//a System.out.println((char)fis.read());//b System.out.println((char)fis.read());//c System.out.println(fis.read());//-1 System.out.println(fis.read());//-1*/ /* 上述每次读取一个字节数据,代码比较重复,我们可以使用循环来解决代码重复问题: 循环初始值: 循环条件:read方法返回-1就停止,read方法返回值不等于-1就继续读取文件 修改循环条件: int read() */ //循环初始值: int i; /* (i = fis.read()) != -1 表示循环条件 1)i = fis.read() 使用字节输入流对象fis调用read方法读取文件中的数据并赋值给变量i 2) (i = fis.read()) != -1 判断i的变量中的值是否等于-1,如果不等于-1,则整体是true 表示满足循环条件,则进入循环体。如果读取到文件末尾,返回-1给i,此时这里的整体是false 则不进入循环体 */ while ((i = fis.read()) != -1) { //循环体 表示读取文件 System.out.println((char)i); } //3.关闭资源 fis.close(); } }
小结:
读取单个字节数据的模板代码:
//循环初始值: int i; /* (i = fis.read()) != -1 表示循环条件 1)i = fis.read() 使用字节输入流对象fis调用read方法读取文件中的数据并赋值给变量i 2) (i = fis.read()) != -1 判断i的变量中的值是否等于-1,如果不等于-1,则整体是true 表示满足循环条件,则进入循环体。如果读取到文件末尾,返回-1给i,此时这里的整体是false 则不进入循环体 */ while ((i = fis.read()) != -1) { //循环体 表示读取文件 System.out.println((char)i); }
-
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 参数: b:表示字节数组,我们在使用该方法的时候需要定义一个空的字节数组用来 存储read方法每次读取的多个字节数据 ,一般建议数组长度是1024的整数倍 返回值:每次调用read方法读取的字节个数,读取到文件末尾read方法返回-1
-
注意细节
package com.itheima.sh.inputstream_02; import java.io.FileInputStream; import java.io.IOException; /* int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 参数: b:表示字节数组,我们在使用该方法的时候需要定义一个空的字节数组用来 存储read方法每次读取的多个字节数据 ,一般建议数组长度是1024的整数倍 返回值:每次调用read方法读取的字节个数,读取到文件末尾read方法返回-1 */ public class InputStreamDemo02 { public static void main(String[] args) throws Exception { method_3(); } private static void method_3() throws Exception{ //1.创建字节输入流对象关联文件 FileInputStream fis = new FileInputStream("3.txt"); //2.定义空的字节数组 byte[] buf = new byte[5]; //调用read方法读取数据 int len = fis.read(buf);//len表示每次读取的字节个数 /* 第一次: 5 hello */ System.out.println("len = " + len);//5 System.out.println(new String(buf,0,len));//hello /* 第二次读取: len = 5 \r\n wor */ len = fis.read(buf); System.out.println("len = " + len);//5 System.out.println(new String(buf,0,len));// /* 第三次读取: len = 5 ld\r\n J */ len = fis.read(buf); System.out.println("len = " + len);//5 System.out.println(new String(buf,0,len));// /* 第四次读取: len = 3 ava */ len = fis.read(buf); System.out.println("len = " + len);//5 System.out.println(new String(buf,0,len));// //关闭资源 fis.close(); } private static void method_2() throws Exception{ //1.创建字节输入流对象关联文件 FileInputStream fis = new FileInputStream("3.txt"); //2.定义空的字节数组 byte[] buf = new byte[5]; //调用read方法读取数据 int len = fis.read(buf);//len表示每次读取的字节个数 /* 第一次: 5 hello */ System.out.println("len = " + len);//5 System.out.println(new String(buf));//hello /* 第二次读取: len = 5 \r\n wor */ len = fis.read(buf); System.out.println("len = " + len);//5 System.out.println(new String(buf));// /* 第三次读取: len = 5 ld\r\n J */ len = fis.read(buf); System.out.println("len = " + len);//5 System.out.println(new String(buf));// /* 第四次读取: len = 3 ava J */ len = fis.read(buf); System.out.println("len = " + len);//5 System.out.println(new String(buf));// //关闭资源 fis.close(); } private static void method_1() throws IOException { //1.创建字节输入流对象关联文件 FileInputStream fis = new FileInputStream("3.txt"); //2.定义空的字节数组 byte[] buf = new byte[1024]; //3.调用read方法一次性读取多个字节 int len = fis.read(buf); //如何将字节数组中的数据转换为字符串 //String(byte[] bytes) 字节数组 //String(byte[] bytes, int offset, int length) //bytes 字节数组 offset :从字节数组哪个索引开始转换 length:转换的字节个数 //将上述整个字节数组转换为字符串 System.out.println(new String(buf)); //关闭资源 fis.close(); } }
图解:
-
小结:
以后我们都是读取几个字节就操作几个字节。
//写出
1.void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 *******
//将字节数组变为字符串
2.String(byte[] bytes, int offset, int length)
bytes 字节数组 offset :从字节数组哪个索引开始转换 length:转换的字节个数
-
读取多个字节的模板代码编写:
/* 读取多个字节模板 int read(byte[] b) */ private static void method_4() throws Exception{ //1.创建字节输入流对象关联文件 FileInputStream fis = new FileInputStream("3.txt"); //2.定义空的字节数组 byte[] buf = new byte[1024]; //定义变量保存每次读取的字节个数 int len; //3.使用循环读取 //将每次读取的多个字节数据放到字节数组buf中,然后将读取的字节个数返回给len //然后判断len是否等于-1.不等于-1说明没达到文件末尾,可以进入循环操作 while((len=fis.read(buf))!=-1){ //输出控制台 System.out.println(new String(buf,0,len)); } //4.释放资源 fis.close(); }
3.使用字节流进行文件的复制(课下必须完成)
package com.itheima.sh.out_in_stream_test_03;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/*
需求:将D:\1.mp3复制到F:\1.mp3
分析:
数据源文件:D:\1.mp3 创建字节输入流FileInputStream对象将数据源文件读取到内存中
目的地文件:F:\1.mp3 创建字节输出流FileOutputStream将内存中的数据写到目的地文件中
步骤:
1.创建字节输入流FileInputStream对象关联数据源文件D:\1.mp3
2.创建字节输出流FileOutputStream对象关联目的地文件F:\1.mp3
3.创建字节数组,大小是1024整数倍
4.定义变量保存每次读取的字节个数
5.使用字节输入流读取数据源文件
6.使用字节输出流将内存中的数据源文件写到目的地文件中
7.关闭资源
*/
public class CopyFileTest01 {
public static void main(String[] args) throws Exception{
//1.创建字节输入流FileInputStream对象关联数据源文件D:\1.mp3
FileInputStream fis = new FileInputStream("D:\\1.mp3");
//2.创建字节输出流FileOutputStream对象关联目的地文件F:\1.mp3
FileOutputStream fos = new FileOutputStream("F:\\1.mp3");
//3.创建字节数组,大小是1024整数倍
byte[] buf = new byte[1024];
//4.定义变量保存每次读取的字节个数
int len;
//5.使用字节输入流读取数据源文件
while((len=fis.read(buf))!=-1){
//6.使用字节输出流将内存中的数据源文件写到目的地文件中
fos.write(buf,0,len);
}
//7.关闭资源
fos.close();
fis.close();
}
}
4.字节流读取字符的问题(理解)
使用字节流可以操作任意类型的文件,但是有些情况下使用字节流操作文本文件会出现乱码问题。
package com.itheima.sh.char_04;
import java.io.FileInputStream;
/*
字节流读取字符的问题:
需求:使用字节流读取D:\\out.txt记事本以下的内容:
abc你好
*/
public class Demo01 {
public static void main(String[] args) throws Exception{
//创建字节输入流对象关联文件
FileInputStream fis = new FileInputStream("D:\\out.txt");
//读取文件内容
/*
如果将字节数组的长度定义为4,那么这里读取数据就会出现问题:
英文字母占一个字节
abc 占3个字节
你这里占3个字节,目前数组只能存储一个字节,所以将你的第一个字节放到数组中,然后我们将整个字节数组直接变为字符串并输出
导致后面也会出现问题,你剩下的两个字节和后面的汉字好的一个字节组合,最后整个汉字的编码值都会产生问题
*/
byte[] buf = new byte[4];
//定义变量保存字节个数
int len;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
//关闭资源
fis.close();
}
}
小结:
1.由于汉字占多个字节,使用字节流读取时会产生问题
在java中为了解决上述字节流读取文本文件的问题,我们使用字符流操作文本文件,字符流只能操作文本文件,字符流可以一次性读取一个字符数据,不管该字符占多少个字节数据。
5.字符输入流【Reader】(掌握)
-
Reader表示字符输入流的父类,属于抽象类,不能创建对象,使用子类FileReader创建对象
构造方法:
1.FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 2.FileReader(String fileName) *********** 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
方法:
- 读取单个字符
int read() 读取单个字符。 每调用一次该方法都会将读取的字符数据存储到返回值,如果读取到文件末尾返回-1
代码演示:
package com.itheima.sh.reader_05; import java.io.FileReader; /* 字符输入流: 构造方法: 2.FileReader(String fileName) *********** 在给定从中读取数据的文件名的情况下创建一个新 FileReader。 方法: int read() 读取单个字符。 每调用一次该方法都会将读取的字符数据存储到返回值,如果读取到文件末尾返回-1 */ public class ReaderDemo01 { public static void main(String[] args) throws Exception{ //1.创建字符输入流对象 FileReader fr = new FileReader("D:\\out.txt"); //2.使用模板读取字符数据 //定义变量保存每次读取的字符数据 int ch; //使用循环读取 //ch=fr.read() 表示每调用一次read方法将读取的字符数据放到返回值ch中 //(ch=fr.read())!=-1 判断ch是否等于-1,如果不等于-1,说明没有读取到文件末尾,可以进入循环体 while((ch=fr.read())!=-1){ System.out.println((char)ch); } //关闭资源 fr.close(); } }
-
读取多个字符(必须掌握)
int read(char[] cbuf) 将字符读入数组。 参数: cbuf:存储多个字符数据,在使用read方法之前需要定义一个空的字符数组,大小也建议1024整数倍 返回值:每次调用read方法读取的字符个数
代码演示:
private static void method_2() throws Exception { //1.创建字符输入流对象 FileReader fr = new FileReader("D:\\out.txt"); //2.创建空的字符数组 char[] ch = new char[1024]; //3.定义变量保存每次读取的字符个数 int len; //4.循环读取 while ((len = fr.read(ch)) != -1) { //将上述字符数组中的数据转换为字符串并输出 //问题:如何将字符数组转换为字符串 //String(char[] value) //String(char[] value, int offset, int count) 使用这个 System.out.println(new String(ch, 0, len)); } //关闭资源 fr.close(); }
6.字符输出流【Writer】(掌握)
方法介绍
1.Writer表示将内存中的字符数据写到文本文件中,属于抽象类,不能创建对象,使用间接子类FileWriter创建对象,子类构造方法:
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
参数:
append的值是true,表示数据的追加 false不追加
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 **********
FileWriter(String fileName, boolean append) **********
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
参数:
fileName:关联要写的文件路径
append的值是true,表示数据的追加 false不追加
代码演示:
package com.itheima.sh.writer_06;
import java.io.FileWriter;
/*
字符输出流:Writer--FileWriter
1.构造方法
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 **********
参数:
fileName:关联要写的文件路径
注意事项:
1)文件不存在则创建文件
2)文件存在,则覆盖原来的数据
3)指定路径不存在会报异常 java.io.FileNotFoundException: aaa\4.txt (系统找不到指定的路径。)
2.方法:
1)void write(char[] cbuf) 写入字符数组。
2)abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
3)void write(int c) 写入单个字符。
4)void write(String str) 写入字符串。*******
5)void write(String str, int off, int len)
6) abstract void flush() 刷新该流的缓冲。
7)abstract void close() 关闭此流,但要先刷新它。
字符流注意事项:
1)字符流底层自带字节缓冲区,其实就是一个字节数组
2)如果使用字符输出流向指定文件中写数据的时候,那么字符输出流会将数据先存储到字节缓冲区中,而不是直接写到文件中
我们需要调用方法将字节缓冲区中的数据刷新到指定硬盘文件中,如果不刷新,那么数据就会存储到字节缓冲区中,然后关闭jvm
数据丢失
我们使用字符输出流中的刷新方法即可:
abstract void flush() 刷新该流的缓冲。
3)flush刷新方法和close关闭流方法区别?
flush() 刷新该流的缓冲。,刷新之后流对象还可以继续使用
close()方法表示将资源还给系统,关闭资源,流不能在使用了,否则会报错。关闭的之前会先刷新
*/
public class WriterDemo01 {
public static void main(String[] args) throws Exception{
//1.创建字符输出流对象
FileWriter fw = new FileWriter("4.txt");
//2.void write(char[] cbuf) 写入字符数组。
char[] ch = {'a','您','1'};
fw.write(ch);
//3.刷新字节缓冲区中的数据到指定文件中
fw.flush();
fw.write("我是锁哥");
fw.flush();
//7)abstract void close() 关闭此流,但要先刷新它。
fw.close();
//java.io.IOException: Stream closed 流已经关闭
//fw.write("我是岩岩");
//fw.flush();
}
}
flush和close区别:
写方法演示:
/*
1)void write(char[] cbuf) 写入字符数组。
2)abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
3)void write(int c) 写入单个字符。 这里可以传入整数也可以传入字符
4)void write(String str) 写入字符串。*******
5)void write(String str, int off, int len)
*/
private static void method_2() throws Exception{
//1.创建字符输出流对象
FileWriter fw = new FileWriter("5.txt");
//abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
/*char[] ch = {'a','您','1'};
//1 表示从索引1开始写数据 2表示写的字符个数
fw.write(ch,1,2);//您1
//刷新
fw.flush();*/
//void write(int c) 写入单个字符。
// fw.write(97);
// fw.write('A');
// 5)void write(String str, int off, int len)
//2 表示从索引2开始写数据 3表示写的字符个数
fw.write("黑马程序员",2,3);
//关闭流
fw.close();
}
数据的追加和换行
1.数据的追加使用构造方法:
FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
参数:
fileName:表示关联的文件路径
append:值是true,表示追加,false表示不追加
FileWriter(File file, boolean append)
根据给定的 File 对象构造一个 FileWriter 对象。
2.数据换行:
String lineSeparator = System.lineSeparator();
代码演示:
/*
数据的追加和换行
*/
public class WriterDemo02 {
public static void main(String[] args) throws IOException {
//1.创建字符输出流对象
FileWriter fw = new FileWriter("5.txt",true);
//获取行分隔符
String lineSeparator = System.lineSeparator();
//2.调用方法写数据
fw.write(lineSeparator+"传智播客");
fw.write(lineSeparator+"传智汇");
//3.刷新
fw.flush();
}
}
7.IO流的异常处理(掌握)
1.jdk7前处理异常的方式
在实际开发中我们对于异常都是捕获处理的。
package com.itheima.sh.reader_writer_test_07;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
需求:将D:\\test\\故事.txt复制到当前项目根目录故事.txt
步骤:
1.创建字符输入流对象关联数据源文件 D:\\test\\故事.txt
2.创建字符输出流对象关联目的地文件 故事.txt
3.使用字符输入流将数据源文件内容读取到内存中
4.使用字符输出流对象调用方法将内存中的数据源文件写到目的地文件中
5.每次写出之后要记得刷新
6.关闭资源
*/
public class Test01 {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建字符输入流对象关联数据源文件 D:\\test\\故事.txt
fr = new FileReader("D:\\test\\故事.txt");
//2.创建字符输出流对象关联目的地文件 故事.txt
fw = new FileWriter("故事.txt");
//3.使用字符输入流将数据源文件内容读取到内存中
char[] ch = new char[1024];
int len;
while ((len = fr.read(ch)) != -1) {
//4.使用字符输出流对象调用方法将内存中的数据源文件写到目的地文件中
fw.write(ch, 0, len);
//5.每次写出之后要记得刷新
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//只要获取了系统资源,无论是否发生异常,都要必须将获取的系统资源还给系统
//6.关闭资源
try {
//为了避免空指针异常 加判断
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.jdk7后处理异常的方式(必须掌握)
上述我们使用了jdk7前对异常进行捕获处理,发现针对释放资源的代码,写起来比较麻烦。所以从jdk7开始引入新的方式优化释放资源的代码,jdk7后只要和IO流有关的类都实现了一个自动释放资源的代码。
自动关闭资源的接口:AutoCloseable
只要实现该接口的实现类都可以按照如下方式书写代码:
try(要释放资源的类的对象){如果要释放资源的类的对象有多个,那么使用分号隔开,最后一个可以不使用分号
可能发生的异常代码
}catch(捕获的异常类 对象名){
处理异常
}
说明:
1.只要将对象放到小括号中,底层帮助我们释放,我们不需要书写释放资源的代码不用再写finally
代码演示:
package com.itheima.sh.reader_writer_test_07;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
try(要释放资源的类的对象){如果要释放资源的类的对象有多个,那么使用分号隔开,最后一个可以不使用分号
可能发生的异常代码
}catch(捕获的异常类 对象名){
处理异常
}
说明:
1.只要将对象放到小括号中,底层帮助我们释放,我们不需要书写释放资源的代码不用再写finally
*/
public class Test02 {
public static void main(String[] args) {
//使用jdk7处理异常
try ( //1.创建字符输入流对象关联数据源文件 D:\\test\\故事.txt
FileReader fr = new FileReader("D:\\test\\故事.txt");
//2.创建字符输出流对象关联目的地文件 故事.txt
FileWriter fw = new FileWriter("故事.txt")
) {
//3.使用字符输入流将数据源文件内容读取到内存中
char[] ch = new char[1024];
int len;
while ((len = fr.read(ch)) != -1) {
//4.使用字符输出流对象调用方法将内存中的数据源文件写到目的地文件中
fw.write(ch, 0, len);
//5.每次写出之后要记得刷新
// fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
小结:
try(要释放资源的类的对象){如果要释放资源的类的对象有多个,那么使用分号隔开,最后一个可以不使用分号
可能发生的异常代码
}catch(捕获的异常类 对象名){
处理异常
}
说明:
1.只要将对象放到小括号中,底层帮助我们释放,我们不需要书写释放资源的代码不用再写finally
8.在idea中配置模板代码:
在以后开发中,对于某部分代码,我们使用率会很高,虽然我们会写,但是写也要需要时间,那么这样经常书写的代码我们可以配置到
idea模板中,然后使用相应的快捷键即可写出来。
配置步骤:
3.