File
- 内存中的数据不能永久存储,硬盘中的数据可以永久存储
- 通过文件读/写硬盘中的数据
IO流是什么?
- 可以将数据从本地文件中读取出来
- 可以将数据从内存保存到本地
File类是什么?
- 告诉jvm要操作的文件/文件夹在哪?
- 对文件/文件夹进行操作,包含创建、删除等
File的方法
File的构造方法
File -- 文件和目录:
- File对象表示文件或目录
- File仅仅是一个路径名,可以存在,也可以不存在
构造方法:
- File(String pathname)
- File(String parent, String child) -- 构造:parent/child
- File(File parent, String child) -- parent/child 的另一种重载
String parent = "files";
String child = "data.txt";
File file = new File(parent);
File file1 = new File(parent, child);
File file2 = new File(file, child);
File的创建功能
- boolean createNewFile() throws IOException
- boolean mkdir() -- 一级文件夹
- boolean mkdirs() -- 多级文件夹
createNewFile当parent不存在进会抛异常:
String parent = "files";
String child = "data.txt";
File file1 = new File(parent, child);
boolean result = file1.createNewFile();
System.out.println(result);
idea中相对路径是相对于项目根目录,与当前java类所在的路径无关
String stParent = "files";
String child = "data.txt";
File parent = new File(stParent);
boolean parentResult = parent.mkdir();
System.out.println(parentResult);
File file1 = new File(parent, child);
boolean result = file1.createNewFile();
System.out.println(result);
在工程根目录下出现了files文件夹..., 再次运行程序,控制能输出两个false
注意点:
- 文件/目录存在时,创建失败,返回false
- createNewFile只创建文件,如果父目录不存会抛出异常
delete
- 不走回收站
- 可以删除文件夹或者文件
- 如果文件夹下有文件,则删除失败
// 如果不选删除文件,删除parent的代码会返回false
System.out.println(file1.delete());
System.out.println(parent.delete());
File的获取和判断方法
- boolean isDirectory()
- boolean isFile()
- boolean exists()
- getName() -- 返回文件或目录名称
- getPath()
- getParent()
- ...
System.out.println(parent.isDirectory());
System.out.println(parent.isFile());
System.out.println("=================get====================");
System.out.println(parent.getName());
System.out.println(file1.getName());
System.out.println(file1.getAbsolutePath());
System.out.println(file1.getPath());
System.out.println(file1.getParent());
还有很多方法,就不一一测试了...
File的listFile方法
返回目录中的文件和目录的File对象数组.
String stParent = "files";
File parent = new File(stParent);
if (!parent.exists()) {
if (!parent.mkdirs()) {
throw new RuntimeException("parent创建失败");
}
}
for (int i = 0; i < 100; i++) {
File file = new File(parent, i + ".txt");
if(i % 5 == 0)
{
file.mkdirs();
}
else {
file.createNewFile();
}
}
File[] files = parent.listFiles();
for (File file : files) {
if(file.isFile())
{
System.out.println(file.getParent() + " --> 是文件");
} else if (file.isDirectory()) {
System.out.println(file.getParent() + " --> 是文件夹");
}
}
// 删除
for (File file : files) {
file.delete();
}
parent.delete();
还有两个重载函数
public File[] listFiles(FilenameFilter filter);
public File[] listFiles(FileFilter filter);
FilenameFilter和FileFilter是两个接口,不管哪个接口,方法中的参数都可以唯一确定要过滤的文件。
@FunctionalInterface
public interface FileFilter {
/*
* pathname -- 表示文件本身
*/
boolean accept(File pathname);
}
@FunctionalInterface
public interface FilenameFilter {
/*
* dir - file 所在的目录
* name - file 的名称,带后缀
*/
boolean accept(File dir, String name);
}
使用示例:
// 只要文件...
File[] files = parent.listFiles(f -> f.isFile());
IO的概述
目的:读写文件数据
是谁在读写: 内存
分类:
- 按流向分
- 数据类型分
什么是纯文本文件?
用记事本打开文件,并能读懂的文件(.txt)
字节流
字节输出流--写文件(FileOutputStream)
3步曲:
- 创建输出流对象, 第二个参数 append 表示是否是追加。
- 写数据
- 释放资源 fileOutputStream.close();
FileOutputStream fileOutputStream =
new FileOutputStream("data.txt");
fileOutputStream.write(10);
fileOutputStream.close();
注意事项
- 文件不存在,会创建
- 文件存在,默认会清空之前的内容,可以通过第二个参数改变这一行为
- 一定要释放资源 -- close()
一次写多个数据
public void write(byte b[]) throws IOException;
public void write(byte b[], int off, int len) throws IOException
- off -- 从哪个位置开始
两个问题
- 怎么加换行 -- String.getBytes()把字符串转换成字节数组!
- 怎么追加 -- 创建流时用第二个参数控制!
FileOutputStream fileOutputStream =
new FileOutputStream("data.txt");
fileOutputStream
.write("hello world".getBytes(StandardCharsets.UTF_8));
fileOutputStream.write("\r\n".getBytes());
fileOutputStream
.write("你好啊".getBytes(StandardCharsets.UTF_8));
fileOutputStream.close();
字节流-try-width-resource
按alt + ctrl + t:
try (FileOutputStream fileOutputStream =
new FileOutputStream("data.txt")) {
fileOutputStream
.write("hello world"
.getBytes(StandardCharsets.UTF_8));
fileOutputStream.write("\r\n".getBytes());
fileOutputStream
.write("你好啊"
.getBytes(StandardCharsets.UTF_8));
}
字节输入流 -- 读文件(FileInputStream)
三步曲
- 创建对象 new FileInputStream("data.txt")
- 读数据 fileInputStream.read()
- 释放资源 fileInputStream.close()
read一次读取一个字节,返回 -1 到文件尾了:
try (FileInputStream fileInputStream =
new FileInputStream("data.txt")) {
int read = fileInputStream.read();
System.out.println(read);
}
读多个字节
try (FileInputStream fileInputStream =
new FileInputStream("data.txt")) {
byte[] bs = new byte[10];
System.out.println(fileInputStream.read(bs));
String str = new String(bs, StandardCharsets.UTF_8);
System.out.println(str);
}
缓冲流(BufferedOutputStream && BufferedInputStream)
- BufferedOutputStream -- 字节缓冲输出流
try (FileOutputStream fileOutputStream =
new FileOutputStream("data.txt")) {
try (BufferedOutputStream bufferedOutputStream =
new BufferedOutputStream(fileOutputStream)) {
bufferedOutputStream
.write("hello world".getBytes(StandardCharsets.UTF_8));
}
}
- BufferedInputStream -- 字节缓冲输入流
try (FileInputStream fileInputStream = new FileInputStream("data.txt")) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {
int read = bufferedInputStream.read();
while (read != -1) {
System.out.println((char)read);
read = bufferedInputStream.read();
}
}
}
它们只能通过字节流来构造。
字符流&字符缓冲流
中文码问题
字节流操作文本文件出现乱码的问题
try (BufferedOutputStream bufferedOutputStream =
new BufferedOutputStream(new FileOutputStream("data.txt"))) {
bufferedOutputStream.write("hello 我来也!".getBytes());
}
try (BufferedInputStream bufferedInputStream =
new BufferedInputStream(new FileInputStream("data.txt"))) {
int ch = bufferedInputStream.read();
while (ch != -1) {
System.out.print((char)ch);
ch = bufferedInputStream.read();
}
System.out.println();
}
- 你可能有注意到,只有一层try块了,通过查看代码知道,BufferedXXXStream在关闭时也会关闭对应的字节流。
字符流-编码表
- windows GBK编码表中用两个字节表示一个中文
- idea默认使用unicode,unicode使用UTF8编解码格式,三个字节表示一个中文
字符流-编码和解码的方法 :
编码:
- String.getBytes()使用平台默认的字符集编码,默认字符集编码可以通过
Charset.defaultCharset().name()
获得。 - String.getBytes(String charSetName)重载方法,可以指定字符集编码
// 如果标准字符编译中有定义,IDEA会给出优化建议
"hello 我来也!".getBytes(StandardCharsets.UTF_8);
"hello 我来也!".getBytes("GBK");
解码:
- String(byte[] bytes), 也是用
Charset.defaultCharset()
获得编码 - String(byte[] bytes, String charsetName),通过第二个参数指定编译
对于中国的程序只要记住:中文字符编码表为GBK
,除非特别需要一般都指定为UTF8.
默认字符集编码可以在操作系统中的设置,如果编码在虚拟机内不支持则使用ISO-8859-1(不支持中文).
字节流读取中文出现乱码的原因
- 如果解码和编码过程使用了不同的字符编译格式,得到的字符串很有可能是乱码。
- 在上之前的代码中,我们每读一个字符就向控制台输出,中文编码需要一个字节以上,如果只输出一个字节,肯定是乱码。
使用字符流操作文件
字符流-读取中文的过程
- 字符流 = 字节流 + 编码表 -- 中文的第一个字节一定是负数。
- 如果是utf8的编译,会先连续读3个字节,然后解码...
字符流-写出数据(FileWriter)
- 创建字符输出流
- 写数据
- 释放资源
try (FileWriter fileWriter = new FileWriter("text.txt")) {
fileWriter.write("你好啊");
}
- FileWriter的构造方法和FileOutputStream使用方法一样
- 没有设置字符集的接口,内部直接使用了默认字符集,JDK11以前如果要指定编码,可以用转换流,后面有介绍
- JDK11之后,FileWriter增加了可以传编码的构造
FileWrite使用默认编码--源码分析
- FileWriter类中只提供了一些构造函数,在构造函数中先构造一个FileOutputStream对象,然后调用父类单参的构造函数,没有指定字符编码
- OutputStreamWriter单参构函数中,实例化了se(StreamEncoder)成员
第二个参数给了null,使用默认字符编码
flush和close方法
- flush把数据正真写到文件,flush之后还可以继续写数据
- close关闭流,关闭之后,不能再写数据
字符流-读取数据(FileReader)
try (FileReader fileReader = new FileReader("text.txt")) {
int read = fileReader.read();
while (read != -1) {
System.out.println((char)read);
read = fileReader.read();
}
}
try (FileReader fileReader = new FileReader("text.txt")) {
char[] buff = new char[1024];
int nread = fileReader.read(buff);
String s = new String(buff, 0,nread);
System.out.println(s);
}
字符缓冲输入流-读取数据(BufferedReader)
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("text.txt"))) {
int ch = bufferedReader.read();
while (ch != -1) {
System.out.println((char)ch);
ch = bufferedReader.read();
}
}
字符缓冲输出流-输出数据(BufferedWriter)
try (BufferedWriter bufferedWriter =
new BufferedWriter(new FileWriter("text.txt"))) {
bufferedWriter.write("北京时间");
LocalDateTime now = LocalDateTime.now();
bufferedWriter.write(now.format(
DateTimeFormatter
.ofPattern("yyyy年MM月dd日 HH时mm分ss秒")));
bufferedWriter.newLine();
}
缓冲流-特有方法
- BufferedWriter.newline()
- BufferedReader.readLine()
转换流&对象操作流&Properties
转换流
- 字节流 《《---转换流---》》字符流
- 字节流 ---InputStreamReader---》》字符流
- 字节流 《《---OutputStreamReader---字符流
指定编码读写
InputStreamReader ireader = new
InputStreamReader(
new FileInputStream("text.txt"), "gbk");
try (BufferedReader bufferedReader =
new BufferedReader(ireader)) {
String s = bufferedReader.readLine();
System.out.println(s);
}
因为之前是用utf8的编码写入的,用gpk读出来的是乱码:
对象操作流
可以把对象以字节流的方式写到本地文件,直接打开文件是读不懂的,需要再次用对象操作流读到内存中。
序列化
User user = new User();
user.setName("张三丰");
user.setPassword("123456");
try (ObjectOutputStream osb = new
ObjectOutputStream(new FileOutputStream("obj.data"))) {
osb.writeObject(user);
}
写的的对象要实现Serializable(标记性接口)接口
public class User implements Serializable {
}
反序列化
try (ObjectInputStream ibs =
new ObjectInputStream(new FileInputStream("obj.data"))) {
User user = (User)ibs.readObject();
System.out.println(user.getName());
System.out.println(user.getPassword());
}
两个注意点
修改了javabean类的属性,读数据会不会出问题?怎么解决?
- 修改类的属性后,类的serialVersionUID会改变,如果我们的类中没有定义jvm会根据类中的信息自动的计算出序列号,本地件也会保存这个序列号。读取的时对比这两个值是否相等....
- 解决方案,可以参考ArrayList类的定义
public class User implements Serializable {
private static final long serialVersionUID = 1;
}
如果对象中的某个属性不想序列化该怎么做?
- 加
transient
关键字
public class User implements Serializable {
private static final long serialVersionUID = 1;
private transient String password = "";
}
Properties
- 它就是一个map对象,没有泛型.
- 有两个与IO相关联的方法
- 键对基本都是字符串
作为map集合的基本使用
和map的用法一模一样:
Properties prop = new Properties()
prop.put("name", "张三丰");
prop.put("age", "18");
prop.put("like", "太极");
prop.remove("age");
prop.put("like","打太极");
System.out.println(prop);
特有方法
- object setProperty(String key, String value)
- String getProperty(String key)
- Set<String> stringPropertyNames()
Properties prop = new Properties();
prop.setProperty("name", "张三丰"); // 就是put方法
System.out.println(prop.getProperty("name")); // 就是get方法
Set<String> strings = prop.stringPropertyNames();
System.out.println(strings); // 就是keySet()
load / store
Properties prop = new Properties();
prop.setProperty("name", "zhang san feng");
prop.setProperty("age", "18");
FileWriter writer = new FileWriter("prop.properties");
// 第二个参数是注释,如果不需要写null
prop.store(writer, "none");
writer.close();
Properties prop1 = new Properties();
FileReader fileReader = new FileReader("prop.properties");
prop1.load(fileReader);
fileReader.close();
System.out.println(prop1);
注意事项:
- properties文件中不能写中文
- 等号两边不要写空格,末尾也不要写分号
#none
#Thu Sep 15 17:18:24 CST 2022
age=18
name=zhang shan feng