文件概述
数据存储方案
数据临时性存储方案:(计算机内存的中存储的数据会在计算机断电后清空)
变量、数组、对象、集合
数据 "持久性" 储存方案:(计算机硬盘中存储的数据在正常情况下不会消失)
- 文件:存储在某种长期储存设备(磁盘、光盘、磁带等)上的一段数据流
- 文件储存的特点:所存信息可以长期、多次使用,不会因为断电而消失
文件管理File类
Java提供的,用于操作文件/文件夹的API
构造方法
- File(String path) 通过传入的 文件/文件夹路径(字符串) 构建一个 File 对象
注意:在使用路径分隔符 `\` 时,小心被 Java 识别为转义字符,要使用 '\\' 转义
// 构建File对象时,两种路径的写法
File file1 = new File("d:\\Develop");
File file2 = new File("d:/Develop");
- File(String parent, String child) 通过文件/文件夹的父路径 + 子路径来构建一个 File 对象
File file = new File("d:\\", "Develop");
- File(File parent, String child) 通过文件/文件夹的父 File 对象 + 子路径来构建一个 File 对象
File file1 = new File("d:");
File file2 = new File(file, "Develop");
常用方法
- exists() : boolean 判断文件/文件夹是否存在
- isFile() : boolean 判断是否是文件
- isDirectory() : boolean 判断是否是文件夹
- getName() : String 获取文件/文件夹的名字
- length() : long 获取文件的大小(单位:字节)
- createNewFile() : boolean 创建文件
- delete() : boolean 删除文件/文件夹(只能删除空文件夹,且不进入回收站)
- list() : String[] 获取指定文件夹下的所有内容列表(文件/文件夹)
- listFiles() : File[] 获取指定文件夹下的所有内容的 File 对象列表(文件/文件夹)
- listFiles(FileFilter fileFilter) : File[] 根据 文件过滤器 来获取指定文件夹下的所有内容的 File 对象列表
FileFilter 文件过滤器接口:(返回值为 true :保留 false: 舍弃)
实现方法一:定义个匿名内部类,重写accept方法
实现方法二:创建一个接口实现类,重写accept方法
输入输出流(IO流)概述
流:流动的水、流动的电、流动的车...
数据流:可以流动的数据
- 根据流动方法分类:(流动的方向时根据程序来决定的)
- 输入流:从数据源流动到程序(读取)
- 输出流:从程序流动到目的地(写入)
- 根据处理的单位分类:
- 字节流:单位字节,byte
- 字节流是最基础的一种处理单位
- 用于文件、图片复制等处理(用记事本打开后是看不懂的乱码)
- 字符流:单位字符,char
- 字符流是基于字节流产生的
- 用于文本文档等需要进行内容操作的处理(用记事本打开后是看的懂的字)
- 字节流:单位字节,byte
输入输出流大体分类:
字节输入流、字节输出流、字符输入流、字符输出流
字节流
字节输入流(fileInputStream)
构造方法:(没有目标文件会报错)
- FileInputStream(File file)
- FileInputStream(String name) 底层就是用第一种构造
常用方法:
- read() : int 读取一个字节的内容(没有下一个字节,返回 -1)
// 循环读取示例
int b = 0;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
- read(byte[] buffer) : int 读取一个字节数组的内容,返回读取了多少个字节到字节数组中,返回 -1 表示读取结束。
- close() : void 关流/释放资源(开启流之后,一定要关流!)
字节输出流(FileOutputSream)
构造方法 :(如果目标文件不存在,会自动创建)
- FileOutputStream(String name) 根据目的地路径,构建一个输出流
- FileOutputStream(String name, boolean append) 根据目的地路径,来构建一个输出流,append 代表是否是追加数据(true:追加 false:覆盖)
- FileOutputStream(File file) 根据目的File对象,构建一个输出流
- FileOutputStream(File file, boolean append) 根据目的File对象,构建一个输出流
常用方法:
- write(int b) : void 写入一个字节数据
- write(byte[] b) : void 写入一个字节数组的数据
- write(byte[] b, int off, int len) : void 写入一个字节数组指定范围的数据 (off:起始索引 len:写入长度)
- close() : void 关流
关流
在使用流相关的 API 时,操作结束后要关流!
Java7 - :正确的关流方式
一般的,为了保证流资源能够正确关闭,将关闭流的方法调用放在 finally 中
FileInputStream fis = null;
try {
// 指定数据源
fis = new FileInputStream("d:/a.txt");
// 读取数据
byte[] buffer = new byte[6];
fis.read(buffer);
for (byte b : buffer) {
System.out.print((char) b);
}
} finally {
// 关流
if (fis != null) {
fis.close();
}
}
Java7 + :自动资源管理 (实现了AutoCloseable接口的类不再需要手动资源释放,java会自动调用类的close方法)
try-with-resource
try (FileInputStream fis = new FileInputStream("d:/a.txt")) {
// 读取数据
byte[] buffer = new byte[6];
fis.read(buffer);
for (byte b : buffer) {
System.out.print((char) b);
}
} catch (Exception e) {
e.printStackTrace();
}
字节流实现文件的复制
需求:将 d 盘的 a.txt 复制到 e 盘
// 指定数据源和输出目的地(创建输入流和输出流)
try (FileInputStream fis = new FileInputStream("d:/a.txt");
FileOutputStream fos = new FileOutputStream("e:/a.txt")) {
// 边读边写
// 读取d盘a.txt内容
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) != -1) {
// 将读取到的内容写入e盘a.txt
fos.write(buffer, 0, len);
}
System.out.println("复制成功!");
} catch (Exception e) {
e.printStackTrace();
}
字符流
字符输入流(FileReader)
构造方法:
- FileReader(String fileName)
- FileReader(File file)
常用方法:
- read() : int 读取一个字符
- read(char[] cbuf) : int 读取一个字符数组的内容,返回的是读取了多少个字符到字符数组中,返回 -1 代表读取结束
- close() : void 关流
字节输出流(FileWriter)
构造方法:
- FileWriter(String fileName)
- FileWriter(String fileName, boolean append)
- FileWriter(File file)
- FileWriter(File file, boolean append)
常用方法:
- write(int c) : void 输出一个字符
- write(char[] cbuf) : void 输出一个字符数组
- write(char[] cbuf, int off, int len) : void 输出一个字符数组的一部分
- write(String str) : void 输出一个字符串
- write(String str, int off, int len) : void 输出一个字符串的一部分
- close() : void 关流
- flush() : void 刷新缓冲区
高效字符流
高效字符输入流(BufferedReader)
构造方法:
- BufferedReader(Reader in)
常用方法:
- readLine() : String 读取一行
高效字符输出流(BufferedWriter)
构造方法:
- BufferedWriter(Writer out)
常用方法:
- newLine() : void 文本换行
高效字节流的其他方法与字符输入流共有。
读取各种类型数据(了解)
DataInputStream
DataOutputStream
构造方法:
- DataInputStream(InputStream in)
- DataOutputStream(OutputStream out)
常用方法:
- readUTF() : String
- readInt() : int
- readDouble() : double
- readChar() : char
- readBoolean() : boolean
- writeUTF(String) : void
- writeInt(int) : void
- writeDouble(double) : void
- writeChar(char) : void
- writeBoolean(boolean) : void
- close() : void
序列化与对象流
ObjectInputStream:反序列化
构造方法:
- ObjectInputStream(InputStream in)
常用方法:
- readObject() : Object 读取对象
ObjectOutputStream :序列化
构造方法:
- ObjectOutputStream(OutputStream out)
常用方法:
- writeObject(Object obj) : void 写入对象
注意: 在输出对象时,该对象所属的 类 必须实现序列化接口
public class 类名 implements Serializable {
}
序列化和反序列化
阿里巴巴开发手册要求:
【强制】 序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;
如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。
说明: 注意 serialVersionUID 不一致会抛出序列化运行时异常。
异常详情:
java.io.InvalidClassException: 包名.实体类名; local class incompatible: stream classdesc serialVersionUID = 6956675782234049705, local class serialVersionUID = -2472659678273126616
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at 包名.测试类名.main(包名.java:xx)
serialVersionUID:类在实现序列化接口后,如果没有指定serialVersionUID,系统会根据类的信息自动生成一个serialVersionUID
注意:系统自动生成的serialVersionUID会根据类的信息改变(新增、删除属性)而改变,无法进行兼容性的升级。
问题:当类的内容改变时(即serialVersionUID发生改变后),原有序列化的内容无法反序列化回来
解决方案:在序列化类中手动添加一个 serialVersionUID ,这样是否允许兼容性升级,可以由为我们自己来控制