本篇将介绍输入输出相关的文件类(File)和输入输出流,以及序列化与反序列化。
本篇介绍的输入输出流包括四大基类(InputStream,OutputStream,Reader,Writer)及其子类(FileInputStream,FileOutputStream,InputStreamReader,OutputStreamWriter,BufferedReader,BufferedWriter,ObjectInputStream,ObjectOutputStream)和子类的子类(FileReader,FileWriter,DataInputStream,DataOutputStream)
一、File 类
java.io.File 类是 Java 专门用来操作文件的类
File 类的常用方法:
public class TestFile {
public static void main(String[] args) {
//创建文件目录(文件夹)对象 D:\IO测试
//1.创建已有目录的对象
File fatherFile = new File("D:\\IO测试");
//2.创建一个新的目录对象及目录
File newDir1 = new File("D:\\IO测试\\test1");
newDir1.mkdir();
//3.创建一个或多个新的目录对象及目录
File newDir2 = new File("D:\\IO测试\\test1\\test2\\test3");
newDir2.mkdirs();
//打印文件目录对象(打印的是文件目录的相对路径,同下面getPath()方法)
System.out.println(fatherFile);
//创建文件对象
File f1 = new File(fatherFile, "hahaha.txt");
//打印文件对象(打印的是文件的相对路径,同下面getPath()方法)
System.out.println(f1);
try {
//通过文件对象创建文件:createNewFile()
boolean isSuccess = f1.createNewFile();
System.out.println(isSuccess);
} catch (IOException e) {
e.printStackTrace();
}
//判断文件或文件目录是否存在:exists()
f1.exists();
fatherFile.exists();
//判断是否是文件:isFile()
f1.isFile();
//判断是否是文件目录:isDirectory()
fatherFile.isDirectory();
//删除文件或文件目录:delete()
f1.delete();
fatherFile.delete();
//获取并打印文件(或文件目录)路径
System.out.println("文件名:"+f1.getName());
System.out.println("相对路径:"+f1.getPath());
System.out.println("绝对路径:"+f1.getAbsolutePath());
//获取并打印文件长度(单位:字节)
System.out.println(f1.length());
// fatherFile.length()也可以打印,但返回值未指定,无意义
}
}
补充:递归的方式打印文件夹中的文件名及目录名
(注意:递归调用自己的方法,非常耗内存,一定要有终止条件!一般不建议使用)
public class Test {
//打印文件下面文件名,如果遇到包含子文件夹,再进入子文件夹打印
public static void getAllFileNames(String filePath) {
//1.创建文件对象
File file = new File(filePath);
//2.判断该文件是目录还是文件,如果是目录,继续进入
if (file.isDirectory()) {
//3.获取该目录下面所有文件对象 File[]
File[] files = file.listFiles();
//4.遍历files数组
for (int i = 0;i < files.length;i++) {
//5.判断每一个文件是否是目录
if (files[i].isDirectory()) {
System.out.println("目录:" + files[i].getName());
getAllFileNames(files[i].getPath()); //递归操作:方法调用自己的方法 压栈操作:当条件不满足的时候,弹栈
} else {
System.out.println("\t文件:" + files[i].getName());
}
}
} else {
//不是目录,打印文件名
System.out.println("\t文件:" + file.getName());
}
}
}
二、字节输入流
基类(抽象类):InputStream —— 子类:FileInputStream
1. InputStream类常用方法
- int read()
- int read(byte[] b)
- int read(byte[] b,int off,int len)
- void close()
- int available():可以从输入流中读取的字节数目
2. 子类FileInputStream常用的构造方法
- FileInputStream(File file)
- FileInputStream(String name)
3. read() 方法示例:
public class TestInputStream {
public static void main(String[] args) {
//读取txt文件里面的内容
File file = new File("D:\\IO测试\\hahaha.txt");
//查看文件长度
System.out.println(file.length());
//创建输入流对象(读取)类似架设管道,指定流向
// InputStream fis = null;
try {
//输入不能自动创建文件和文件目录(文件夹),所以读取文件前要确保文件存在
InputStream fis = new FileInputStream(file);
// fis = new FileInputStream(file);
//读取管道里面数据流内容
int data = -1;
while((data = fis.read()) != -1) {
System.out.print((char) data + "\t");
}
//关闭流
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// finally {
// //关闭流是必须执行的操作,所以可以放finally块里,但是需要把fis放到try-catch外面,变成全局变量
// fis.close();
// }
}
}
read(byte[] b) 方法示例:
public class TestInputStream {
public static void main(String[] args) {
//读取txt文件里面的内容
File file = new File("D:\\IO测试\\hahaha.txt");
//查看文件长度
System.out.println(file.length());
//创建输入流对象(读取)类似架设管道,指定流向
InputStream fis = null;
try {
fis = new FileInputStream(file);
//读取管道里面数据流内容
int data = -1;
byte[] bytes = new byte[1024];
while((data = fis.read(bytes)) != -1) {
System.out.print("读取的字节数:" + data + "\t");
//read(byte[] bytes)方法返回的是读取的字节数量,如果没有读取到,返回-1
//读取的内容存入bytes数组里面
for(int i = 0;i < bytes.length;i++) {
if(bytes[i] != 0) {
char c = (char) bytes[i];
System.out.print(c + "\t");
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭流
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
三、字节输出流
基类(抽象类):OutputStream —— 子类:FileOutputStream
1. OutputStream类常用方法
- void write(int c)
- void write(byte[] buf)
- void write(byte[] b,int off,int len)
- void close()
- void flush():强制把缓冲区的数据写到输出流中
2. 子类FileOutputStream常用的构造方法
- FileOutputStream (File file)
- FileOutputStream(String name)
- FileOutputStream(String name,boolean append)
3. write(int c) 方法示例:
public class TestOutputStream {
public static void main(String[] args) {
int data = 97;
//构建流对象
OutputStream fos = null;
try {
//输出可以自动创建文件,但不能创建文件目录(文件夹)
// fos = new FileOutputStream("D:\\IO测试\\hahaha.txt"); //无true表示覆盖原内容
fos = new FileOutputStream("D:\\IO测试\\hahaha.txt",true); //有true表示追加,不覆盖原内容
//写入数据
fos.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭流
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
write(byte[] buf) 方法示例:
public class TestOutputStream {
public static void main(String[] args) {
byte[] bytes = {97,98,99,100,101};
/* String str = "我爱乔乔,乔乔最好!";
* bytes = str.getBytes(); //String的getBytes()可以将字符串转换成字节数组,然后输出,但是较麻烦,字符串输入输出可以通过字符流
*/ //构建流对象
OutputStream fos = null;
try {
fos = new FileOutputStream("D:\\IO测试\\hahaha.txt"); //无true表示覆盖原内容
// fos = new FileOutputStream("D:\\IO测试\\hahaha.txt",true); //有true表示追加,不覆盖原内容
//写入数据
fos.write(bytes); //将数组内容写入文件
// fos.write(bytes,1,2); //后面跟2个数字截取数组的一部分写入,第一个作为开始下标,第二个表示截取的字节数
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭流
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
四、字符输入流
基类(抽象类):Reader —— 子类:InputStreamReader —— 子类:FileReader
1. Reader类常用方法
- int read()
- int read(char[] c)
- read(char[] c,int off,int len)
- void close()
2. 子类InputStreamReader常用的构造方法
- InputStreamReader(InputStream in)
- InputStreamReader(InputStream in,String charsetName)
3. FileReader类是InputStreamReader的子类
- FileReader(File file)
- FileReader(String name)
- 该类只能按照本地平台的字符编码来读取数据,用户不能指定其他的字符编码类型 System.out.println(System.getProperty("file.encoding"));获得本地平台的字符编码类型
4. BufferedReader类是Reader类的子类( 基类:Reader —— 子类:BufferedReader )
- BufferedReader类带有缓冲区,按行读取内容的readLine()方法
- BufferedReader常用的构造方法:BufferedReader(Reader in)
- Reader子类BufferedReader特有的方法:readLine()
5. read() 方法示例(包含字符输入缓冲流 BufferedReader 及其特有的 readLine() 方法):
public class TestReader {
public static void main(String[] args) {
Reader fr = null;
// BufferedReader br = null;
//读取文件
try {
fr = new FileReader("古侠今遇.txt");
int data = -1;
while((data = fr.read()) != -1) { //挨个读取字符,一次读取一个字符,不方便,借助字符输入缓冲流的方法可以一次读一行
System.out.print((char)data);
}
// br = new BufferedReader(fr); //字符输入缓冲流,包装字符流
// String line = null;
// while((line = br.readLine()) != null) { // BufferedReader类特有的 readLine()方法,一次可以读一行
// System.out.println(line);
// }
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// br.close();
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6. 解决读取时中文乱码:使用InputStreamReader并设置编码格式
public class Test {
public static void main(String[] args) {
//获取本地平台的字符编码方式
System.out.println(System.getProperty("file.encoding"));
//编码方式不同导致乱码及其解决方式
FileReader fr = null;
// InputStreamReader isr = null;
// InputStream is = null;
try {
fr = new FileReader("古侠今遇 utf-8版.txt");
int data = -1;
while((data = fr.read()) != -1) {
System.out.print((char)data);
}
// is = new FileInputStream("古侠今遇 utf-8版.txt");
//InputStreamReader的带参构造可以设置编码方式,解决编码方式不同导致的乱码
// isr = new InputStreamReader(is,"utf-8");
// data = -1;
// while((data = isr.read()) != -1) {
// System.out.print((char)data);
// }
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fr.close();
// isr.close();
// is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
五、字符输出流
基类(抽象类):Writer —— 子类:OutputStreamWriter —— 子类:FileWriter
1. Writer类常用方法
- write(String str)
- write(String str,int off,int len)
- void close()
- void flush()
2. 子类OutputStreamWriter常用的构造方法
- OutputStreamWriter(OutputStream out)
- OutputStreamWriter(OutputStream out,String charsetName)
3. FileWriter类是OutputStreamWriter的子类
- FileWriter (File file)
- FileWriter (String name)
- 该类只能按照本地平台的字符编码来写数据,用户不能指定其他的字符编码类型
4. BufferedWriter类是Writer类的子类( 基类:Writer —— 子类:BufferedWriter )
- BufferedWriter类带有缓冲区
- BufferedWriter常用的构造方法:BufferedWriter(Writer out)
5. write(String str) 方法示例:
public class TestWriter {
public static void main(String[] args) {
Writer fw = null;
try {
fw = new FileWriter("输出流自动创建文件.txt",true); //运行后记得刷新,才会显示在project
fw.write("北大青鸟\n");
// fw.flush(); //flush()方法可以强制把缓冲区的数据写到输出流中
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
*BufferedWriter 示例(未测试):
六、二进制流(传输图片、音频、视频等文件)
1. DataInputStream类(注意:父类是 FilterInputStream,不是 FileInputStream)
- 基类(抽象类):InputStream —— 子类:FilterInputStream —— 子类:DataInputStream
- DataInputStream类与FileInputStream类结合使用读取二进制文件
2. DataOutputStream类(注意:父类是 FilterOutputStream,不是 FileOutputStream)
- 基类(抽象类):OutputStream —— 子类:FilterOutputStream —— 子类:DataOutputStream
- DataOutputStream类与FileOutputStream类结合使用写二进制文件
3. 利用二进制流复制音频文件示例:
public class TestDataStream {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
//二进制流的包装类
DataInputStream dis = null;
DataOutputStream dos = null;
try {
fis = new FileInputStream("观药 - 溯×大雾×坠落星空.mp3");
fos = new FileOutputStream("憨憨.mp3");
dis = new DataInputStream(fis);
dos = new DataOutputStream(fos);
//复制
int data = -1;
while((data = dis.read()) != -1) {
dos.write(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
七、序列化与反序列化(关键接口:Serializable)
1. 序列化:序列化是将对象的状态写入到特定的流中的过程
步骤:实现 Serializable 接口 → 创建对象输出流 → 调用 writeObject() 方法将对象写入文件 → 关闭对象输出流(ObjectOutputStream 是 OutputStream 的子类)
注意:使用集合保存对象,可以将集合中的所有对象序列化
2. 反序列化:反序列化则是从特定的流中获取数据重新构建对象的过程
步骤:实现 Serializable 接口 → 创建对象输入流 → 调用 readObject() 方法读取对象 → 关闭对象输入流(ObjectInputStream 是 InputStream 的子类)
注意:如果向文件中使用序列化机制写入多个对象,那么反序列化恢复对象时,必须按照写入的顺序读取
3. 示例——创建示例类:
public class Student implements Serializable { //序列化需要实现 Serializable 接口
private String name;
private String id;
private double score;
public Student(String name, String id, double score) {
super();
this.name = name;
this.id = id;
this.score = score;
}
public Student() {
super();
}
@Override
public String toString() {
return "Student [name=" + name + ", id=" + id + ", score=" + score + "]";
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public double getScore() {return score;}
public void setScore(double score) {this.score = score;}
}
序列化和反序列化对象示例:
public class TestObjectStream {
// private transient //隐藏属性,序列化后会被隐藏
public static void main(String[] args) {
ObjectInputStream ois = null; //对象输入流
ObjectOutputStream oos = null; //对象输出流
//字节输入输出流作为对象输入输出流的参数
InputStream is = null;
OutputStream os = null;
//1.创建对象
Student zhangsan = new Student("1001","张三",99);
try {
os = new FileOutputStream("student.txt");
oos = new ObjectOutputStream(os);
//2.将对象写入文件(序列化)
oos.writeObject(zhangsan);
oos.flush(); //flush()方法输出---------------------------
//3.反序列化读取对象信息
is = new FileInputStream("student.txt");
ois = new ObjectInputStream(is);
//读取并打印对象信息
Student stu = (Student) ois.readObject();
System.out.println(stu.toString());
System.out.println("写入对象成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
oos.close();
os.close();
ois.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
序列化和反序列化集合示例:
public class TestObjectStream {
public static void main(String[] args) {
ObjectInputStream ois = null; //对象输入流
ObjectOutputStream oos = null; //对象输出流
//字节输入输出流作为对象输入输出流的参数
InputStream is = null;
OutputStream os = null;
//1.创建对象和集合
Student zhangsan = new Student("1001","张三",99);
Student lisi = new Student("1002","李四",96);
Student wangwu = new Student("1003","王五",97);
ArrayList<Student> stus = new ArrayList<>();
stus.add(zhangsan);
stus.add(lisi);
stus.add(wangwu);
try {
os = new FileOutputStream("students.txt");
oos = new ObjectOutputStream(os);
//2.将对象写入文件(序列化)
oos.writeObject(stus);
oos.flush(); //flush()方法输出---------------------------
//3.反序列化读取对象信息
is = new FileInputStream("students.txt");
ois = new ObjectInputStream(is);
//读取并打印对象信息
ArrayList<Student> students = (ArrayList<Student>) ois.readObject();
for (Student student : students) {
System.out.println(student.toString());
}
System.out.println("写入对象成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
oos.close();
os.close();
ois.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}