终于学习到了本人认为Java中体系最复杂的IO流这一章了。如果在学习之前没有好好的捋一捋IO流的体系,各种流之间的关系,学习起来真的会非常吃力。
IO概述:
IO流(Input&Output):是为了解决不同设备之间数据传输的问题。IO流一般配合着网络,才能发挥其强大的功能。
IO的体系图
分析:
在学习IO体系的时候,我们一般是按照字符流和字节流来展开的。每一个体系中,都有对应的普通的流和高效的流。
首先,学习字符流
字符流包括:
A:字符输入流 Reader —— 是一个抽象类,要想实现数据的输入,需要new其子类对象
B:字符输出流 Writer —— 是一个抽象类,new其子类对象
在学习过程中,通常会遇到这样的问题:一个流到底是输入流还是输出流,会有不同的争论。其实这个是相对的,一般情况下,我们都是站在Java程序的角度上去看待这个问题。在Java的角度,流进Java程序的,就是输入流,流出Java程序的就是输出流。
需求:我们一般都会使用程序对文本文件来处理数据。要想对文件进行读取和写入数据,那么需要用到与File有关的输入输出流
**FileReader & FileWriter** 下面我们来学习一下这两个类
代码: FileWriter
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) {
// 创建FileWriter对象
FileWriter fw = null;
try {
/*
* 创建字符输出流对象做了几件事? A:调用系统功能创建了指定的文件; B:创建了字符输出流对象; C:把字符输出流对象指向创建的文件
*/
fw = new FileWriter("a.txt");
//调用写数据的功能
fw.write("你好,傻逼IO");
/*
* 为什么要flush() 在Java中,1个字符=2个字节
* 在系统文件数据的底层单位是字节,而我们现在使用的是字符输出流,所以不能直接把数据写入文件,
* 在这里,是把要输出的数据存放在缓冲区里,只有我们刷新缓冲区,数据才会被写入到文件中.
*/
fw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
/*
* 为什么一定要close() 1.让流对象变成垃圾 2.通知系统去释放与文件相关的资源.
*/
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
结果:
分析:
A:创建字符流对象,做了几件事? 1、调用了系统功能创建了指定文件;2、创建了字符输出流对象;3、把字符输出流对象
指向了创建的文件。
B:为什么要flush() 在Java中,1个字符=2个字节
在系统文件数据的底层单位是字节,而我们现在使用的是字符输出流,所以不能直接把数据写入文件,
在这里,是把要输出的数据存放在缓冲区里,只有我们刷新缓冲区,数据才会被写入到文件中.
C:为什么一定要close()
1、让流变成垃圾,释放资源
2、通知系统去释放与文件管理相关的资源。
FileWriter的几个小问题
字符输出流操作步骤:
* A:创建字符输出流对象
* B:调用字符输出流对象的写入数据方法,并刷新缓冲区
* C:释放资源
*
* 三个小问题:
*
* 问题1:为什么FileWriter没有无参构造方法?
* 因为在写数据的时候,必须要明确些到哪里去.
*
* 问题2:flush()和close()的区别?
* flush(): 只刷新缓冲区,流对象还可以使用
* close(): 先刷新缓冲区,再关闭流对象,流对象不可以继续被使用.
*
* 问题3:难道每次调用完流对象的写入数据方法时,都需要刷新缓冲区吗?是否可以不刷新,直接等到close()来解决?
* 这两种方法都不可取.
代码:FileWriter 加入了捕捉异常标准代码
package cn.itcast.filewriter;
/*
* FileWriter加入异常处理的标准代码
*
*/
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDem3 {
public static void main(String[] args) {
// 创建字符输出流对象
FileWriter fw = null;
try {
fw = new FileWriter("c.txt");
fw.write("干了这杯热翔");
fw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileReader
FileReader 字符输入流
* 构造方法:
* FileReader(File file)
* FileReader(String fileName) 选用
*
* 读取方法:
* int read(): 每次读取一个字符,没读取完一个字符,自动移动到下一个数据位置等待读取
* 当没有数据可读时,会返回-1
int read(char[] cbuf):
代码:
public class FileReaderDemo {
public static void main(String[] args) {
// 创建字符输入流对象
FileReader fr = null;
try {
fr = new FileReader("FileReaderDemo.java");
// int ch = fr.read();
// while(ch != -1){
// System.out.print((char)ch);
// ch = fr.read();
// }
//开发是的写法
int ch = 0;
while((ch=fr.read())!=-1){
System.out.print((char)ch);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果:
分析:
读取一个文件,在控制台上输出
FileReader读取数据方式二:
* int read(char[] cbuf): 每次读取数据,把数据存储到字符数组中,读取长度自定义,一般为1024的整数倍
* 返回值是实际读取的长度.
代码2:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo3 {
public static void main(String[] args) throws IOException {
//创建字符输入流对象
FileReader fr = new FileReader("FileReaderDemo.java");
//读取 开发中的代码
char[] chs = new char[1024];
int len = 0;
while((len = fr.read(chs))!=-1){
System.out.print(new String(chs,0,len));
}
}
}
结果:
分析:
这个是开始中的标准代码,每次读取1024个字符,大大的提高了效率。在开发中,一般这个字符数组的大小,都是设置为1024的整数倍。
那么在学习完FileReader和FileWriter两个对文件操作的类后,就可以用这些学习过的方法,来进行文件的复制了。
需求:将本项目目录下的FileReaderDemo.java文件,复制到D:\copy.java
* 思路:
* 数据源:
* FileReaderDemo.java – Reader –FileReader
*
* 目的地:
* D:\copy.java – Writer – FileWriter
代码;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 创建字符输入流对象
FileReader fr = new FileReader("FileReaderDemo.java");
// 创建字符输出流对象
FileWriter fw = new FileWriter("D:\\copy.java");
// 读入数据
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fw.flush();
// 释放资源
fw.close();
fr.close();
}
}
结果:
相对高效的复制操作,其实也就是用了字符数组的原理:
代码:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFileDemo2 {
public static void main(String[] args) throws IOException {
//创建字符输入流对象
FileReader fr = new FileReader("FileReaderDemo.java");
//创建字符输出流对象
FileWriter fw = new FileWriter("d:\\copy1.java");
//创建一个大小为1024的字符数组,存储输入流读取到的数据
char[] chs = new char[1024];
int ch = 0;
//读取
while((ch = fr.read(chs))!= -1){
//将存储在字符数组中的数据写进指定的文件
fw.write(chs);
}
//刷新输出流
fw.flush();
//关闭资源
fw.close();
fr.close();
}
}
学习完简单的文件字符流类,下面来学习一下简单的文件字节流类
FileInputStream 和 FileOutputStream
在IO体系里,基本上方法和格式都是固定的,出了部分类有自己特有的方法外,其他基本上都是通用。那么,我们直接使用在FileReader 和 FileWriter类中的方法,进行操作
通过字节流往文件中写数据。
*
* 字节输出流操作步骤:
* A:创建字节输出流对象
* B:调用写数据的方法
* C:释放资源
FileOutputStream
代码:
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
// FileOutputStream fos = new FileOutputStream("a.txt",true);
FileOutputStream fos = new FileOutputStream("a.txt");
// 调用写数据的方法
// 写入一个字节
// fos.write(97);
// fos.write(98);
// fos.write(99);
// fos.flush();
// 写一个字节数组
// byte[] bys = { 97, 98, 99, 100, 101 };
byte[] bys = "abcde".getBytes();
// fos.write(bys);
// 写一个字节数组的一部分
fos.write(bys, 0, 2);
// 释放资源
fos.close();
}
}
FileInputStream
代码:
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("b.txt");
// 调用读取数据的方式,并显示
// 方式1
// int by = 0;
// while ((by = fis.read()) != -1) {
// System.out.println(by);
// }
// 方式2
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
// 释放资源
fis.close();
}
}
——————————————————————————————————————————-到这里,已经把简单的字符流和字节流学习了。不过这些方法都属于比较低效的。
下面,我们学习一下高效的字符缓冲流和字节缓冲流
A:字符缓冲流
|–BufferedWriter
|–BufferedReader
B:字节缓冲流
|–BufferedInputStream
|–BufferedOutputStream
字符缓冲流
* 我们开始自己定义数组,给出缓冲区大小,是为了提高效率。
* 那么,java在设计类的时候,它也考虑到了这一点,所以,就提供了一种高效的流。带缓冲区的流。
* BufferedWriter:写入数据
* BufferedReader:读取数据
*
* BufferedWriter:
* 构造方法:BufferedWriter(Writer out)
* 为什么传递的是Writer呢?
* 因为BufferedWriter这种流,被称为缓冲流,它只提供数据的缓冲功能。
* 真正的读写还得靠别人。所以,我们这里将使用FileWriter作为参数传递。
*
* 缓冲区只提供缓冲功能,没有真正的读写。
*
* 基本流:能直接进行读写的流对象。
* 高级流:站在基本流的基础上,提供一些特殊的功能。(处理流。)
代码:BufferedWriter
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
// 创建缓冲流对象
// Writer fw = new FileWriter("c.txt");
// FileWriter fw = new FileWriter("c.txt");
// BufferedWriter bw = new BufferedWriter(fw);
BufferedWriter bw = new BufferedWriter(new FileWriter("c.txt"));
// 写入数据
bw.write("hello");
bw.flush();
// 释放资源
// fw.close();// 这个不用关闭
bw.close();
}
}
代码:FileReader:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/*
* 字符缓冲流读取数据
*/
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
// 创建字符缓冲流对象
BufferedReader br = new BufferedReader(new FileReader("c.txt"));
// 读取数据
// int ch = 0;
// while ((ch = br.read()) != -1) {
// System.out.print((char) ch);
// }
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
System.out.print(new String(chs, 0, len));
}
// 释放资源
br.close();
}
}
在学习简单IO流的时候,有一个复制文件的操作,如果要复制的文件比较大,可以使用字符缓冲流来提高效率
代码:
package cn.itcast_03;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
* 用字符高效流复制文本文件。
*
* 数据源:
* FileOutputStreamDemo.java
* 目的地:
* Copy.java
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
// 封装数据源和目的地
BufferedReader br = new BufferedReader(new FileReader(
"FileOutputStreamDemo.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
// 一次一个字符
// int ch = 0;
// while ((ch = br.read()) != -1) {
// bw.write(ch);
// // bw.flush();
// }
// 一次一个字符数组
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
}
// 释放资源
bw.close();
br.close();
}
}
结果:
由于操作速度非常快,都是用毫秒值来计算的,那么我们要怎样才能知道,到底字符缓冲流,是不是真的比较高效呢?
我们在学习System类的时候,有一个currentTimeMillis()的方法,可以用来计算一段代码之间的时间
不多说,马上改造:
代码
package cn.itcast_04;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 需求:把d:\\哥有老婆.mp4复制到项目路径下x.mp3(x=1,2,3,4)
*
* @author Administrator
*
* 基本字节流一次读写一个字节: 共耗时:66922毫秒
* 基本字节流一次读写一个字节数组: 共耗时:94毫秒
* 高效字节流一次读写一个字节:共耗时:1235毫秒
* 高效字节流一次读写一个字节数组:共耗时:47毫秒
*/
public class CopyMP3 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
// method1();
// method2();
// method3();
method4();
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "毫秒");
}
//高效字节流一次读写一个字节数组
private static void method4() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"d:\\哥有老婆.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("4.mp4"));
byte[] bys = new byte[1024];
int len = 0;
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bos.close();
bis.close();
}
//基本字节流一次读写一个字节数组
private static void method3() throws IOException {
FileInputStream fis = new FileInputStream("d:\\哥有老婆.mp4");
FileOutputStream fos = new FileOutputStream("3.mp4");
byte[] bys = new byte[1024];
int len = 0;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
fos.close();
fis.close();
}
// 高效字节流一次读写一个字节
private static void method2() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"d:\\哥有老婆.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("2.mp4"));
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
}
// 基本字节流一次读写一个字节
private static void method1() throws IOException {
FileInputStream fis = new FileInputStream("d:\\哥有老婆.mp4");
FileOutputStream fos = new FileOutputStream("1.mp4");
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
}
结果:
* 基本字节流一次读写一个字节: 共耗时:66922毫秒
* 基本字节流一次读写一个字节数组: 共耗时:94毫秒
* 高效字节流一次读写一个字节:共耗时:1235毫秒
* 高效字节流一次读写一个字节数组:共耗时:47毫秒
分析:
高效流确实会更有效率,一般在开发中,我们都是选择高效流来使用。
BufferedWriter 和 BufferedReader 的特有方法
* BufferedWriter:
* public void newLine():根据系统平台写入行分隔符
*
* BufferedReader:
* public String readLine():一次读取一行的数据。但是不包含换行符。
代码如下:
public class BufferedStreamDemo {
public static void main(String[] args) throws IOException {
//创建流对象
BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
// 读取数据
String line = null;
while((line=br.readLine())!=null){
System.out.println(line);
}
br.close();
}
}