Java I/O流概述
一、IO流概述
- 流的分类
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
- 流的继承关系
4个抽象基类:
InputStream
OutputStream
Reader
Writer
说明:上图中蓝色背景的流是本章的重点。
二、节点流的使用
2.1 FileReader 和 FileWriter
//FileReader的基本使用
@Test
public void testFileReader() throws IOException {
//1.创建文件对象
File file1 = new File("hello.txt");
//2. 创建流对象
FileReader fr = new FileReader(file1);
//3. 读入数据的过程
int data = fr.read();
while (data != -1) {
System.out.print((char) data);
data = fr.read();
}
//4.对于流的关闭操作,必须手动实现
fr.close();
}
- 优化以后:
//对上述程序优化1:在io流中,凡是需要手动关闭流资源的程序中,都需要使用try-catch-finally处理异常
@Test
public void testFileReader1() {
FileReader fr = null;
try {
//1.创建文件对象
File file1 = new File("hello.txt");
//2. 创建流对象
fr = new FileReader(file1);
//3. 读入数据的过程
int data;//保存每次读取的字符数据
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.对于流的关闭操作,必须手动实现
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 进一步优化:
//对上述程序优化2:每次读取一个字符数组
@Test
public void testFileReader2() {
FileReader fr = null;
try {
//1.创建文件对象
File file1 = new File("hello.txt");
//2. 创建流对象
fr = new FileReader(file1);
//3. 读入数据的过程
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符的个数
while((len = fr.read(cbuf)) != -1){
//错误的:
// for (int i = 0; i < cbuf.length; i++) {
// System.out.print(cbuf[i]);
//
// }
//错误的:
// String str = new String(cbuf);
// System.out.print(str);
//正确的:
// for (int i = 0; i < len; i++) {
// System.out.print(cbuf[i]);
// }
//正确的:
String str = new String(cbuf,0,len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.对于流的关闭操作,必须手动实现
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- FileWriter的使用
@Test
public void testFileWriter() {
FileWriter fw = null;
try {
//1. 创建输出到的File对象
File file1 = new File("info.txt");
//2. 创建输出流
// fw = new FileWriter(file1);
fw = new FileWriter(file1,true);
//3. 输出数据的过程
// fw.write('a');
// fw.write("I love You!");
fw.write("I love You!\n".toCharArray());
fw.write("You love him".toCharArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 手动的关闭资源
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 使用总结
结论:
1. 对于输入流来说,读取的文件一定要存在,否则会报FileNotFoundException
2. 对于输出流来说,要写出到的文件可以不存在。
如果不存在,则在执行过程中,会自动创建对应的文件
如果存在,1)使用FileWriter(File file) 或 FileWriter(File file,false):会对已有的文件进行覆盖
2)FileWriter(File file,true):在现有文件内容的末尾,追加内容
3. 流,因为都需要进行资源的关闭,所有本章所有的异常,只要涉及流资源的关闭,都必须使用try-catch-finally
- 综合使用FileReader和FileWriter实现文本文件的复制
/*
综合使用FileReader和FileWriter实现文本文件的复制
*/
@Test
public void testFileReaderWriter(){
FileReader fr = null;
FileWriter fw = null;
try {
//1.指明读取的File对象和写出到的File对象对应的文件
File srcFile = new File("hello.txt");
File destFile = new File("hello1.txt");
//不能处理非文本文件
// File srcFile = new File("baby.jpg");
// File destFile = new File("baby1.jpg");
//2. 创建对应的FileReader和FileWriter
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3. 读取并写出数据的过程
char[] cbuf = new char[5];
int len;//记录每次读入到char[]中字符的个数
while((len = fr.read(cbuf)) != -1){
// fw.write(cbuf);//错误的
fw.write(cbuf,0,len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
//方式一:
// try {
// if(fw != null)
// fw.close();
// } catch (IOException e) {
// e.printStackTrace();
// }finally{
// try {
// if(fr != null)
// fr.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
//方式二;
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 思考
//FileReader和FileWriter:属于字符流,不能用来处理非文本文件的数据!
//如果想处理非文本文件的数据,就需要使用字节流:FileInputStream和FileOutputStream
2.2 FileInputStream和FileOutputStream
//复制一个文件
@Test
public void testFileInputOutputStream() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件
// File srcFile = new File("baby.jpg");
// File destFile = new File("baby2.jpg");
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//2. 造流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3. 读取并写出的操作
byte[] buffer = new byte[5];
int len;//记录每次读取到buffer中的字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
// String data = new String(buffer, 0, len);
// System.out.print(data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
结论:
* 通常,我们使用字节流来处理非文本文件(.jpg, .mp3,.mp4,.avi,.doc,.ppt,.xls)
* 使用字符流来处理文本文件(.txt,.java,.py)
三、处理流之一:缓冲流的使用
3.1 基本结构
* 抽象基类 文件流 缓冲流(处理流的一种):提高数据读写的效率
* InputStream FileInputStream BufferedInputStream
* OutputStream FileOutputStream BufferedOutputStream
* Reader FileReader BufferedReader
* Writer FileWriter BufferedWriter
3.2 BufferedInputStream和BufferedOutputStream的使用
@Test
public void testBufferedInputOutput() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1. 造文件
File srcFile = new File("baby.jpg");
File destFile = new File("baby3.jpg");
//2. 造流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3. 读写的细节操作
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
//先关闭外面的流,再关闭内部的流
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
//可以省略
// fos.close();
// fis.close();
}
}
3. 3 对比文件流与缓冲流的执行效率
/*
对比FileInputStream + FileOutputSteam 与 BufferedInputStream + BufferedOutputStream的读写效率
*/
public void copyFileWithFiled(String src,String dest){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件
File srcFile = new File(src);
File destFile = new File(dest);
//2. 造流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3. 读取并写出的操作
byte[] buffer = new byte[1024];
int len;//记录每次读取到buffer中的字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void copyFileWithBuffered(String src,String dest){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1. 造文件
File srcFile = new File(src);
File destFile = new File(dest);
//2. 造流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3. 读写的细节操作
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
//先关闭外面的流,再关闭内部的流
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testCopyTime(){
long start = System.currentTimeMillis();
String src = "G:\\教学视频\\01-Java基础\\尚硅谷_200213大数据Java基础_宋红康\\6-每日视频\\01-作业题.avi";
String dest = "G:\\教学视频\\01-Java基础\\尚硅谷_200213大数据Java基础_宋红康\\6-每日视频\\test02.avi";
// copyFileWithFiled(src,dest);
copyFileWithBuffered(src,dest);
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//2028 - 507
}
3.4 BufferedReader和BufferedWriter的使用
@Test
public void testBufferedReaderWriter() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//1. 造文件、造流
br = new BufferedReader(new FileReader(new File("dbcp.txt")));
bw = new BufferedWriter(new FileWriter(new File("dbcp-1.txt")));
//2. 读写文件的细节
//写法一:
// char[] cbuf = new char[1024];
// int len;
// while ((len = br.read(cbuf)) != -1) {
// bw.write(cbuf, 0, len);
// }
//写法二:
String data;
while((data = br.readLine()) != null){
// bw.write(data + "\n");
bw.write(data);
bw.newLine();//换行
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 体会在BufferedReader中使用readLine()
四、处理流之二:转换流
4.1 介绍
* 一、
* 解码:字节、字节数组 ---> 字符、字符数组、字符串
* 编码:字符、字符数组、字符串 ---> 字节、字节数组
*
* 解码过程中,使用的字符集必须是当初编码时使用的字符集!否则,解码就会出现乱码!
*
* 二、转换流:
* InputStreamReader:将输入型的字节流转换为输入型的字符流
* OutputStreamWriter:将输出型的字符流转换为输出型的字节流
4.2 代码实现
//如下的代码应该还是需要使用try-catch-finally处理的!
@Test
public void testInputStreamReader() throws IOException {
//1.
FileInputStream fis = new FileInputStream("dbcp.txt");
// InputStreamReader isr = new InputStreamReader(fis);//默认与IDEA设置的字符集相同:utf-8
InputStreamReader isr = new InputStreamReader(fis, "utf-8");//显示指明字符集:utf-8
//2.
char[] cbuf = new char[1024];
int len;
while ((len = isr.read(cbuf)) != -1) {
String str = new String(cbuf, 0, len);
System.out.print(str);
}
//3.
isr.close();
}
//如下的代码应该还是需要使用try-catch-finally处理的!
@Test
public void testInputStreamReaderOutputStreamWriter() throws IOException {
//1.
InputStreamReader isr = new InputStreamReader(new FileInputStream("dbcp.txt"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("dbcp_gbk200213.txt"),"gbk");//指明编码时的字符集
//2.
char[] cbuf = new char[1024];
int len;
while((len = isr.read(cbuf)) != -1){
osw.write(cbuf,0,len);
}
//3.
osw.close();
isr.close();
}
五、处理流之三:对象流
5.1 举例说明序列化过程
//序列化过程:ObjectOutputStream:实现内存中的数据,写入到具体的文件中
@Test
public void testObjectOutputStream() throws Exception {
//1.创建文件和流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
//操作基本数据类型
// oos.writeByte();
// oos.writeBoolean();
//2.操作对象
oos.writeObject(new String("Tom"));
oos.flush();//刷新
oos.writeObject(new String("王辰硕"));
oos.flush();//刷新
//3.关闭资源
oos.close();
}
5.2 举例说明反序列化过程
//反序列化过程:ObjectInputStream:将磁盘文件中保存的对象,还原为内存中的对象
@Test
public void testObjectInputStream() throws IOException, ClassNotFoundException {
//1.
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"));
//2.
String s1 = (String) ois.readObject();
System.out.println(s1);
String s2 = (String) ois.readObject();
System.out.println(s2);
//3.
ois.close();
}
5.3 小结:
对象流的使用:
* 1. ObjectInputStream 和 ObjectOutputStream
* 2. 作用:用于存储和读取基本数据类型数据或对象的处理流。
* 它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
*
*
* 面试题:你是如何理解对象的序列化机制的?
* 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,
* 或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
5.4 要想自定义的类可序列化,则需要满足
/**
* @author shkstart
* @create 2020 下午 4:56
* <p>
* 要想自定义的类可序列化,则需要满足:
* ① 实现接口:java.io.Serializable
* ② 显式声明全局常量:serialVersionUID,用于唯一标识当前类
* ③ 要想当前类的对象可序列化,必须其所有的属性也是可以序列化的
*
* 说明:1. 默认情况下,基本数据类型的变量都可以序列化
* 2. ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
*
*/
public class Person implements Serializable {
static final long serialVersionUID = 43453452L;
private int age;
private String name;
private Account acct;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", acct=" + acct +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public Person(int age, String name, Account acct) {
this.age = age;
this.name = name;
this.acct = acct;
}
}
class Account implements Serializable {
private double balance;
static final long serialVersionUID = 43456453452L;
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
public Account(double balance) {
this.balance = balance;
}
}
六、(了解)处理流之四:标准的输入输出流
七、(了解)处理流之五:打印流
setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public Person(int age, String name, Account acct) {
this.age = age;
this.name = name;
this.acct = acct;
}
}
class Account implements Serializable {
private double balance;
static final long serialVersionUID = 43456453452L;
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
public Account(double balance) {
this.balance = balance;
}
}
## 八、(了解)处理流之四:标准的输入输出流
## 九、(了解)处理流之五:打印流
## 十、(了解)处理流之六:数据流
其他的处理流(了解):
标准的输入输出流
/*
标准的输入流:System.in 默认从键盘输入
标准的输出流:System.out 默认从显示屏输出
重定向:通过System类的setIn,setOut方法对默认设备进行改变。
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
*/
@Test
public void test1() {
Scanner scann = new Scanner(System.in);
System.out.println("请输入一个字符串");
String inf = scann.nextLine();
System.out.println(inf);
//关闭操作
scann.close();
}
/*
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,
直至当输入“e”或者“exit”时,退出程序。
*/
@Test
public void test2() {
System.out.println("请输入信息(退出输入e或exit):");
// 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { // 读取用户输入的一行数据 --> 阻塞程序
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
// 将读取到的整行字符串转成大写输出
System.out.println("-->:" + s.toUpperCase());
System.out.println("继续输入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
打印流
/*
打印流: PrintStream 和 PrintWriter
1. 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
2. System.out返回的是PrintStream的实例
*/
@Test
public void test3() {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {// 把标准输出流(控制台输出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
}
数据流
/*
数据流:
DataInputStream 和 DataOutputStream
作用:为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。
说明:可以使用对象流替换数据流
*/
@Test
public void test4() {
DataOutputStream dos = null;
try { // 创建连接到指定文件的数据输出流对象
dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dos.writeUTF("我爱北京天安门"); // 写UTF字符串
dos.writeBoolean(false); // 写入布尔值
dos.writeLong(1234567890L); // 写入长整数
System.out.println("写文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 关闭流对象
try {
if (dos != null) {
// 关闭过滤流时,会自动关闭它包装的底层节点流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test5() {
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream("destData.dat"));
String info = dis.readUTF();
boolean flag = dis.readBoolean();
long time = dis.readLong();
System.out.println(info);
System.out.println(flag);
System.out.println(time);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
随机存取文件流
/**
* RandomAccessFile类的使用:
* 1. 此类直接继承于java.lang.Object类
* 2. 此类实现类DataInput和DataOutput接口,此类既可以作为输入流,有可以作为输出流
* 3. 如果写出到的文件存在,则不会对文件进行覆盖,而是从头开始对文件内容进行覆盖
* 4. 可以实现从文件内容的指定位置开始写入数据:seek(int position)
*
* @author shkstart
* @create 2020 上午 10:13
*/
public class RandomAccessFileTest {
@Test
public void test1() {
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile("baby.jpg", "r");
raf2 = new RandomAccessFile("baby1.jpg", "rw");
byte[] buffer = new byte[1024];
int len;
while ((len = raf1.read(buffer)) != -1) {
raf2.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (raf2 != null)
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (raf1 != null)
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test2() throws IOException {
RandomAccessFile raf = new RandomAccessFile("hello.txt","rw");
raf.seek(5);
raf.write("xyz".getBytes());
raf.close();
}
}
八、介绍NIO
1. Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
2. NIO与IO的对比:
IO NIO
byte[] / char[] Buffer
流Stream FileChannel
NIO : Non-Blocking 非阻塞式
3.NIO的相关API:
|-----java.nio.channels.Channel
|-----FileChannel:处理本地文件
|-----SocketChannel:TCP网络编程的客户端的Channel
|-----ServerSocketChannel:TCP网络编程的服务器端的Channel
|-----DatagramChannel:UDP网络编程中发送端和接收端的Channel
4. NIO2
随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。
5.
Path:替换原有的File类,表示一个文件或文件目录
提供了丰富的方法
Paths:用来实例化Path
Files:用来操作文件或文件目录的工具类
九、练习题
- 简答题
* 说明流的三种分类方式
流的流向:输入流、输出流
单位的不同:字节流、字符流
流的角色:节点流、处理流
* 谈谈你对对象序列化机制的理解?我们以对象流为载体,讲解的。
序列化过程:对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
反序列化过程:当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
* 谈谈你对编码与解码过程的理解?
载体:String < ---- > byte[]
InputStreamReader\OutputStreamWriter
编码:String ---> byte[] ; OutputStreamWriter
解码: byte[] ----> String ; InputStreamReader
* 对象要想实现序列化,需要满足哪几个条件
* ① 实现接口:java.io.Serializable
* ② 显式声明全局常量:serialVersionUID,用于唯一标识当前类
* ③ 要想当前类的对象可序列化,必须其所有的属性也是可以序列化的
- 编程题
练习:
使用缓冲流实现a.jpg文件复制为b.jpg文件的操作
public class Exer01 {
public static void main(String[] args) {
String src = "/exer01/a.jpg" ;
String dest = "/exer01/b.jpg" ;
bufferedCopyFile(src,dest);
}
/**
* 使用缓冲流从原件中复制文件到目标文件中
* @param src 源文件的路径
* @param dest 目标文件的路径
*/
public static void bufferedCopyFile(String src , String dest){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
//1.创建IO流
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(dest));
//2.数据的读写
byte[] data = new byte[1024];
int length ;
while ((length = bis.read(data)) != -1){
bos.write(data,0,length);
}
System.out.println("文件复制成功");
}catch(Exception e){
e.printStackTrace();
}finally{
//3.关闭文件
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
练习:
将gbk格式的文件转换为utf-8格式存储
public class GBKToUTF_8 {
@Test
public void test() {
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
isr = new InputStreamReader(new FileInputStream(new File("test.txt")),"GBK");
osw = new OutputStreamWriter(new FileOutputStream(new File("test_copy.txt")), "UTF-8");
char[] cbuf = new char[8192];
int len;
while ((len = isr.read(cbuf)) != -1) {
osw.write(cbuf, 0, len);
}
System.out.println("格式转换成功<GBK → UTF-8>");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (osw != null)
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (isr != null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
十、小结
-
流的分类
-
流的体系结构:4个抽象基类、具体的流
-
4个文件流:FileXxxx,是最基本的处理文件的流
-
4个缓冲流:BufferedXxxx,是处理流的一种,包裹上面提到的4个文件流,可以提高读写效率
-
2个转换流:InputStreamReader、OutputStreamWriter
-
2个对象流:ObjectInputStream、ObjectOutputStream,对象的序列化机制
-
处理流的过程非常的规范!
1)创建文件及必要的流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(String path))); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(String path)); 2) 读写数据的过程: read(byte[] / char[]) ; write(byte[] / char[] , 0, len) 3) 关闭资源:必须在finally中显式的声明流的close()