File
java.io.File
类是专门用来操作文件/目录的类,可以对文件/目录进行查找、删除、修改等操作- File类的注意点:
- 一个File对象就代表硬盘中的一个文件/目录
- File类的构造器并不会检查文件/目录是否存在。因此无论文件/目录是否存在,都不会影响File对象的创建
- File对象可以对文件/目录进行查找、删除、修改等操作,但是不能对文件内容进行读写操作
File.separator
来获取路径分隔符,避免跨平台运行问题
/*** 文件搜索* @param file 需要搜索的文件目录* @param fileName 文件名*/
public static void searchFile(File file, String fileName) {if(file == null || !file.exists() || file.isFile()) {return;}//获取当前目录下的一级文件或文件夹File[] files = file.listFiles();if(files == null || files.length == 0){for (File f : files) {if(f.isFile()){if(f.getName().equals(fileName)){System.out.println(f.getAbsolutePath());}}else{searchFile(f, fileName);}}}
}
Hutool FileUtil 类
- 文件操作:包括文件目录的新建、删除、复制、移动、改名等
- 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等。
- 绝对路径:针对 ClassPath 中的文件转换为绝对路径文件。
- 文件名:主文件名,扩展名的获取
- 读操作:包括 getReader、readXXX 操作
- 写操作:包括 getWriter、writeXXX 操作
FileUtil.move(file, dest, true);
FileUtil.del(file);
FileUtil.rename(file, "FileUtilDemo3.java", true);
FileUtil.readLines(file, "UTF-8").forEach(System.out::println);
RandomAccessFile
- 设计Java中访问文件的类,它允许随机访问文件,即可以在文件中的任何位置进行读取和写入操作
- 访问模式:
'r'
:只读模式'rw'
:读写模式,如果文件不存在,将被创建'rws'
:同步读写模式,要求在本地设备上的文件内容或元数据的每次更新都同步写入底层存储设备,'rwd'
:同步读写模式,要求在本地设备上的文件内容的每次更新都同步写入底层存储设备
按传输方式分

字节流
- 一切文件(图片、音频、视频等)都是以二进制的形式存储的,传输的时候也是。所以字节流可以传输任意类型的文件数据
字节输入流:
java.io.InputStream
是字节输入流的超类close()
:关闭此流并释放相关资源int read()
:从输入流读取数据的下一个字节int read(byte[] b)
:从输入流中读取若干个字节,并将它们存储在缓冲区数组b中,返回值:读入缓冲区的总字节数,或者如果因为已到达流的末尾而没有更多数据,则为 -1
。int read(byte[] b, int off, int len)
:b:数组读取缓冲区、off:数组b中数据写入的起始偏移量、len:最多可读取的字节数
字节输出流:
java.io.OutputStream
是字节输出流的超类close()
:关闭此流并释放相关资源flush()
:刷新此输出流并强制缓冲区的字节被写入到目的地write(byte[] b)
:将b.length个字节从指定的字节数组写入此输出流write(byte[] b,int off,int len)
:在指定字节数组从偏移量off开始,写入len个字节到此输出流
字符流
- 在使用时需要注意字符集编码,
StandardCharsets
类
字符输入流:
java.io.Reader
字符输入流的超类close()
:关闭此流并释放相关资源int read()
:读取的字符,作为 0 到 65535( 0x00-0xffff
)范围内的整数,或者如果已到达流末尾则为-1int read(char[] cbuf)
:将字符读入数组,读取的字符数,如果到达流末尾则为-1
字符输出流:
java.io.Writer
字符输出流的超类close()
:关闭此流并释放相关资源write(int c)
:写入单个字符write(char[] cbuf)
:写入一个字符数组write(char[] cbuf, int off, int len)
:写入字符数组的一部分write(String str)
:写入一个字符串write(String str, int off, int len)
:写入字符串的一部分
按功能类型分

