FileInputStream 和 FileOutputStream
阻塞式IO模型
Java IO是Java自带的 API,用于读取和写入数据(输入和输出)。大多数应用程序需要处理一些输入并根据该输入产生一些输出。
Java IO (java.io) 包的作用域
该java.io包实际上并未解决所有类型的输入和输出。例如,Java IO 包中不包括 GUI 或网页的输入和输出。
Java IO 包主要关注文件、网络流、内部内存缓冲区等的输入和输出。
Java IO 包不包含用于打开网络通信所必需的网络套接字的类。为此,您需要使用Java Networking API。但是,一旦您打开了一个套接字(网络连接),您就可以通过 Java IOInputStream和OutputStream类在其中读取和写入数据。
Java NIO - 替代 IO API
Java 还包含另一个称为Java NIO 的IO API 。它包含的类与 Java IO 和 Java Networking API 的功能大致相同,但 Java NIO 可以在非阻塞模式下工作。在某些情况下,非阻塞 IO 可以提供比阻塞 IO 更大的性能提升。
IO Streams 流
IO Streams 是Java IO 中的核心概念。流是概念上无限的数据流。您可以从流中读取或写入流。流连接到数据源或数据目标。Java IO 中的流可以基于字节(读取和写入字节)或基于字符(读取和写入字符)。
InputStream、OutputStream、Reader 和 Writer
需要从某个源读取数据的程序需要一个InputStream或 一个Reader。需要将数据写入某个目的地的程序需要一个 OutputStream或Writer。下图也说明了这一点:
一个InputStream或Reader链接到源数据。一个OutputStream 或Writer链接到数据的目的地。
Java IO 的用途和特性
Java的IO包含很多子类InputStream,OutputStream,Reader 和Writer类。原因是,所有这些子类都针对各种不同的目的。这就是为什么有这么多不同的类。所涉及的目的总结如下:
- 文件访问 File Access
- 网络接入 Network Access
- 内部存储器缓冲区访问 Internal Memory Buffer Access
- 线程间通信(管道) Inter-Thread Communication (Pipes)
- 缓冲 Buffering
- 过滤 Filtering
- 解析 Parsing
- 阅读和写作文本(读者/作家) Reading and Writing Text (Readers / Writers)
- 读取和写入原始数据(long、int 等) Reading and Writing Primitive Data (long, int etc.)
- 读写对象 Reading and Writing Objects
在通读 Java IO 类时了解这些目的是很好的。它们使理解类的目标更容易一些。
Java IO 类概览表
基于字节输入 | 基于字节输出 | 基于字符输入 | 基于字符输出 | |
---|---|---|---|---|
基本的 | InputStream | OutputStream | ReaderInputStreamReader | WriterOutputStreamWriter |
数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
文件 | FileInputStreamRandomAccessFile | FileOutputStreamRandomAccessFile | FileReader | FileWriter |
管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
缓冲 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
过滤 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
解析 | PushbackInputStreamStreamTokenizer | PushbackReaderLineNumberReader | ||
字符串 | StringReader | StringWriter | ||
Data | DataInputStream | DataOutputStream | ||
Object | ObjectInputStream | ObjectOutputStream |
Java IO: Files
Java IO 文件类
Java IO API 包含以下与在 Java 中处理文件相关的类:
- 文件 File
- 随机存取文件 RandomAccessFile
- 文件输入流 FileInputStream
- 文件阅读器 FileReader
- 文件输出流 FileOutputStream
- 文件写入器 FileWriter
通过Java IO 读取文件
如果您需要从一端读取文件到另一端,您可以使用 FileInputStream 或FileReader具体取决于您是要将文件作为二进制数据还是文本数据读取。这两个类使您可以从文件的开头到结尾一次一个字节或一个字符地读取文件,或者将字节读入byteor数组char,再次从文件的开头到结尾。您不必读取整个文件,但您只能按字节和字符存储在文件中的顺序读取它们。
如果您需要跳过文件并从这里和那里只读取其中的一部分,您可以使用 RandomAccessFile.
通过 Java IO 写入文件
如果您需要将文件从一端写入另一端,则可以使用 aFileOutputStream 或FileWriter具体取决于您是需要写入二进制数据还是字符。您可以从文件的开头到结尾一次写入一个字节或字符,或者写入byte 和 的数组char。数据按照写入的顺序依次存储在文件中。
如果你需要跳过一个文件并在不同的地方写入它,例如附加到文件的末尾,你可以使用RandomAccessFile.
Java 文件输入输出流分析及使用
Java FileInputStream 示例
在Java 的FileInputStream类java.io.FileInputStream,能够读取文件的内容作为字节流。JavaFileInputStream类是Java InputStream的子类。这意味着您将 JavaFileInputStream用作InputStream (FileInputStream行为类似于InputStream)。
这是一个简单的FileInputStream例子:
public class test {
public static void main(String[] args) {
InputStream input=null;
try {
input = new FileInputStream("C:\\IO_Test\\FileInputStream.txt");
byte[] data=new byte[1024];
int i=0;
while((i=input.read(data))!=-1){
System.out.println(new String(data,0,i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
FileInputStream 构造函数
该FileInputStream你可以用它来创建三个不同的构造函数FileInputStream 实例。我将在这里介绍前两个。
第一个构造函数将 String作为参数。这String应该包含文件系统中要读取的文件所在的路径。这是一个代码示例:
String path = "C:\\user\\data\\thefile.txt";
FileInputStream fileInputStream = new FileInputStream(path);
请注意path String. 它需要双反斜杠 ( ) 在 中创建单个反斜杠String,因为反斜杠是 Java 字符串中的转义字符。要获得单个反斜杠,您需要使用转义序列\。
在 unix 上,文件路径可能如下所示:
String path = "/home/jakobjenkov/data/thefile.txt";
第二个FileInputStream构造函数接受一个File对象作为参数。该File 对象必须指向您要读取的文件。下面是一个例子:
String path = "C:\\user\\data\\thefile.txt";
File file =new File(path);
FileInputStream fileInputStream = new FileInputStream(file);
读取性能
一次读取一个字节数组比一次从 Java 中读取一个字节要快FileInputStream。通过读取字节数组而不是一次读取一个字节,差异很容易使性能提高 10 倍或更多。
获得的确切加速取决于您读取的字节数组的大小,以及运行代码的计算机的操作系统、硬件等。在决定之前,您应该研究目标系统的硬盘缓冲区大小等。然而,8KB 及以上的缓冲区大小将提供良好的加速。但是,一旦您的字节数组超过了底层操作系统和硬件的容量,您将不会从更大的字节数组中获得更大的加速。
您可能需要尝试不同的字节数组大小并测量读取性能,以找到最佳的字节数组大小。
通过 BufferedInputStream 缓冲读取
您可以FileInputStream 使用Java BufferedInputStream添加对字节数组的透明、自动读取和缓冲。所述BufferedInputStream 读取字节的块成从底层的字节数组FileInputStream。然后,您可以从 中一个一个地读取字节,BufferedInputStream并且仍然可以获得大量来自读取字节数组而不是一次一个字节的加速。这是将 Java 包装FileInputStream在 a 中的示例BufferedInputStream:
InputStream input = new BufferedInputStream(
new FileInputStream("c:\\data\\input-file.txt"),
1024 * 1024 /* 缓冲区大小 */
);
请注意, BufferedInputStream是一个InputStream子类,可以在任何可以以InputStream的方式使用。
关闭FileInputStream
当您完成从 Java 读取数据时,FileInputStream您必须关闭它。您可以FileInputStream通过调用从close()继承的方法来关闭 a InputStream。这是打开 a FileInputStream,从中读取所有数据,然后关闭它的示例:
try( FileInputStream fileInputStream = new FileInputStream("file.txt") ) {
int data = fileInputStream.read();
while(data != -1){
data = fileInputStream.read();
}
}
一旦正在执行的线程退出该try块,该块FileInputStream就会关闭。如果从try块内部抛出异常,则捕获异常, FileInputStream关闭该异常,然后重新抛出异常。因此FileInputStream,当在 try-with-resources 块中使用时,可以保证 是关闭的。
将 FileInputStream 转换为 Reader
JavaFileInputStream是基于字节的数据流。您可能知道,Java IO API 也有一组基于字符的输入流,称为“读取器”。您可以在Java转换FileInputStream 为JavaReader使用Java的InputStreamReader中。你可以阅读更多关于如何使用InputStreamReader通过单击前一句的链接,但这里是将一个Java的一个简单的例子FileInputStream来的InputStreamReader:
InputStream inputStream = new FileInputStream("c:\\data\\input.txt");
Reader inputStreamReader = new InputStreamReader(inputStream);
FileOutputStream文件输出流
FileOutputStream 示例
这是一个简单的 JavaFileOutputStream示例:
public class test {
public static void main(String[] args) {
OutputStream outputStream=null;
try {
outputStream =new FileOutputStream("C:\\\\IO_Test\\\\FileInputStream.txt",true);
String str="hello java;你好 世界";
byte[] bytes = str.getBytes();
outputStream.write(bytes);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream 构造函数
FileOutputStream 的构造函数与FileInputStream类似,不过它多了一个有用的参数,你可以通过传出true/false来决定是否在原文件的基础上进行写入。
OutputStream output = new FileOutputStream("c:\\data\\output-text.txt", true); //append
OutputStream output = new FileOutputStream("c:\\data\\output-text.txt", false); //覆盖
当您省略第二个boolean参数并因此仅使用采用文件路径的构造函数时,默认模式是覆盖给定路径上的任何现有文件。
写入性能
将字节数组写入 Java FileOutputStream 比一次写入一个字节要快。加速可能非常显着 - 高达 10 倍或更多。因此,建议尽可能使用这些write(byte[]) 方法。
您获得的确切加速取决于您运行 Java 代码的计算机的底层操作系统和硬件。加速取决于内存速度、硬盘速度和缓冲区大小等问题。
Flash 冲洗
当您将数据写入 Java 时FileOutputStream,数据可能会在内部缓存在计算机内存中,并在稍后写入磁盘。例如,每次有 X 量的数据要写入,或FileOutputStream关闭时。
如果你想确保所有写入的数据都写入磁盘而不必关闭FileOutputStream 你可以调用它的flush()方法。调用flush()将确保FileOutputStream到目前为止已写入的所有数据也完全写入磁盘。下面是调用 JavaFileOutputStream flush()方法的示例:
OutputStream outputStream = new FileOutputStream("c:\\data\\output-text.txt");
Byte bytes[]=new byte[]{1,2,3,4,5};
outputStream.write(bytes);
outputStream.flush()
1、什么是流
流是一连串流动的字符, 是一组有序的数据序列,是以先进先出方式发送信息的通道,将数据从一个地方带到另一个地方,在 java 中所有数据都是使用流读写的。同时可以通过流进行文件的读写操作。
2、流的分类
按照流向,可以分为输入流和输出流两大类,简单来说,可以理解为将磁盘中的数据读入到内存中即为输入流;相反,将内存中的数据存入磁盘中即为输出流
按处理数据的单元,可以将流分为字节流和字符流两大类
字节流是 8 位通用字节流,字符流是 16 位 Unicode 字符流
3、文件读写操作
文件分为文本文件和二进制文件,对不同类型的文件,我们需要的操作也不尽相同
FileInpitStream
# FileInputStream类常用方法
int read( )
int read(byte[] b)
int read(byte[] b,int off,int len)
void close( )
int available()
# 子类FileInputStream常用的构造方法
FileInputStream(File file)
FileInputStream(String name)
# FileInputStream读取文本文件操作步骤:
#1. 创建文件文件输入流对象
FileInputStream fis= new FileInputStream(File类或String类文件路径);
#2. 读取文本文件的数据
fis.read();
#3. 关闭文件流
fis.close();
参考代码:
public class TestFileInputStream {
public static void main(String[] args) {
String path = "D:\\aa.txt";
File file = new File(path);
// 需先判断文件是否存在,若不存在,创建
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileInputStream fileInputStream = null;
try {
// 创建输入流对象(一般参数使用File类型,也可以跟路径path)
fileInputStream = new FileInputStream(file);
// 进行文件的操作,若读到文件末尾,将返回一个-1
int read = -1;
while (true) {
// fileInputStream读取到的是字节,并非字符型
// 需要经过强转(char)才能显示字符
read = fileInputStream.read();
if (read == -1) {
break;
}
System.out.print((char) read);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源,释放前,需要先确认文件是否存在
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在以上代码中,我们会存在两个问题:一是手动关闭资源比较麻烦,二是一个字节一个字节的读取不够高效,对此,我们进行优化
参考代码:
/**
* @description 如果觉得手动关闭资源比较麻烦,可以使用jdk1.7引入的自动关闭资源的写法
*/
public class TestFileInputStream2 {
public static void main(String[] args) {
String path = "D:\\aa.txt";
// 在try中创建流后会自动释放资源:fileInputStream.close()
try(FileInputStream fileInputStream = new FileInputStream(path)) {
while (true) {
// 创建缓冲区,用字符数组读取内容
byte[] datas = new byte[3];
int read = fileInputStream.read(datas);
System.out.println(read);
// 获取数据
for (int i = 0; i < datas.length; i++) {
System.out.print((char)datas[i]+"");
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileOutputStream
# FileOutputStream类常用方法
void write(int c)
void write(byte[] buf)
void write(byte[] b,int off,int len)
void close( )
# 子类FileOutputStream常用的构造方法
FileOutputStream (File file)
FileOutputStream(String name)
FileOutputStream(String name,boolean append)
# FileOutputStream写入文本文件操作步骤:
#1. 创建文件文件输出流对象
OutputStream os = new FileOutputStream("D:\\hello.txt");
#2. 把数据写入文本文件中
String str ="content";
byte[] words = str.getBytes();
os.write(words, 0, words.length);
#3. 关闭文件流
os.close();
参考代码:
public class TestFileOutputStream {
public static void main(String[] args) {
String path = "d:\\info.txt";
OutputStream outputStream = null;
try {
// 1.先判断是否存在该文件
File file = new File(path);
boolean ret = false;
if (!file.exists()) {
// 2.创建文件
ret = file.createNewFile();
}
// 有文件
if (ret) {
// 3、创建输出流
outputStream = new FileOutputStream(file);
// 4、进行写入操作,写入字符
outputStream.write('a');
System.out.println("写入成功!");
} else {
System.out.println("文件不存在!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
上述代码存在几个问题:一个是该写入方式只会覆盖,而不是追加;二是存在编码混乱的问题,对此,我们进行优化
参考代码:
public class TestFileOutputStream2 {
public static void main(String[] args) {
String path = "d:\\info.txt";
OutputStream outputStream = null;
try {
// 1.先判断是否存在该文件
File file = new File(path);
if (!file.exists()) {
// 2.创建文件
if (!file.createNewFile()) {
System.out.println("文件创建失败!");
return;
}
}
// 3、指定编码
String word = "888";
byte[] bytes = word.getBytes(StandardCharsets.UTF_8);
// 4、创建输出流,追加方式
outputStream = new FileOutputStream(file,true);
// 5、进行写入操作
outputStream.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
总结
1. 拿到流 或是拿到流
String path = "D:\\reloadD\\www\\java\\study\\_2020\\target\\aa.zip";
FileOutputStream fileOutputStream = new FileOutputStream(path);
// 或
File file = new File(path);
FileOutputStream fileOutputStream1 = new FileOutputStream(file);
// 或
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[1024]);
URL url = new URL(httpUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(10 * 1000);
inputStream = con.getInputStream();
2. 拿到流后就是写
URL url = new URL(httpUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(10 * 1000);
inputStream = con.getInputStream();
if (Objects.nonNull(inputStream)) {
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
bos.write(buf, 0, len);
}
}
File file = new File(System.getProperty("java.io.tmpdir") + File.separator + "verify.pdf");
if (file.exists()) {
return file;
}
OutputStream os = new FileOutputStream(file);
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
3. 文件写完就可以关闭,流下载要在写之前设置下载头信息,不然自定义文件名不生效
if (ObjectUtil.isNotEmpty(inputStream)) {
inputStream.close();
}
bos.close();
// 或
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename="
+ java.net.URLEncoder.encode(sheetName, "UTF-8") + ".xlsx");
out = response.getOutputStream();
writer.flush(out, true);
writer.close();
IoUtil.close(out);
// 或
String strTime = DateUtil.format(new Date(), "yyyyMMddHHmmss");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(strTime + "对账单", "UTF-8") + ".zip");
ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());
try {
for (Map<String, Object> map : maps) {
ZipEntry zipEntry = new ZipEntry((String) map.get("fileName"));
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write((byte[]) map.get("outByte"));
}
} catch (Exception e) {
log.error("下载远程文件信息错误信息:" + e.getMessage());
} finally {
IoUtil.close(zipOutputStream);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?