文件流

​ 文件在程序中是以流的形式来操作的,文件保存在硬盘中,通过输入流被java程序读取,程序再通过输出流输出进文件中。流是数据在数据源和程序之间经历的路径,输入流是数据从数据源到程序的路径,输出流是程序到数据源的路径。

​ java.io包中提供了各种流类和接口,以获取不同种类的数据,并通过方法输入或输出数据。

流的分类

  • 按照数据单位可以分为:字节流(8bit)、字符流
  • 按照数据流向分为:输入流、输出流
  • 按照流的角色不同分为:节点流、处理流/包装流

常用的文件操作

  1. 创建文件对象相关构造器和方法

    • new File(String pathname):根据路径创建文件对象
    • new File(File parent, String child):根据父目录文件+子路径构建
    • new File(String parent, String child):同上
  2. 获取文件相关信息

    • getName:获取文件名
    • getAbsolutePath:获取绝对路径
    • getParent:获取文件父目录
    • length:获取文件占用字节
    • exists:文件是否存在
    • isFile:是否为文件
    • isDirectory:是否为文件夹
    • delete:删除文件
    • mkdir/mkdirs创建目录/多级目录

常用IO类

  1. InputStream/OutputStream:从文件中读取字节的类,有基本的字节输入输出功能,常使用byte数组进行接收和写入

    • 使用getBytes将字符串转成Byte数组,从而对文件进行写入
    • 使用完输出输入流之后一定要关闭流,可以使用try-with-resources语句来确保资源的正确关闭。

综合使用示例:

public class Test {
public static void main(String[] args) {
String sourceFilePath = "/source.txt";
String destinationFilePath = "/destination.txt";

// try-with-resources语法:在try语句中初始化流类
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFilePath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destinationFilePath))
) {
byte[] buffer = new byte[1024];
int bytesRead;

// 读取并写入文件
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
System.out.println("File copied successfully.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
  1. Reader/Writer:从文件中读取字符的类,主要用于处理字符数据。
public class Test {
public static void main(String[] args) {
String sourceFilePath = "/source.txt";
String destinationFilePath = "/destination.txt";

// try-with-resources语法:在try语句中初始化流类
try (
FileReader fr = new FileReader(sourceFilePath);
FileWriter fw = new FileWriter(destinationFilePath)
) {
byte[] buffer = new byte[1024];
int bytesRead;

// 读取并写入文件
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
System.out.println("File copied successfully.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
  1. 节点流可以从一个特定的数据源读写数据,如上面使用的FileReader、FileWriter。节点流是底层流,直接与数据源相接。

  2. 处理流(包装流)是连接已存在的流,以增加缓冲的方式来提高输入输出的效率,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、BufferedWritter。处理流既可以消除不同节点流的实现差异,也可以提供方便的方法来完成输入输出。处理流使用了修改器设计模式,不会与数据直接相连。

  3. 对象流ObjectInputStream/ObjectOutputStream是用于对象序列化和反序列化的类。它们可以将对象转换为字节流,以便在网络上传输或保存到文件中。

    • 序列化就是在保存数据时,保存数据的值和数据类型

    • 反序列就是在恢复数据时,恢复数据的值和数据类型

    • 如果想让某个对象支持序列化,则其类必须实现Serializable(推荐,该类为标记接口,不需要实现方法)和Externalizable两个接口其中之一

    • 反序列化时,读取的顺序需要和序列化的顺序一致,并且返回的是Object类型,需要向下转型

    • 序列化的类中建议添加SerialVersionUID,为了提高版本兼容性

    • 序列化对象时,默认里面所有属性都会序列化(所以要求该类中所有的属性必须可序列化),除了statictransient修饰的成员

    • 序列化可继承,父类实现了序列化,则其所有子类都可以序列化

示例:

public class Test{
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person("Jack", 20, "blue");
String testPath = "e:\\test.dat";
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(testPath));
oos.writeObject(person);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(testPath));
Person person_ = (Person)ois.readObject();
ois.close();
}
}

class Person implements Serializable{ // 测试用的序列化类
String name;
int age;
String likedColor;

public Person(String name, int age, String likedColor){
this.name = name;
this.age = age;
this.likedColor = likedColor;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", likedColor='" + likedColor + '\'' +
'}';
}
//......
}
  1. 标准输入输出流System.in/System.out

    • System.in编译类型为InputStream,运行类型为BufferedInputStream,输入对象为键盘

    • System.out编译类型和运行类型均为PrintStream,输出对象为显示器

  2. 转换流InputStreamReader/OutputStreamWriter

    • InputStreamReader:可以将InputStream包装成Reader
    • OutputStreamWriter:可以将OutputStream包装成Writer
    • 可以在使用时指定编码格式

Properties读写文件

​ 在java集合类中介绍过,Properties类是一种专门用于读写配置文件的集合类。在项目过程中经常要从数据库中读文件,读取的文件类型经常是.properties文件。在Properties文件中配置文件的格式为:key=value,默认格式为String。

常用方法

  1. load:加载配置文件的键值对到Properties对象
  2. store:将Properties中的键值对存储到配置文件,在IDEA中,保存时如果有中文,会存储为unicode码
  3. list:将数据显示到指定设备/流对象
  4. getProperty(key):根据键获取值
  5. setProperty(key, value):设置键值对到Properties对象
public class Test{
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
pro.load(new FileReader("/mysql.properties"));
// 把键值对显示到控制台
pro.list(System.out);
// 根据键获取值
String user = pro.getProperty("user");
String pwd = pro.getProperty("pwd");
// 存储键值对到Properties文件中
pro.setProperty("user2", "inori");
pro.setProperty("pwd2", "123");
pro.store(new FileOutputStream("/mysql2.properties"), null); // 想要写入必须保存,第二个参数为想要写入的注释,如果不为null的话会写在文件的第一行
}
}