文件
FileInputStream文件字节输入流
FileInputStream(String name)
:name 路径名FileInputStream(File file)
:file 指定的file对象
close()
:关闭此流并释放相关资源FileChannel getChannel()
:返回与文件输入流相关联的唯一FileChannel对象read()、read(byte[] b)、read(byte[] b,int off,int len)
从输入流中读取字节数据long skip(long n)
:跳过并丢弃输入流中的n个字节的数据
@Test
public void test() throws Exception {try (FileInputStream fs = new FileInputStream(path);){int len;byte[] bytes = new byte[1024];while((len = fs.read(bytes)) != -1){System.out.print(new String(bytes,0, len));}}
}
FileOutputStream文件字节输出流
FileOutputStream(File file)
创建一个文件输出流,写入指定的file对象FileOutputStream(File file, boolean append)
创建一个文件输出流,写入指定的file对象,append为true则表示字节被写入文件的末尾而不是开头FileOutputStream(String name)
FileOutputStream(String name, boolean append)
close()
:关闭此流并释放相关资源FileChannel getChannel()
:返回与文件输出流相关联的唯一FileChannel对象write(byte[] b)、write(byte[] b,int off,int len)、write(int b)
将数据写入输出流
@Test
public void test1() throws Exception {try (FileOutputStream fileOutputStream = new FileOutputStream(path,true);) {fileOutputStream.write("字节输出流".getBytes());}
}
FileReader文件字符输入流
- 继承自
InputStreamReader
- 使用默认缓冲区大小从字符文件中读取文本。字节到字符的解码使用指定的字符集或平台的默认字符集。
- 构造函数:
FileReader(File file)
使用平台默认的字符集,根据给定的file创建的输入流FileReader(File file, Charset charset)
根据指定的file和字符集常见输入流FileReader(String fileName)
FileReader(String fileName, Charset charset)
@Test
public void test3() throws Exception {try (FileReader fileReader = new FileReader(path);) {int len;char[] chars = new char[1024];while ((len = fileReader.read(chars)) != -1) {System.out.print(new String(chars, 0, len));}}
}
FileWriter文件字符输出流
- 继承自
InputStreamWriter
- 使用默认缓冲区大小将文本写入字符文件。字符到字节的编码使用指定的字符集或平台的默认字符集。
- 构造函数:
FileWriter(File file)
使用平台默认字符集,根据给定file创建输出流FileWriter(File file, boolean append)
使用平台默认字符集,根据给定file创建输出流,append为true,则字节将被写入文件的末尾而不是开头FileWriter(File file, Charset charset)
根据指定的file和编码,创建输出流FileWriter(File file, Charset charset, boolean append)
FileWriter(String fileName)
FileWriter(String fileName, boolean append)
FileWriter(String fileName, Charset charset)
FileWriter(String fileName, Charset charset, boolean append)
@Test
public void test4() throws Exception {try( FileWriter fileWriter = new FileWriter(path,true);){fileWriter.write("字符输出流");fileWriter.write("\n");}
}
@Test
public void test5() throws Exception {try (FileReader fileReader = new FileReader(path, StandardCharsets.UTF_8);FileWriter fileWriter = new FileWriter(new_path,StandardCharsets.UTF_16);){int len;char[] chars = new char[1024];while ((len = fileReader.read(chars)) != -1) {fileWriter.write(chars, 0, len);}} catch (IOException e) {throw new RuntimeException(e);}
}
数组
- 作为文件处理的中间层
- 能够实现在内存中暂存,不需要创建临时文件
- 需要注意OOM的问题
ByteArrayInputStream字节数组输入流:
- 将内存中的字节数组包装成输入流
- 当需要InputStream但是文件已经在内存中,比如
ObjectInputStream
ByteArrayOutputStream字节数组输出流
- 将数据流的形式写入内部的字节数组中
- 配合
ObjectInputStream
从内存中获取数据
CharArrayReader字符数组输入流
CharArrayWriter字符数组输入流
管道
- Piped流提供了一种线程间通信的机制,允许一个线程向管道写入数组,另一个线程从管道读取数据
- 分为字节流和字符流
- 共同特点:
- 线程通信:转为跨线程数据交换设计
- 先进先出:保持数据的写入顺序
- 阻塞机制:读操作在无数据时阻塞,写操作在缓冲区满时阻塞
- 必须连接:输入输出流需要先连接才能使用
- 默认缓冲区:字节流:1024字节,字符流:1024字符
- 可以通过构造函数调整大小
基本数据类型
DataInputStream
和DataOutputStream
提供了对基本数据类型和字符串的格式化读写功能DataOutputStream
:
DataOutputStream(OutputStream in)
- 基本数据类型序列化:将Java基本数据类型转为字节流写入
- UTF-8字符串支持
- 平台无关格式:保证不同平台读取一致性
// 基本类型写入
void writeBoolean(boolean v)
void writeByte(int v)
void writeShort(int v)
void writeChar(int v)
void writeInt(int v)
void writeLong(long v)
void writeFloat(float v)
void writeDouble(double v)// 特殊写入
void writeUTF(String str) // 使用UTF-8修改版编码
void writeBytes(String s) // 按字节写入(截断字符)
void writeChars(String s) // 按字符写入(UTF-16BE)
DataInputStream(InputStream in)
- 反序列化基本类型:从字节流重建Java基本类型
- 精确匹配写入格式:必须与写入顺序严格对应
- EOF检测:读取时可能抛出EOFException
// 基本类型读取
boolean readBoolean()
byte readByte()
short readShort()
char readChar()
int readInt()
long readLong()
float readFloat()
double readDouble()// 特殊读取
String readUTF() // 读取UTF-8修改版编码字符串
@Test
public void test6() throws Exception {try (DataOutputStream dos = new DataOutputStream(new FileOutputStream( path))) {dos.writeInt(123456);dos.writeUTF("你好");dos.writeDouble(3.1415926);dos.writeBoolean(true);}try (DataInputStream dis = new DataInputStream(new FileInputStream(path))) {int i = dis.readInt();String s = dis.readUTF();double d = dis.readDouble();boolean b = dis.readBoolean();System.out.println(i);System.out.println(s);System.out.println(d);System.out.println(b);}
}
缓冲
- 缓冲流式对字节流和字符流的一种封装,通过在内存中开辟缓冲区来提高I/O的操作效率
- 缓冲流的工作原理是将数据写入缓冲区,当缓冲区满时在一次性写入文件或输出流,或者当缓冲区为空时一次性从文件或输入流中读取一定量的数据
字节缓冲流
BufferedInputStream(InputStream in)
:创建一个缓冲区默认大小8k的字节输入流BufferedInputStream(InputStream in,int size)
:创建一个指定缓冲区大小的字节输入流BufferedOutputStream(OutputStream out)
: 创建一个缓冲区默认大小8k的字节输出流BufferedOutputStream(OutputStream out,int size)
: 创建一个指定缓冲区大小的字节输出流
- 传统的I/O是阻塞模式:读/写->等待->读/写->等待
- 减少物理I/O操作次数:将多次小I/O操作合并为一次大I/O操作
- 减少上下文切换
public synchronized int read(byte b[], int off, int len)
- 调用
read1()
尝试读取数据 - 如果返回值≤0(表示EOF或者错误):返回负数或者返回读取到的字节数
- 累加已读取的字节数
- 如果字节数≥期望值,则返回已读取的字节数
- 检查地城输入流是否还有可用数据,如果没有,则返回当前已读取的字节数
private int read1(byte[] b, int off, int len)
- 检查缓冲区是否还有可用数据,avail <= 0,说明缓冲区没有可用数据,需要从底层流填充缓冲区
- 如果请求读取长度≥缓冲区且不需要知识reset,则直接返回请求读取长度的数据块
- 反之填充缓冲区,
public synchronized void write(byte b[], int off, int len)
- 先刷新缓冲区已有数据
- 直接将数组数据写入底层输出流,绕过缓冲区,避免大数组写入时不必要的缓冲区复制操作
- 如果缓冲区空间不足,刷新当前缓冲区,为新数据腾出空间
- 将数据写入缓冲区
字符缓冲流
- BufferedReader类继承自Reader类,提供了一些便捷的方法,例如
redLine()
方法可以一次读取一行数据,而不是一个字符一个字符的读取 - BufferedWriter类继承自Writer类,提供了一些便捷的方法,例如
newLine()
方法可以写入一个特定的行分隔符 - 构造方法:
BufferedReader(Reader in)
创建一个默认缓冲区8k大小的字符输入流BufferedReader(Reader in, int sz)
创建一个指定缓冲区大小的字符输入流BufferedWriter(Writer out)
创建一个默认缓冲区8k大小的字符输出流BufferedWriter(Writer out, int sz)
创建一个指定缓冲区大小的字符输出流
@Test
public void test8() throws Exception {ArrayList<String> strings = new ArrayList<>();strings.add("锄禾日当午");strings.add("汗滴禾当午");strings.add("谁知盘中餐");strings.add("粒粒皆辛苦");try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path));){for (String string : strings) {bufferedWriter.write(string);bufferedWriter.newLine();}}
}@Test
public void test7() throws Exception {try (BufferedReader bufferedReader = new BufferedReader(new FileReader(path));){String line;while ((line = bufferedReader.readLine()) != null){System.out.println(line);}}
}
转换
- 转换流可以将一个字节流转换为字符流,这种转换通常用于处理文件数据,如读取文本文件或数据从网络传输到应用程序
- 用途:Java10之前用于解决编码问题
序列化
public static <T> T deepCopy(T obj) throws IOException, ClassNotFoundException {try (ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(obj);try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis)) {return (T) ois.readObject();}}
}
- Serializable自动序列化,static和transient修改的字段不能被序列化
- Externalizable自定义序列化,Externalizable 接口的 writeExternal 和 readExternal 方法由类实现
public class ExternalizableTest implements Externalizable {private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(content);}@Overridepublic void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {content = (String) in.readObject();}public static void main(String[] args) throws Exception {ExternalizableTest et = new ExternalizableTest();ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File("test")));out.writeObject(et);ObjectInput in = new ObjectInputStream(new FileInputStream(new File("test")));et = (ExternalizableTest) in.readObject();System.out.println(et.content);out.close();in.close();}
}