JavaIO的整体框架图
IO流从方向上来说,可以分为输入流和输出流;
从传输内容上来说,可以分为字符流和字节流
防止记混的口诀
所谓的IO,说白了就是数据在内存和硬盘之间的传输
输入流 = %Reader = %InputStream,从硬盘写入内存;
输出流 = %Writer = %OutputStream,从内存写入硬盘;
站在内存的角度,输入流是读入,输出流是写出;
站在硬盘的角度,输入流是读出,输出流是写入
我们为了防止记忆错乱,我建议站在内存的角度记忆,因为In和Out能对应上入和出
输入流 = %Reader = %InputStream = 读 入内存
输出流 = %Writer = %OutputStream = 写 出
字符流
文本,我们能读的懂的都可以认为是字符流。比如:文章,java文件等等
字符流的本质是字符数组char[]
字符输入流的超类:
Reader: 子类FileReader,BufferedReader
字符输出流的超类:
Writer:子类FileWriter,BufferedWriter
字符流可以分为字符输入流Reader和字符输出流Writer
文件字符输入流FileReader
import java.io.*;
public class ReaderTest {
public static void main(String[] args) {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\hello.txt");
File file2 = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
Reader reader = null;
try {
reader = new FileReader(file);
// 1. 单个字符读取
int c = reader.read();// 这样只能一个字符一个字符地读
System.out.println((char)c);
// 2. while循环读取
while((c = reader.read()) != -1){
System.out.print((char)c);
}
// 3. reader填充字符数组
char[] cArr = new char[100];
// 填充
int len = reader.read(cArr);
System.out.println("读取的长度:" + len + " 读取的内容:" + Arrays.toString(cArr));
len = reader.read(cArr);
System.out.println("第二次读取的长度:" + len + " 读取的内容:" + Arrays.toString(cArr));
len = reader.read(cArr);
System.out.println("第三次读取的长度:" + len + " 读取的内容:" + Arrays.toString(cArr));
// 4. reader填充字符数组,并且拼接为字符串
char[] cArr2 = new char[200];
int len2 = -1;
while ((len2 = reader.read(cArr2)) != -1) {
String str = new String(cArr2, 0, len2 );
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件字符输出流FileWriter
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterTest {
public static void main(String[] args) {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\hello.txt");
Writer writer = null;
try {
// 1. 直接write会删掉之前全部的,然后放进去新的
writer = new FileWriter(file);
writer.write("Hello 111Writer!!!");
// 2. 在源文件后拼接字符流
writer = new FileWriter(file, true);
writer.write("append text!!!");
// 3. 循环分段拼接字符流
writer = new FileWriter(file, true);
for (int i = 0; i < 100; i++) {
writer.write("HelloWorld\n");
//每次写入10个helloworld的时候做一次flush
if (i % 10 == 0) {
writer.flush();
}
}
// 4. 字符数组(字符流)的直接输出(写入)
File file2 = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
writer = new FileWriter(file2);
//定义一个数组
char[] c = {'a', 'b', 'p', 'b', 'p'};
writer.write(c);
writer.write("\r\n");
writer.write(c, 2, 2);
writer.write("\r\n");
writer.write(98);
writer.write("\r\n");
writer.write("我们今生注定是沧桑", 2, 6);
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
//判断writer不是空防止空指针异常
if (writer != null) {
try {
//关闭流
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意:一般的writer会将原内容删除,再写新内容;如果想要在原有内容的基础上填充,就需要让append为true
Writer writer = new FileWriter(file, true);
flush方法是干什么的?
writer.flush()的作用在于将缓冲区的所有字符都发送给硬盘
只有Writer和OutputStream才有flush方法;但是不管读写都有自己的缓冲区,只不过读操作不需要我们手动清空缓冲区罢了
java在使用流时,都会有一个缓冲区,按一种它认为比较高效的方法来发数据:把要发的数据先放到缓冲区,缓冲区放满以后再一次性发过去,而不是分开一次一次地发.
而flush()表示强制将缓冲区中的数据发送出去,不必等到缓冲区满.
执行close()方法时,会强制执行一次flush()方法
但是如果忘了使用close(),也没有用flush()这个方法,很多情况下会出现流的另一边读不到数据的问题,特别是在数据特别小的情况下
我们测试用的数据都是很少的,FileWriter默认的缓冲区大小是8KB,显然到不了这个长度
为什么需要缓冲区(重点)
因为内存与硬盘的读写操作比较耗费时间,为了减少内存与磁盘的交互次数,降低时间消耗,我们使用缓冲区缓存一部分内容,只有缓冲区满了才会向硬盘发送写请求,这样做可以减少交互次数,降低耗时
使用FileReader和FileWriter完成文件拷贝
import java.io.*;
public class CopyFileTest {
public static void main(String[] args) throws IOException {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\hello.txt");
Writer writer = new FileWriter("helloCopy.txt", true);
Reader reader = new FileReader(file);
// 清空之前的内容
writer.write("");
// 一字节一字节地读取
char[] cArr = new char[1024];
int len = -1;
while ((len = reader.read(cArr)) != -1) {
String str = new String(cArr, 0, len);
// 读到的字符串写入文件中
writer.write(cArr, 0, len);
writer.flush();
}
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
}
}
缓冲字符输入流BufferedReader
public class BufferReaderTest {
public static void main(String[] args) {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
// BufferedReader是一行一行读取的
while((line = bufferedReader.readLine()) != null){
//打印一行
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭外层的对象的时候,内存的资源会自动的被关闭
if(bufferedReader != null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
BufferedReader读取数据是一行一行地读的
之前说过Reader和Writer都有自己的缓冲区,BufferedReader和FileWriter的缓冲区大小相同,区别在于BUfferedWriter只有缓冲区满了才会进行字符转码(将字符转化为字节)
缓冲字符输出流BufferedWriter
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class BufferWriterTest {
public static void main(String[] args) {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
BufferedWriter bufferWriter = null;
try {
bufferWriter = new BufferedWriter(new FileWriter(file, true));
bufferWriter.write("BufferedWriter写入的内容:11111");
//换行
bufferWriter.newLine();
bufferWriter.write("BufferedWriter写入的内容:22222");
bufferWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//资源关闭
if (bufferWriter != null) {
try {
bufferWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
使用BufferedReader和BufferedWriter完成文件拷贝
原理同上,写法不同
import java.io.*;
public class BufferedCopyFile {
public static void main(String[] args) {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
BufferedReader bufferedReader = null;
BufferedWriter bufferWriter = null;
try {
bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
// BufferedReader是一行一行读取的
while ((line = bufferedReader.readLine()) != null) {
bufferWriter = new BufferedWriter(new FileWriter("bufferedCopyFile.txt", true));
bufferWriter.write(line);
bufferWriter.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭外层的对象的时候,内存的资源会自动的被关闭
if(bufferedReader != null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//资源关闭
if (bufferWriter != null) {
try {
bufferWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Buffered为什么高效?
所有的Writer字符流,都要先变成InputStreamReader/OutputStreamWriter,然后转化为InputStream/OutputStream变成字节数组,最终转化为二进制数据才能在计算机底层进行传输
所有的Reader字符流,过程相反
我们可以看到,字符流读写存在编解码的过程,而Buffered之所以高效,就是因为FileWriter每次调用write()方法,就会调用一次OutputStreamWriter中的write()方法,而BufferedWriter只有在缓冲区满了才会调用OutputStreamWriter中的write()方法。
打印字符输出流PrintWriter
PrintWriter和FileWriter之间的区别
区别在于 :
- PrintWriter设计之初就是为了写出,所以没有PrintReader
- PrintWriter 提供了一些额外的格式化方法,如 println ,print和 printf,而且可以将内容打印到控制台上
- 封装了字符输出流,还可以字符流和字节流的转换
- 可以打印各种数据类型
- PrintWriter 在传递流时所做的是以 缓冲(BufferedWriter) 方式打开它,FileWriter不是
- 与 PrintStream 类不同,如果启用了自动刷新,但只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作(PrintStream是任何写方法都可以自动刷新)
PrintWriter(Writer out, boolean autoFlush);// true开启,false关闭
public PrintWriter(File file) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),
false);
}
FileWriter没有缓冲
public FileWriter(File file) throws IOException {
super(new FileOutputStream(file));
}
demo
打印控制台
PrintWriter pw = null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("c.txt"));
pw = new PrintWriter("d.txt");
String line = null;
while((line = br.readLine()) != null){
pw.println(line);
pw.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(pw != null){
pw.close();
}
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
打印到文件中(其实就是向文件中写)
PrintWriter pw = null;
try {
pw = new PrintWriter("c.txt");
pw.println(1);
pw.println(1.1);
pw.println("liangge");
pw.println('c');
pw.println(false);
pw.println(1.2f);
pw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if(pw != null){
pw.close();
}
}
字节流
二进制的数据,这种数据一般用文本打开我们读不懂。比如,图片文件,mp3文件。
字节输入流的超类:InputStream:
子类:FileInputStream
字节输出流的超类:
OutputStream:
子类FileOutputStream
字节流传输的是二进制的数据
字节流的本质是字节数组byte[]
字符流能传输的,字节流也可以传输;
字节流能传输的,字符流不一定能传输
文件字节输入流FIleInputStream
public static void main(String[] args) {
//创建字符输入流的对象
InputStream in = null;
try {
in = new FileInputStream("a.txt");
int r = in.read();
System.out.println((char)r);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件字节输出流FIleOutputStream
public class IOByteDemo {
public static void main(String[] args) {
//创建字节输出流
OutputStream out = null;
try {
out = new FileOutputStream(new File("a.txt"));
//字节流不需要flush
out.write(98);// Ascii码对应的是小写字母b
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(out != null){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲字节输入流BufferedInputStream
public static void main(String[] args) throws IOException {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collectionAndIOStream-test\\helloCopy.txt");
// 以下两种方式均可
// BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()));
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
byte[] bytes = new byte[1024];
int len = -1;
while ((len = bis.read(bytes)) != -1) {
String s = new String(bytes, 0, len);
System.out.print(s);
}
}
比BufferedReader更快,因为不需要转化为InputStreamReader,但是仍然需要编码
缓冲字节输入流BufferedOutputStream
public static void main(String[] args) throws IOException {
File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collectionAndIOStream-test\\buf.txt");
// 以下两种方式均可
// BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(file.toPath()));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
//创建一个字节的数组
byte[] bs = {97, 99, 103, 111};
bos.write(bs, 1, 2);
}
比BufferedWriter更快,因为不需要转化为OutputStreamWriter,但是仍然需要解码
BufferedStream拷贝文件
public static void main(String[] args) throws IOException {
File fileRead = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collectionAndIOStream-test\\helloCopy.txt");
File fileWrite = new File("bisAndBos.txt");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileRead));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileWrite));
int count = 0;
while (count == 0) {
count = bis.available();
}
int fileSize = bis.available();
long written = 0;
int beteSize = 1024;
byte[] bytes = new byte[beteSize];
while (written < fileSize) {
if (written + beteSize > fileSize) {
beteSize = (int) (fileSize - written);
bytes = new byte[beteSize];
}
bis.read(bytes);
bos.write(bytes);
bos.flush();
written += beteSize;
}
}
对象字节输出流ObjectOutputStream(序列化流)
对象转化为二进制数据,被称为序列化;
将二进制数据转化为对象,被称为反序列化
我们假设有这样一个Person
必须实现Serializable接口!!!
public class Person implements Serializable{
//唯一的序列化接口的id值
private String name;
private int age;
private Date birth;
private static final long serialVersionUID = 3587752576949978144L;
private String addr;
public Person(String name, int age, Date birth) {
this.name = name;
this.age = age;
this.birth = birth;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
", addr='" + addr + '\'' +
'}';
}
}
对象流进行序列化
public static void main(String[] args) {
Person p = new Person("宇智波止水", 16, new Date());
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream("person-data.txt"));
out.writeObject(p);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
txt内容
对象字节输入流ObjectInputStream(反序列化流)
把我们刚才序列化的字节序列反序列化为java对象
public static void main(String[] args) {
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new FileInputStream("person-data.txt"));
//读取一个对象, 叫反序列化
Object o = in.readObject();
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}