目录
1.File类
Java的File
类是java.io
包中的一个抽象表示形式,用于表示文件或目录路径名。它提供了一系列方法来操作文件系统中的文件和目录,但不涉及文件内容的读写操作。要读写文件内容,通常需要使用其他java.io
包下的类,如FileInputStream
、FileOutputStream
等。
主要功能
-
创建文件或目录:
-
createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。 -
mkdir()
:创建此抽象路径名表示的目录。 -
mkdirs()
:创建此抽象路径名命名的目录,包括所有必需但不存在的父目录。
-
-
删除文件或目录:
-
delete()
:删除此抽象路径名表示的文件或目录。
-
-
检查文件或目录的状态:
-
exists()
:测试此抽象路径名表示的文件或目录是否存在。 -
isDirectory()
:测试此抽象路径名表示的文件是否为目录。 -
isFile()
:测试此抽象路径名表示的文件是否为普通文件。 -
canRead()
:测试应用程序是否可以读取此抽象路径名表示的文件。 -
canWrite()
:测试应用程序是否可以修改此抽象路径名表示的文件。 -
isHidden()
:测试此抽象路径名表示的文件是否是一个隐藏文件。
-
-
获取文件或目录的信息:
-
length()
:返回由此抽象路径名表示的文件的长度。 -
lastModified()
:获取文件最后修改的时间。 -
list()
:返回一个字符串数组,包含此抽象路径名表示的目录中的文件和子目录的名称。 -
listFiles()
:返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件和子目录。
-
-
重命名或移动文件:
-
renameTo(File dest)
:将此抽象路径名表示的文件重命名为指定的文件。
-
-
获取文件路径:
-
getAbsolutePath()
:返回此抽象路径名的绝对路径名字符串。 -
getPath()
:将此抽象路径名转换为一个路径名字符串。 -
getParent()
:返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回null。 -
getName()
:返回由此抽象路径名表示的文件或目录的名称。
-
public static void main(String[] args) { String fileParentPath = "D:\\qxl\\img"; // 真实存在的文件夹路径 String fileName = "03next.png"; // 真实存在的文件名 String fileParentPathNoExist = "D:\\qxl\\img1"; // 不存在的文件夹路径 String fileNameNoExist = "03next.png1"; // 不存在的文件名 // 当前工作目录是项目路径,注意多模块下,需要加上模块路径 System.out.println("当前工作目录(项目路径):" + System.getProperty("user.dir")); File file = new File(fileParentPath, fileName); System.out.println("真实文件是否存在:" +file.exists()); System.out.println("真实文件名称:" +file.getName()); // 相对路径没有盘符 System.out.println("真实文件相对路径:" +file.getPath()); // 绝对路径盘符开头 System.out.println("真实文件绝对路径:" +file.getAbsolutePath()); File fileNoExist = new File(fileParentPathNoExist, fileNameNoExist); System.out.println("虚假文件是否存在:" +fileNoExist.exists()); System.out.println("虚假文件名称:" +fileNoExist.getName()); // 相对路径没有盘符 System.out.println("虚假文件相对路径:" +fileNoExist.getPath()); // 绝对路径盘符开头 System.out.println("虚假文件绝对路径:" +fileNoExist.getAbsolutePath()); try { System.out.println("创建已存在的文件:" + file.createNewFile()); File fileNew = new File(fileParentPath, "03next1.png"); System.out.println("创建不存在的文件:" + fileNew.createNewFile()); System.out.println("创建不存在的文件目录+文件:" + fileNoExist.createNewFile()); System.out.println("删除存在的文件:" + fileNew.delete()); System.out.println("删除存在的空字节文件:" + fileNew.delete()); System.out.println("删除不存在的文件目录+文件:" + fileNoExist.delete()); } catch (Exception e) { System.out.println("创建文件失败:" + e.getMessage()); // 异常信息 } }
注意:
-
创建File对象的时候,需要传递一个路径,这个路径定为到哪个文件或者文件夹上,我们的File就代表哪个对象
-
createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。-
如果文件存在,不做操作,返回false
-
如果文件不存在
-
路径合法:创建新的空文件
-
路径不合法:抛出异常信息 -> 系统找不到指定的路径
-
-
-
delete()
:不抛出校验文件路径检查异常信息,在File
类的内部实现中,有一个私有的isInvalid
字段,用于标记文件路径是否无效。无效的文件路径进行删除会返回false;正确的情况删除成功返回true且删除对应文件。
2.IO流——输入与输出
File类的createNewFile()方法可以创建空文件,但文件内容的读取写入是以来IO流的。
Java的IO(输入/输出)流是用于处理数据输入和输出的一组类和接口。这些类和接口位于 java.io
包中,提供了丰富的功能来处理文件、网络连接和其他输入输出资源。IO流主要分为两大类:字节流和字符流。
字节流
字节流用于处理二进制数据,如图像、音频文件等。字节流的基本类是 InputStream
和 OutputStream
。
-
InputStream:用于从源读取字节。
-
FileInputStream
:从文件中读取字节。 -
ByteArrayInputStream
:从字节数组中读取字节。 -
BufferedInputStream
:带缓冲的输入流,提高读取效率。 -
DataInputStream
:可以从输入流中读取基本数据类型。
-
-
OutputStream:用于向目的地写入字节。
-
FileOutputStream
:向文件写入字节。 -
ByteArrayOutputStream
:向字节数组写入字节。 -
BufferedOutputStream
:带缓冲的输出流,提高写入效率。 -
DataOutputStream
:可以向输出流中写入基本数据类型。
-
字符流
字符流用于处理文本数据,支持字符编码,如UTF-8、GBK等。字符流的基本类是 Reader
和 Writer
。
-
Reader:用于从源读取字符。
-
FileReader
:从文件中读取字符。 -
StringReader
:从字符串中读取字符。 -
BufferedReader
:带缓冲的字符输入流,提高读取效率。 -
InputStreamReader
:将字节流转换为字符流。
-
-
Writer:用于向目的地写入字符。
-
FileWriter
:向文件写入字符。 -
StringWriter
:向字符串写入字符。 -
BufferedWriter
:带缓冲的字符输出流,提高写入效率。 -
OutputStreamWriter
:将字符流转换为字节流。
-
示例:字节流读取文件
public static void main(String[] args) throws IOException { File file = new File("test.txt"); try { file.createNewFile(); } catch (IOException e) { System.out.println("创建文件失败"); } System.out.println(file.getAbsolutePath()); // 将文件读取成输入字节流 FileInputStream inputStream = new FileInputStream(file); // 输入字节流将读取的文件写入指定位置 FileOutputStream outputStream = new FileOutputStream("test2.txt"); byte[] buffer = new byte[1024]; int byteRead; // 读取字节流 while ((byteRead = inputStream.read(buffer))!= -1) { for(int i = 0; i < byteRead; i++) { outputStream.write(buffer[i]); } } outputStream.close(); inputStream.close(); }
注意:
-
test.txt是一个字符文件,使用字节流读取时没有指定编码,结果会乱码。处理乱码:
-
使用字符流:字符流(如
FileReader
和FileWriter
)会自动处理字符编码问题。你可以指定字符编码,确保读取和写入时使用相同的编码。 -
使用
InputStreamReader
和OutputStreamWriter
:这些类可以将字节流转换为字符流,并允许你指定字符编码。
-
-
读取到文件末尾的判定条件:inputStream.read()方法规定,如果读到了文件的结束标记,方法直接返回-1
-
inputStream.read()读取字节是一个一个读取的,read()方法也可以接收一个带有长度的字节数组参数,进行批量读取
示例:字符流读取文件
public class CharStreamExample { public static void main(String[] args) { BufferedReader br = null; FileWriter fw = null; try { // 创建输入流和输出流 br = new BufferedReader(new FileReader("input.txt")); fw = new FileWriter("output.txt"); String line; // 读取并写入字符 while ((line = br.readLine()) != null) { fw.write(line); fw.write(System.lineSeparator()); // 写入换行符 } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) br.close(); if (fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3.缓冲流
1.为啥要学字节缓冲流
之前所写的FileOutputStream,FileInputStream,FileReader,FileWriter这都叫做基本类,其中FileInputStream和FileOutputStream的读写方法都是本地方法(方法声明上带native),本地方法是和系统以及硬盘打交道的,也就是说这两个对象的读和写都是在硬盘之间进行读写的,效率不高;缓冲流中底层带一个长度为8192的数组(缓冲区),此时读和写都是在内存中完成的(在缓冲区之间完成),内存中的读写效率非常高 使用之前需要将基本流包装成缓冲流,其实就new对象时,传递基本流
2.字节缓冲流
a.BufferedOutputStream:字节缓冲输出流 构造:BufferedOutputStream(OutputStream out) 使用:和FileOutputStream一样 b.BufferedInputStream:字节缓冲输入流 构造:BufferedInputStream(InputStream in) 使用:和FileInputStream一样
3.字符缓冲流
a.BufferedWriter:字符缓冲输出流
构造:BufferedWriter(Writer w) 使用:和FileWriter一样 特有方法:newLine() 换行
b.BufferdReader:字符缓冲输入流
构造:BufferedReader(Reader r)
使用:和FileReader一样
特有方法:String readLine(),一次读一行,读到结尾返回null
4.转换流
想要不乱码,编码和解码遵循的规则(字符编码)要一致
想要不乱码,最重要的是先知道这个字符按照什么编码去存的 UTF-8中一个汉字占3个字节 GBK中一个汉字占2个字节
-
字节流通向字符流的桥梁 -> 读数据:
构造:InputStreamReader(InputStream in,String charsetName),charsetName:指定编码,不区分大小写 作用: 可以直接指定编码,按照指定的编码去读内容 用法:基本用法和FileReader一样
-
字符流通向字节流的桥梁 -> 写数据:
构造:OutputStreamWriter(OutputStream out,String charsetName)
作用:按照指定的编码规则去存数据 用法:和FileWriter一样
5.序列化与反序列化
序列化(Serialization)和反序列化(Deserialization)是将对象的状态保存到文件或内存中,以及从文件或内存中恢复对象状态的过程。序列化通常用于在网络传输、持久化存储和对象复制等场景中。
示例代码
序列化
import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } public class SerializationExample { public static void main(String[] args) { Person person = new Person("John Doe", 30); try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) { oos.writeObject(person); System.out.println("对象已序列化到文件 person.ser"); } catch (Exception e) { e.printStackTrace(); } } }
反序列化
import java.io.FileInputStream; import java.io.ObjectInputStream; public class DeserializationExample { public static void main(String[] args) { Person person = null; try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) { person = (Person) ois.readObject(); System.out.println("对象已反序列化: " + person); } catch (Exception e) { e.printStackTrace(); } } }
关键点解释
-
实现
Serializable
接口:-
Person
类实现了Serializable
接口,表示该类的对象可以被序列化。 -
serialVersionUID
是一个版本控制字段,用于确保序列化和反序列化时类的版本一致性。
-
-
序列化过程:
-
创建一个
ObjectOutputStream
对象,将其包装在一个FileOutputStream
对象中。 -
调用
writeObject
方法将对象写入文件。
-
-
反序列化过程:
-
创建一个
ObjectInputStream
对象,将其包装在一个FileInputStream
对象中。 -
调用
readObject
方法从文件中读取对象,并将其强制转换回原来的类类型。
-
注意事项
-
transient
关键字:-
如果你不希望某个字段被序列化,可以使用 transient
private transient int secret;
-
-
自定义序列化:
-
你可以通过实现writeObject()和readObject()方法来自定义序列化和反序列化过程。例如:
private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(secret); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); secret = in.readInt(); }
-
-
序列化版本控制:
-
serialVersionUID
字段用于版本控制。如果你更改了类的结构(例如添加或删除字段),但希望旧版本的序列化数据仍然可以被正确反序列化,可以通过显式设置serialVersionUID
来实现。
-
-
安全性:
-
序列化和反序列化过程中可能存在安全风险,特别是当处理不受信任的数据时。建议对反序列化的数据进行严格的验证。
-