第12章_IO流
File类
引入
对文件操作
package com.ymj.test11IO;
import java.io.File;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) throws IOException {
File f = new File("d:\\test.txt");
File f1 = new File("d:/test.txt");
//File.separator 属性可以帮助获取当前操作系统的路径拼接符号
File f2 = new File("d:" + File.separator + "test.txt");
//常用方法
if (f.exists()){
f.delete();
}else{
f.createNewFile();
}
System.out.println(f == f1);//比较地址
System.out.println(f.equals(f1));//比较路径
//路径:绝对路径:f.getAbsolutePath(),相对路径:f.getPath(),f.toString (相对路径)
}
}
对目录操作
包含文件的方法
跟目录相关的方法
package com.ymj.test11IO;
import java.io.File;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) throws IOException {
File f = new File("d:\\test.txt");
File f1 = new File("d:/test.txt");
//File.separator 属性可以帮助获取当前操作系统的路径拼接符号
File f2 = new File("d:" + File.separator + "test.txt");
//常用方法
if (f.exists()) {
f.delete();
} else {
f.createNewFile();
}
System.out.println(f == f1);//比较地址
System.out.println(f.equals(f1));//比较路径
//路径:绝对路径:f.getAbsolutePath(),相对路径:f.getPath(),f.toString (相对路径)
File file = new File("D:\\a\\b\\c");
// 创建目录
file.mkdir();
file.mkdirs();
//删除:如果是删除目录的话,只会删除一层,并且前提:这层目录是空的,里面没有内容,如果内容就不会被删除
file.delete();
//查看
String[] list = file.list();
for (String s : list) {
System.out.println(s);
}
System.out.println("===============");
File[] files = file.listFiles();//作用更加广泛
for (File fi :
files) {
System.out.println(fi.getName() + "," + fi.getAbsolutePath());
}
}
}
IO流
引入
- File类:封装文件/目录的各种信息,对目录/文件进行操作,但不可以获取到文件/目录中的内容。
- 引入:IO流
I/O :Input/Output的缩写,用于处理设备之间的数据传输
- 形象理解
- IO流的体系结构
读取文件内容FileReader
一个字符一个字符的将文件中的内容读取到程序中了
package com.ymj.test11IO;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Test02 {
public static void main(String[] args) throws IOException {
//文件 -> 程序
//1.有一个文件: -> 创建一个File类的对象
File f = new File("d:\\Test.txt");
//2.利用FileReader这个流,接入源文件上 -> 创建一个FileReader流对象
FileReader fr = new FileReader(f);
// 3.进行读取动作
//到了文件结尾,读取的地址为-1
int n = fr.read();
while (n != -1) {
System.out.println(n);
n = fr.read();
}
int n1;
while ((n1 = fr.read()) != -1) {
System.out.println(n1);
}
//4.关闭流
//流,数据库,网络资源,靠jvm本身无法关闭,必须手动关闭;
fr.close();
}
}
一次性读取五个字符
利用缓冲数组
package com.ymj.test11IO;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class Test03 {
public static void main(String[] args) throws IOException {
//文件 -> 程序
//1.有一个文件: -> 创建一个File类的对象
File f = new File("d:\\Test.txt");
//2.利用FileReader这个流,接入源文件上 -> 创建一个FileReader流对象
FileReader fr = new FileReader(f);
// 3.进行读取动作
//到了文件结尾,读取的地址为-1
char[] ch = new char[5];
int len = fr.read(ch);//一次性读取五个,返回该数组的有效长度
while(len!=-1){
for (int i = 0; i < len; i++) {
System.out.println(ch[i]);
}
// 方式2:
String str = new String(ch,0,len);
System.out.println(str);
len = fr.read(ch);
}
//4.关闭流
//流,数据库,网络资源,靠jvm本身无法关闭,必须手动关闭;
fr.close();
}
}
FileWriter
File f1 = new File("d:\\Demo.txt");
FileWriter fw = new FileWriter(f1, false);//默认false,覆盖原内容,true为追加内容
String str = "Hello Java !@";
for (int i = 0; i < str.length(); i++)
fw.write(str.charAt(i));
// fw.write(str);
fw.close();
利用FileReader、FileWriter文件复制
package com.ymj.test11IO;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Test04 {
public static void main(String[] args) throws IOException {
//源文件
File f1 = new File("d:\\Test.txt");
//目标文件
File f2 = new File("d:\\Test02.txt");
//Input流
FileReader fr = new FileReader(f1);
//Out流
FileWriter fw = new FileWriter(f2);
//复制
//[1] 一个一个字符复制
int n = fr.read();//n为读取的ASC码
while (n != -1) { //文件末尾ASC码=-1;
fw.write(n);
n = fr.read();
}
//[2] 利用缓冲字符数组
char[] ch = new char[5];
int len = fr.read(ch);//返回值是ch的有效长度
while(len!=-1){//读取到最后,文件末尾len = -1
fw.write(ch,0,len);
len = fr.read(ch);
}
//[3] 利用缓冲字符数组,转为字符串
char[] ch1 = new char[5];
int len1 = fr.read(ch);
while(len1!=-1){
String s = new String(ch1,0,len1);
fw.write(s);
len1 = fr.read(ch1);
}
//6.关闭流,倒着关闭
fw.close();
fr.close();
}
}
利用try-catch-final解决异常
package com.ymj.test11IO;
import java.io.*;
public class Test05 {
public static void main(String[] args) {
//源文件
File f1 = new File("d:\\Test.txt");
//目标文件
File f2 = new File("d:\\Test02.txt");
//Input流
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader(f1);
//Out流
fw = new FileWriter(f2);
//复制
//[1] 一个一个字符复制
//n为读取的ASC码
int n = fr.read();
while (n != -1) {
fw.write(n);
n = fr.read();
}
//[2] 利用缓冲字符数组
char[] ch = new char[5];
int len = fr.read(ch);
while (len != -1) {
fw.write(ch, 0, len);
len = fr.read(ch);
}
//[3] 利用缓冲字符数组,转为字符串
char[] ch1 = new char[5];
int len1 = fr.read(ch1);
while (len1 != -1) {
String s = new String(ch1, 0, len1);
fw.write(s);
len1 = fr.read(ch1);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} 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();
}
}
}
}
FileStreamReader读取文件
注意:
- 文件是UTF-8存储的,英文字符占1个字节,中文字符占3个字节;如果用字节流读取的话,1个中文字符会读取出三个ASC码;
- 文件是文本文件,那么就不要使用字节流读取了,建议使用字符流
- read()读取一个字节,但返回值是int类型,而不是byte类型
- read()方法底层做了处理,让返回的数据都是“正数”
- 就是为了避免如果字节返回的是-1的话,无法判断是读入的字节还是文件结尾。
利用字节流读取非文本文件
缓冲字节流(处理流)BufferedInputStream
- 利用缓冲区
想要完成上面的效果,单纯的靠FileInputStream,FileOutputStream是不可以完成的,这个时候就需要功能的加强,需要引入新的流(在FileInputStream,FileOutputStream外面再套一层流):BufferedInputStream,BufferedOutputStream -> 处理流
package com.ymj.test11IO;
import java.io.*;
public class Test06 {
public static void main(String[] args) throws IOException {
File f1 = new File("d:\\lol.png");
File f2 = new File("d:\\lol2.png");
// FileInputStream fi = new FileInputStream(f1);
// FileOutputStream fo = new FileOutputStream(f2);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f1));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f2));
byte[] bt = new byte[1024 * 6];
int len = bis.read(bt);
while (len != -1) {
bos.write(bt,0,len);
// bos.flush(); //底层 调用fushBuffer()方法 自动刷新缓冲区
len = bis.read(bt);
}
//关闭流时,高级流包含低级流时,当高级流关闭了,其包含的流也会自动关闭
bos.close();
bis.close();
}
}
三个方法复制非文本文件的效率
- 读一个字节,写入一个字节
复制完成时间:2147
- 利用缓冲字节数组
2
- 利用缓冲区
1
缓冲字符流(处理流)-BufferReader、BufferWriter
package com.ymj.test11IO;
import java.io.*;
public class Test07 {
public static void main(String[] args) throws IOException {
File f1 = new File("d:\\Test.txt");
File f2 = new File("d:\\Demo.txt");
BufferedReader br = new BufferedReader(new FileReader(f1));
BufferedWriter bw = new BufferedWriter(new FileWriter(f2));
//方式2;
// char[] ch = new char[1024];
// int n = br.read(ch);
// while (n!=-1){
// bw.write(ch,0,n);
// n = br.read(ch);
// }
//方式3:读取String,每次读取文本文件中一行,返回字符串
String str = br.readLine();
while (str!=null){
bw.write(str);
bw.newLine();//新启动一行
str = br.readLine();
}
bw.close();
br.close();
}
}
转换流InputStreamReader、OutputStreamWriter
- 转换流:作用,将字节流和字符流进行转换。
- 属于字符流(看结尾)
- InputStreamReader:字节输入流 -> 字符输入流
- OutputStreamWriter:字符输出流 -> 字节输出流
- 图解:
- 将输入的字节流转换为输入的字符流,然后完成文件
package com.ymj.test11IO;
import java.io.*;
public class Test08 {
public static void main(String[] args) throws IOException {
//将字节流FileInputStream转换为字符流InputStreamReader
InputStreamReader isr = new InputStreamReader(new FileInputStream(new File("d:\\Test.txt")), "utf-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File("d:\\Demo.txt")), “gbk”);
char[] ch = new char[1024];
int len = isr.read(ch);
while (len!=-1){
osw.write(ch,0,len);
len = isr.read(ch);
}
osw.close();
isr.close();
}
}
System类对IO流的支持
- System.in 标准流
package com.ymj.test11IO;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class Test09 {
public static void main(String[] args) throws IOException {
// //标准的输入流:从键盘输入
// InputStream in = System.in;
// int n = in.read();//等待键盘的录入,该方法是一个阻塞方法
// System.out.println(n);
//
// //以前案例:从键盘录入一个int类型数据
// //System.in管子,怼到键盘上,从键盘录入的恶化,就从这个官到程序中了
// //Scanner的作用,扫描器,起到扫描作用,扫键盘的从这根管出来的数据
// Scanner sc = new Scanner(System.in);
// System.out.println(sc.nextInt());
//Scanner也可以扫描其他管的内容
Scanner sc1 = new Scanner(new FileInputStream("d:\\Test.txt"));
while(sc1.hasNext()){
System.out.println(sc1.next());
}
}
}
- System.out PrintStream
PrintStream out = System.out;
out.println("hello");
练习:键盘录入内容输出到文件中
键盘录入的 System.in 是字节流,输入的有中文和英文,所以需要先转换为字符流
- 解决思路
- 代码
package com.ymj.test11IO;
import java.io.*;
public class Test10 {
public static void main(String[] args) throws IOException {
//1、先准备输入方向
//键盘录入
InputStream in = System.in;//字节流
//字节流转换为字符流
InputStreamReader isr = new InputStreamReader(in);
//在isr上套一个缓冲流
BufferedReader br = new BufferedReader(isr);
//2、再准备输出方向
//准备目标文件
File f = new File("d:\\Demo2.txt");
//写入流
FileWriter fw = new FileWriter(f);
//套一个缓冲流
BufferedWriter bw = new BufferedWriter(fw);
//3.开始动作
String s = br.readLine();
while (!s.equals("exit")){
bw.write(s);
bw.newLine();
s = br.readLine();
}
//4.关闭流
bw.close();
br.close();
}
}
数据流DataInputStream、DataOutputStream
package com.ymj.test11IO;
import java.io.*;
public class Test11 {
public static void main(String[] args) throws IOException {
//将内存中的基本数据类型和字符串的变量 写成 文件中
DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("d:\\Demo2.txt")));
//向外将变量写到文件中
dos.writeUTF("你好");
dos.write(1);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream(new File("d:\\Demo2.txt")));
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
dis.close();
}
}
写出的类型和读入的类型顺序 要 匹配!
对象流-ObjectInputStream,ObjectOutputStream
实现Serializable接口
操作自定义类的对象
自定义的Person类:
package com.ymj.test11IO.serializabletest;
public class Person {
private int age;
private String name;
private String sex;
public Person() {
}
public Person(int age, String name, String sex) {
this.age = age;
this.name = name;
this.sex = sex;
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
测试类:
package com.ymj.test11IO.serializabletest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Test13 {
public static void main(String[] args) throws IOException {
//序列化:将内存中对象 -》 文件:
//有一个对象
Person p = new Person(36,"ryan","boy");
//有对象流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt")));
//想外写
oos.writeObject(p);
//关闭流
oos.close();
//将对象
}
}
运行时异常:
出现异常的原因:
想要序列化的对象对应的类,必须实现一个接口:
public interface Serializable {
}
接口内部,什么都没有,这种接口叫标识接口。
起到标识作用,只要实现这个接口的类的对象才能序列化,否则不可以。
解决方法:将Person实现这个标识接口就可以:
package com.ymj.test11IO.serializabletest;
import java.io.Serializable;
public class Person implements Serializable {
private int age;
private String name;
private String sex;
public Person() {
}
public Person(int age, String name, String sex) {
this.age = age;
this.name = name;
this.sex = sex;
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
测试:发现序列化成功,Person具备了序列化的能力
二进制数据,用程序实现反序列化操作,将对象恢复到内存总来:(?没有成功->序列化的文件不能修改,最好重新序列化一遍,再进行反序列化操作)解决:serialVersionUID变化,原对象修改了代码,未增加serialVersionUID,解决方法,加一个serialVersionUID;
package com.ymj.test11IO.serializabletest;
import java.io.*;
public class Test14 {
public static void main(String[] args) throws IOException, ClassNotFoundException, InvalidClassException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\Demo4.txt")));
//读入内存
Person p = (Person) (ois.readObject());
System.out.println(p.toString());
// System.out.println(o/*.toString()*/);
ois.close();
}
}
反序列化成功 将二进制 -> 内存
serialVersionUID
在Person类中加入toString方法后,反序列化会报错:
解决方法:给这个类加入一个序列版本号 serialVersionUID
IDEA中配置序列版本号
序列化细节
- 被序列化的类内部的所有属性,必须是可序列化的(基本数据类型都是可序列化的)
- static transient修饰的属性不可以被序列化(transient 保护的字段,例如密码)