做基因表达热图的网站,h5网站价格,高端品牌衣服排行榜前十名,商贸公司寮步网站建设价钱晚上好#xff0c;愿这深深的夜色给你带来安宁#xff0c;让温馨的夜晚抚平你一天的疲惫#xff0c;美好的梦想在这个寂静的夜晚悄悄成长。 目录
前言
什么是IO流#xff1f;
IO流的作用#xff1a;
一、基础流
1. 字节流
1.1 字节输入流 FileInputStream
1.2 字节… 晚上好愿这深深的夜色给你带来安宁让温馨的夜晚抚平你一天的疲惫美好的梦想在这个寂静的夜晚悄悄成长。 目录
前言
什么是IO流
IO流的作用
一、基础流
1. 字节流
1.1 字节输入流 FileInputStream
1.2 字节输出流 FileOutputStream
1.3 新的释放资源方式
2. 字符流
2.1 字符输入流 FileReader
2.2 字符输出流 FileWriter
二、高级流
1 字节缓冲流
1.1 字节缓冲输入流 BufferedInputStream
1.2 字节缓冲输出流 BufferedOutputStream
2 字符缓冲流
2.1 字符缓冲输入流 BufferedReader
2.2 字符缓冲输出流 BufferedWriter
3 转换流
3.1 输入转换流 InputStreamReader
3.2 输出转换流 InputStreamWriter
4. 打印流
4.1 PrintStream
5. 序列化流
5.1 序列化流 ObjectOutputStream
1. 基本使用
2. transient 关键字的作用
3. 序列化版本号的作用
4. 序列化的好处和应用场景
5. 序列化的注意事项
5.2 反序列化流 ObjectInputStream
6. IO流相关工具包
总结 前言 当我们做java开发时需要在内存磁盘网络中传输数据时可能要一次性要传输的数据很大而我们的内存空间有限无法完成大文件的批量传输这时候我们就可以使用IO流IO流传输数据就是像流水一样缓缓的流动传输。 前置知识 File
在没学习File之前想想我们以前写的程序有什么问题
是不是只有在IDEA程序启动的时候会输出结果当程序结束数组、集合、变量、对象存的数据都不会存在。
它们都是内存中的数据容器。记录的数据断电或者程序终止时会丢失。 但是有些文件想长期保存下去咋整呢
这时候就需要引入文件的概念了它是非常重要的存储在计算机硬盘的存储方式。 即便断电或者程序终止了存储在硬盘文件中的数据也不会丢失。
而我们熟知的Mysql就是存储在磁盘上的一个文件游戏的存档也是这样这就是我们上次玩的内容为什么下次跳转还是原处的原因。
怎么操作文件
File只能对文件本身进行操作不能读写文件里面存储的数据。而真正读写就需要IO流。 File 类是一个核心的文件和目录操作工具。它提供了丰富的方法来管理文件系统中的文件和目录使得文件的创建、查找、修改和删除等操作变得非常便捷。 1. 创建和初始化 File 对象
1.1 使用文件绝对路径初始化
文件可以/或者\\
File file new File(D:/test/path/to/file.txt); 注意反斜杠代表转义字符两个斜杠表示目录中的一个 /
File file new File(D:\\test\\path\\to\\file.txt);
1.2 使用文件相对路径初始化
不带盘符默认系统会认为是相对路径会在当前工程下去找文件
File file new File(path/to/file.txt);
2. 判断信息相关的方法 3. 创建文件和删除文件的方法 4. 遍历文件夹的方法 5. 搜索盘符下的指定文件 searchFile 方法用于在给定的目录 dir 下搜索名为 fileName 的文件。首先检查传入的目录是否为合法目录如果不是则直接返回。使用 listFiles() 方法获取目录下的所有文件和文件夹。遍历这些文件和文件夹 如果是文件 (isFile() 返回 true)则检查文件名是否与目标文件名 fileName 匹配如果匹配则打印文件的绝对路径。如果是目录 (isDirectory() 返回 true)则递归调用 searchFile 方法继续搜索该目录下的文件。 import java.io.File;public class Main {public static void main(String[] args) {searchFile(new File(D:/),3bdb662e620d6792fdc3fb9f69fd7dc.png);}/*** 去目录下搜索某个文件* param dir 目录* param fileName 要搜索的文件*/public static void searchFile(File dir, String fileName){//1.处理非法情况 递归出口文件路径不存在或者是文件(因为判断过了子文件夹是否符合了)if (dir null || !dir.exists() || !dir.isDirectory()){return;}//2. 剩下的情况只有文件夹了File[] files dir.listFiles();//3. 判断当前目录下存在一级文件对象if (files ! null){//4. 遍历全部一级文件对象for (File file : files) {//5. 判断是否是文件还是文件夹if (file.isFile()){//文件存在if (file.getName().equals(fileName)){System.out.println(找到了 file.getAbsoluteFile());//启动exe程序
// Runtime runtime Runtime.getRuntime();
// runtime.exec(file.getAbsolutePath());}}else {//文件夹继续重复这个过程searchFile(file,fileName);}}}}
}
前置知识 编码表
计算机中底层都是硬件只支持二进制数据进行操作。但是我们汉语和英语的字符是怎么存储到计算机中的呢
此时就做出引出了编码表的概念
编码字符按照制定的字符集转换成字节。解码字节按照制定的字符集转换成字符。 编码字符串转字节数组。解码字节数组转字符串。
1. ASCll字符集 (不包含汉字)
将每个字符都与对应的数字码点进行对应。然后进行编码变成二进制。
ASCll码表只有128个码点其中对应0~128。1个字节存储。
编码将数字转成二进制并且最后一位补0。
2. GBK字符集 (国标码)
ASCll码表对于美国人来说完全够了但是中国人存在汉字ASCll码表就完全处理不了我们汉子存储的问题了。
因此就引出了GBK编码表。
GBK字符集包含了2万多个汉字等字符GBK中一个中文字符编码成两个字节的形式存储。
并且GBK兼容ASCll字符集。ASCll中的东西GBK编码不能改变。 并且ASCll里面有的东西都符合标准一个字节首位为0但GBK中的汉字第一个字符首位是1。也就是说看完第一个字节的首位1后会把后一个字节连接来进行读取。因此ASCll可以包含2^15个字符。
3. Unicode字符集 (万国码)
如果每个国家都因为美国的ASCll码表不兼容就发明了一个新的码表不同语言的国家就会出现码表这样计算机在世界数据互通的时候就会出现很多的问题因此国际组织的耗时者就推出了Unicode字符集。
Unicode字符集是国际组织制定的可以容纳世界上所有文字和符合的字符集。
3.1 UTF-32 编码方案
采用了4个字节表示一个字符有容乃大的思想。
但大家都觉得这种编码方案占用存储空间。通信效率就会降低。
3.2 UTF-8 编码方案
UTF-8就很好的解决UTF-32奢侈的思想。
UTF-8采用可变长编码方案共有四个长度区1个字节、2个字节、3个字节、4个字节。
1个字节英文字符和数字(兼容ASCll码表)
3个字节汉语字符 (兼容大部分国家字符)
2个字节常见的非拉丁文字符和日语的平假名、片假名如希腊文、西里尔文。
4个字节较为罕见的或者历史上用于稀有文字的字符。
为了防止读和写不一致的情况底层也会有一种编码规则 注意1编码的方式要和解码的方式一致否则会出现乱码。不符合我的编码方式我无法解析。比如GBK 1xxxxxx UTF-8不认识你就会输出。注意2数字和字母不会出现乱码因为所有语言都兼容了ASCll码表。
什么是IO流 IO流是用于处理设备之间的数据传输的机制Java中的IO流主要分为两大类输入流和输出流。输入流用于从源读取数据输出流用于向目标写入数据。流的概念可以看作是数据在源和目标之间的传输管道数据可以是字节或字符。 输出流以内存为基准将内存中的数据以字节或者字符的形式写到磁盘或者网络中的流。
输入流以内存为基准将磁盘或者网络中以字节或者字符的形式读到内存中的数据的流。
IO流的作用 对文件或者网络中的数据进行读写的操作。 把数据从磁盘、网络中读取到程序中来、用到的是输入流。读数据 把程序中的数据写入磁盘或网络中、用到的是输出流。写数据 游戏记录存档、文件拷贝、发qq通过IO流写到网络发给对方。 在Java编程中IO输入/输出流是非常重要的概念。它们为我们提供了处理文件、网络连接和其他输入输出数据的能力。本文将深入探讨Java中的IO流包括其类型、工作原理以及如何有效地使用它们。
前置知识操作系统在IO做了什么事情 文件系统处理 文件定位操作系统通过文件路径定位要写入的文件。这包括解析文件路径、检查文件是否存在等。权限检查操作系统会检查当前进程是否有足够的权限来对文件进行写入操作。这涉及到访问控制列表ACL或者文件的所有者/组权限。文件打开如果文件没有被打开操作系统会打开文件以准备写入。这会涉及到文件描述符的分配和管理。 缓存管理 写入缓冲区为了提高性能操作系统可能会将待写入的数据先写入到内存中的缓冲区。这些缓冲区可以是系统级的页缓存或者特定文件的缓冲区。延迟写入有时候操作系统会延迟实际的磁盘写入将数据先存放在内存中以便进行批量写入或者提高响应速度。 磁盘管理 传输数据一旦数据准备好操作系统会调用磁盘驱动程序来实际传输数据到磁盘。这包括通过总线如SATA、SCSI等发送数据到磁盘控制器。写入磁盘磁盘管理模块负责将数据写入到磁盘的相应位置。这可能涉及到磁盘寻道、数据区域的写入等物理操作。 数据安全性保证 事务处理对于一些文件系统操作系统可能会使用事务处理来确保数据的原子性。这意味着要么写入操作完全成功要么完全失败不会出现部分写入的情况。日志记录某些文件系统会在写入前记录写操作的日志以便在发生系统崩溃或中断时可以恢复文件系统到一致的状态。 更新元数据 文件元数据更新成功写入后操作系统会更新文件的元数据例如文件大小、修改时间、访问时间等。文件系统元数据更新此外文件系统可能还需要更新其自身的元数据结构如索引节点inode或文件分配表以反映文件的最新状态。 返回写入结果 写入成功如果写入成功操作系统会返回成功的状态给应用程序通常还会返回写入的字节数或其他相关信息。错误处理如果写入过程中出现错误如磁盘空间不足、权限错误等操作系统会返回相应的错误码或异常通知应用程序写入操作失败。
操作系统会负责将数据从内核空间传输到用户空间程序的地址空间或者从用户空间传输到内核空间这取决于是读取还是写入操作。
一、基础流
1. 字节流
1.1 字节输入流 FileInputStream
字节流以字节为单位处理数据本质上所有文件都是以字节组成适合处理二进制文件、图像、视频等数据。 首先创建FileInputStream文件字节输入流管道、与源文件接通。使用read()读取一个文件然后强转成一个字符。-1表示数据已经读完。 import java.io.FileInputStream;
import java.io.IOException;public class FileInputStreamExample {public static void main(String[] args) {String filePath input.txt; // 替换为你的文件路径try (FileInputStream fis new FileInputStream(filePath)) {int data;while ((data fis.read()) ! -1) {System.out.println((char)data);}} catch (IOException e) {e.printStackTrace();}}
}注意 一个字节的读取可能会出现乱码(汉字三个字节你读完一个就强转成char肯定会出现错误)我们可以一次性读取全部字节然后把全部字节转换为一个字符串就不会出现乱码了(但编码和解码要保持一致)。 一次读取多个字节(重点)
每一次读取一个字节的形式是非常的差的假如我这个文件有1024字节意味着就需要1024次系统调用每次读一次就需要调用一次系统资源。 数据传输方式字节数组可以一次性传输多个字节或多个数据块而不是逐个字节或逐个数据块进行传输。这种批量传输可以减少在传输过程中的固定开销例如系统调用的次数从而提高效率。 系统调用开销每次进行 IO 操作时操作系统需要执行系统调用。系统调用本身会引入一定的开销包括上下文切换、参数传递等。如果能够减少系统调用的次数就可以减少这些开销提高 IO 性能。 缓存利用使用字节数组可以更有效地利用操作系统的缓存机制。一次性读取或写入多个数据块可以更好地利用内存和磁盘缓存减少了频繁的访问开销。 数据块大小选择适当的数据块大小也会影响 IO 性能。过小的数据块会增加系统调用的频率而过大的数据块可能会导致资源浪费或不必要的延迟。字节数组允许程序员在不同的场景下选择最优的数据块大小以获得最佳的性能表现。
按照操作系统介绍
系统调用System Call是操作系统OS提供给应用程序使用的一种服务机制。它允许应用程序请求操作系统内核执行某些操作如文件操作、进程控制、网络通信等这些操作通常涉及到底层硬件资源的管理和控制。
具体来说当应用程序需要进行一些只有操作系统才能完成的任务时例如打开文件、读取数据、创建进程等它就会通过系统调用接口向操作系统发出请求。操作系统收到请求后会在内核态Kernel Mode执行相应的操作并将结果返回给应用程序。
系统调用的执行涉及到从用户态User Mode切换到内核态的过程这个切换需要一些开销例如保存和恢复进程的状态、权限检查、参数传递等。因此系统调用的频率和执行效率会直接影响应用程序的性能。
关于数据块大小的问题如果数据块过小应用程序可能需要频繁地进行系统调用来完成读取或写入操作增加了系统调用的开销而数据块过大时虽然每次系统调用传输的数据量更多但可能会导致内存或磁盘资源的浪费或者造成不必要的延迟因为大块数据可能需要更多时间来传输和处理。
因此为了优化性能通常需要在系统调用的开销和数据块大小之间寻找一个平衡点以获得最佳的 IO 性能。 import java.io.FileInputStream;
import java.io.IOException;public class ReadFileToStringExample {public static void main(String[] args) {String filePath input.txt; // 替换为你的文件路径int bufferSize 1024; // 设置每次读取的字节数 filePath.length()读取全部一次try (FileInputStream fis new FileInputStream(filePath)) {StringBuilder contentBuilder new StringBuilder();byte[] buffer new byte[bufferSize];int bytesRead;// 每次读取 bufferSize 大小的字节到缓冲区直到文件末尾while ((bytesRead fis.read(buffer)) ! -1) {// 将读取的字节转换为字符串指定字符集例如UTF-8String part new String(buffer, 0, bytesRead, UTF-8);contentBuilder.append(part);}// 输出文件内容String content contentBuilder.toString();System.out.println(文件内容如下:);System.out.println(content);} catch (IOException e) {e.printStackTrace();}}
}byte[] buffer is.readAllBytes(); // 读取输入流中的所有字节到缓冲区
System.out.println(new String(buffer)); // 将字节数组转换为字符串并打印输出is.readAllBytes() 方法会从输入流 is 中读取所有可用的字节并将其存储在一个字节数组 buffer 中。这个方法在 Java 9 及更高版本中可用。
读取出多少倒出多少
String part new String(buffer, 0, bytesRead, UTF-8);读取出多少倒出多少防止上一次读取内容影响到它。
因为你可能上次数组中存满元素了下次读取只会从0索引覆盖到老数组中但老数组的元素并没有被完全覆盖。
比如abcde
你的字节数组长度是3第一读取就是abc 第二次就是dec就会出现数据不一致的问题。
1.2 字节输出流 FileOutputStream import java.io.FileOutputStream;
import java.io.IOException;public class FileOutputStreamExample {public static void main(String[] args) {String filePath output.txt; // 文件路径可以是任何你想要写入的文件路径try {// 创建文件字节输出流对象如果文件存在则会被覆盖FileOutputStream fos new FileOutputStream(filePath);// 写入一个字节到文件fos.write(65); // 写入字符 A// 写入一个字节数组到文件byte[] data Hello, FileOutputStream!.getBytes();fos.write(data);// 写入字节数组的一部分到文件fos.write(data, 0, 5); // 写入 Hello// 关闭流fos.close();System.out.println(数据成功写入文件 filePath);} catch (IOException e) {System.out.println(写入文件时出现异常 e.getMessage());}}
}1.3 新的释放资源方式
使用 try-with-resources 语句
try-with-resources 是一种在 JDK 7 中引入的语法用于自动关闭实现了 AutoCloseable 或 Closeable 接口的资源这些资源在 try 代码块结束时会自动关闭不再需要显式调用 close() 方法。在 JDK 8 中try-with-resources 语句得到了改进可以处理带有资源的多个声明而不需要额外的嵌套。
try (BufferedReader reader new BufferedReader(new FileReader(file.txt));BufferedWriter writer new BufferedWriter(new FileWriter(output.txt))) {// 使用 reader 和 writer 进行读写操作
} catch (IOException e) {// 处理异常
}在这个例子中无论是否发生异常BufferedReader 和 BufferedWriter 都会在 try 语句结束时自动关闭。如果发生异常会先关闭资源然后再抛出异常。 关闭顺序 如果在 try-with-resources 语句中声明了多个资源它们的关闭顺序与它们被声明的顺序相反即先声明的后关闭。例如在上面的示例中writer 会先于 reader 关闭。扩展finally的一些细节
1.4 案例文件复制 文件路径定义 String sourceFile source.txt; 和 String destFile destination.txt; 分别指定了源文件和目标文件的路径。这里假设 source.txt 是要复制的源文件destination.txt 是复制后生成的目标文件。 流的创建和使用 FileInputStream fis new FileInputStream(sourceFile); 创建了一个 FileInputStream 对象用于读取 source.txt 文件的内容。FileOutputStream fos new FileOutputStream(destFile); 创建了一个 FileOutputStream 对象用于写入数据到 destination.txt 文件中。 复制过程 使用 byte[] buffer 作为缓冲区大小为 1024 字节。fis.read(buffer) 从输入流中读取数据到缓冲区并返回实际读取的字节数。当返回值为 -1 时表示已经读取到文件末尾。fos.write(buffer, 0, length) 将缓冲区中的数据写入到输出流中其中 length 是实际读取的字节数。 关闭流 在复制完成后使用 fis.close() 和 fos.close() 分别关闭输入流和输出流释放资源。 异常处理 使用 try-catch 块捕获可能出现的 IOException 异常在异常发生时输出异常信息。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class FileCopyExample {public static void main(String[] args) {String sourceFile source.png; // 源文件路径String destFile destination.png; // 目标文件路径try {// 创建文件输入流对象FileInputStream fis new FileInputStream(sourceFile);// 创建文件输出流对象如果文件不存在则会被创建FileOutputStream fos new FileOutputStream(destFile);// 设置缓冲区byte[] buffer new byte[1024];int length;// 从输入流中读取数据到缓冲区然后写入输出流while ((length fis.read(buffer)) 0) {fos.write(buffer, 0, length);}// 关闭流fis.close();fos.close();System.out.println(文件复制成功。);} catch (IOException e) {System.out.println(文件复制时出现异常 e.getMessage());}}
}2. 字符流
流中都是一个个的字符只适合操作纯文本文件。
英文的话就读取一个字节中文就读取三个字节。
2.1 字符输入流 FileReader
2.1.1 一次读取一个字符
import java.io.FileReader;
import java.io.Reader;
import java.io.IOException;public class CharacterInputStreamExample {public static void main(String[] args) {try {// 创建一个字符输入流从文件中读取数据Reader reader new FileReader(input.txt);// 一次读取一个字符并输出int character;while ((character reader.read()) ! -1) {System.out.print((char) character);}// 关闭流reader.close();} catch (IOException e) {e.printStackTrace();}}
}2.1.2 一次读取多个字符
import java.io.FileReader;
import java.io.Reader;
import java.io.IOException;public class CharacterInputStreamExample {public static void main(String[] args) {try {Reader reader new FileReader(input.txt);char[] buffer new char[1024]; // 缓冲区大小可以根据实际需求调整int numCharsRead;while ((numCharsRead reader.read(buffer, 0, buffer.length)) ! -1) {// 将读取到的字符数组转换为字符串进行处理或输出String data new String(buffer, 0, numCharsRead);System.out.print(data);}reader.close();} catch (IOException e) {e.printStackTrace();}}
}2.2 字符输出流 FileWriter
字符输出流写出数据后必须刷新流或者关闭流写出去的数据才能生效。
因为字符输出流在底层会自动创建一个缓冲区写在缓冲区(内存)中是非常快的然后系统会在释放资源(包含刷新操作)或者刷新流的时候才会写出去。
如果缓冲区装满了还没释放资源和刷新流系统会自动帮我们将缓冲区的数据写一次。 import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;public class CharacterOutputStreamExample {public static void main(String[] args) {String fileName output.txt;String text Hello, world!;try (Writer writer new FileWriter(fileName)) {// 一次写多个字符writer.write(text);System.out.println(写入字符成功到文件 fileName);} catch (IOException e) {System.err.println(写入文件时发生错误: e.getMessage());}}
}二、高级流 对基本流进行包装以提高原始流读写数据的性能。
1 字节缓冲流
系统会来一个数组里面都是字节一次全部读出来然后进行写。
字节数组读取多少、倒出多少。 缓冲区可以减少对数据存取的频率通过一次性读取或写入一定量的数据减少了与外部设备的交互次数提高了数据读写的效率和整体系统性能。 底层还是低级流只不过会自动帮我们创建一个字节数组 1.1 字节缓冲输入流 BufferedInputStream
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class BufferedInputStreamExample {public static void main(String[] args) {String fileName input.txt;try (InputStream fileInputStream new FileInputStream(fileName);BufferedInputStream bufferedInputStream new BufferedInputStream(fileInputStream)) {// 读取数据int data;while ((data bufferedInputStream.read()) ! -1) {System.out.print((char) data);}} catch (IOException e) {System.err.println(读取文件时发生错误: e.getMessage());}}
}解释和注意事项 BufferedInputStream 的使用在 BufferedInputStream 的构造方法中传入一个 FileInputStream 对象它将从文件中读取数据并缓存到内存中以提高读取效率。 数据读取通过 bufferedInputStream.read() 方法每次读取一个字节数据直到读取到文件末尾返回 -1为止。在示例中我们将读取的每个字节数据转换为字符并打印出来。 异常处理与之前一样使用 try-with-resources 结构确保流在使用完毕后正确关闭并捕获可能的 IO 异常。
1.2 字节缓冲输出流 BufferedOutputStream
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;public class BufferedOutputStreamExample {public static void main(String[] args) {String fileName output.txt;String data Hello, BufferedOutputStream!;try (OutputStream fileOutputStream new FileOutputStream(fileName);BufferedOutputStream bufferedOutputStream new BufferedOutputStream(fileOutputStream)) {// 写入数据bufferedOutputStream.write(data.getBytes());System.out.println(数据写入成功到文件 fileName);} catch (IOException e) {System.err.println(写入文件时发生错误: e.getMessage());}}
}解释和注意事项 BufferedOutputStream 的使用在 BufferedOutputStream 的构造方法中传入一个 FileOutputStream 对象它将把数据写入到文件中并进行缓冲以提高写入效率。 数据写入通过 bufferedOutputStream.write(byte[]) 方法将指定的字节数组数据写入到文件中。在示例中我们将字符串 Hello, BufferedOutputStream! 转换成字节数组并写入文件。 异常处理同样地使用 try-with-resources 结构确保流在使用完毕后正确关闭并捕获可能的 IO 异常。
2 字符缓冲流
2.1 字符缓冲输入流 BufferedReader
自带一个特有的readLine()方法自动读取一整行。 如果缓冲区有数据回到缓冲区中去读没有回到文件中读并且加载到缓冲区。
2.2 字符缓冲输出流 BufferedWriter
自带一个特有的newLine()方法换行。 import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;public class BufferedStreamExample {public static void main(String[] args) {String inputFile input.txt;String outputFile output.txt;try (BufferedReader reader new BufferedReader(new FileReader(inputFile));BufferedWriter writer new BufferedWriter(new FileWriter(outputFile));) {String line;while ((line reader.readLine()) ! null) {// 每次读取一行数据并写入到输出文件writer.write(line);writer.newLine(); // 写入换行符保持原始文件的行结构}System.out.println(文件复制成功);} catch (IOException e) {System.err.println(文件操作出错: e.getMessage());}}
}定义文件路径inputFile 和 outputFile 分别是输入文件和输出文件的路径。 try-with-resources使用 try-with-resources 语句来自动管理流的关闭。在 Java 7 中引入了这一特性可以有效地简化代码并确保资源得到正确关闭。 BufferedReader 读取文件使用 FileReader 包装器来创建 BufferedReader 对象用于逐行读取 inputFile 中的内容。 BufferedWriter 写入文件使用 FileWriter 包装器来创建 BufferedWriter 对象用于将读取的内容写入 outputFile 中。writer.write(line) 写入每一行的内容writer.newLine() 写入换行符以保持原始文件的行结构。 异常处理捕获可能抛出的 IOException并在控制台输出错误消息。 成功消息如果没有异常抛出输出 文件复制成功。
3 转换流
可以解决输入文件和读取文件所使用的编码不一致的情况下会出现乱码的问题。 解决思路:先获取文件的原始字节流再将其按真实的字符集编码转成字符输入流这样字符输入流中的字符就不乱码了。 3.1 输入转换流 InputStreamReader
当提到输入输出转换流 InputStreamReader 和 OutputStreamWriter 时它们通常用于处理字符数据和字节数据之间的转换。这些流是字符流和字节流之间的桥梁可以方便地将字节流转换为字符流或者将字符流转换为字节流。
// 示例 1使用 InputStreamReader 将字节流转换为字符流
InputStream inputStream new FileInputStream(input.txt); // 字节输入流
Reader reader new InputStreamReader(inputStream, StandardCharsets.UTF_8); // 将字节流转换为字符流// 使用 BufferedReader 包装 InputStreamReader以便按行读取
BufferedReader bufferedReader new BufferedReader(reader);
String line;
while ((line bufferedReader.readLine()) ! null) {System.out.println(line); // 输出每行内容
}// 关闭流
bufferedReader.close();3.2 输出转换流 InputStreamWriter
// 示例 2使用 OutputStreamWriter 将字符流转换为字节流
OutputStream outputStream new FileOutputStream(output.txt); // 字节输出流
Writer writer new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); // 将字符流转换为字节流// 使用 BufferedWriter 包装 OutputStreamWriter以便写入数据
BufferedWriter bufferedWriter new BufferedWriter(writer);
bufferedWriter.write(Hello, world!\n); // 写入字符数据
bufferedWriter.write(你好世界\n);
bufferedWriter.flush(); // 刷新缓冲区确保数据写入到文件中// 关闭流
bufferedWriter.close();4. 打印流
4.1 PrintStream
打印流PrintStream是Java中用于将各种数据类型格式化输出到目标设备如控制台、文件等的类。它是Java标准库中的一部分位于java.io包中。 格式化输出PrintStream提供了多种用于格式化输出的方法例如print()和println()和printf()系列方法可以输出各种数据类型如基本类型、字符串、对象的值并自动进行格式化处理。 目标设备PrintStream可以输出到多种目标设备最常见的是输出到控制台System.out也可以通过构造方法指定输出到文件或其他输出流。 异常处理PrintStream处理输出过程中可能出现的I/O异常它的方法通常不会抛出IOException而是通过checkError()方法来检查是否发生了错误。 自动刷新PrintStream可以设置是否在每次调用println()方法后自动刷新缓冲区。
代码如下示例
import java.io.*;public class PrintStreamExample {public static void main(String[] args) {try {// 输出到控制台PrintStream ps System.out;ps.println(Hello, PrintStream!);// 输出到文件File file new File(output.txt);FileOutputStream fos new FileOutputStream(file);PrintStream fileStream new PrintStream(fos);fileStream.println(Output to file.);// 格式化输出double pi Math.PI;ps.printf(Pi value: %.2f\n, pi);// 关闭流fileStream.close();} catch (FileNotFoundException e) {e.printStackTrace();}}
}5. 序列化流
5.1 序列化流 ObjectOutputStream
1. 基本使用
2. transient 关键字的作用
3. 序列化版本号的作用
Java序列化机制通过版本号来确定序列化对象的兼容性具体包括以下几点 手动指定版本号 当一个类实现了 Serializable 接口但没有显式指定 serialVersionUID序列化版本号时Java序列化机制会自动生成一个版本号。这个自动生成的版本号基于类的成员字段、方法等生成的散列值。手动为类添加 serialVersionUID 可以避免在修改类结构后导致自动生成的版本号发生变化从而引起反序列化失败的问题。 自动生成版本号的问题 如果一个类没有显式指定版本号而后来修改了类的结构例如增加、删除或者修改了字段或方法Java序列化机制会重新自动生成一个不同的版本号。这样的话使用旧版本序列化的对象尝试反序列化时由于版本号不匹配会抛出 InvalidClassException 异常导致反序列化失败。 手动指定版本号的优点 手动指定 serialVersionUID 可以确保在类结构发生变化时版本号不变从而保证序列化和反序列化的兼容性。这对于在分布式系统中特别重要因为不同的系统可能会使用相同类的不同版本。
在实际开发中推荐对需要序列化的类显式地添加 serialVersionUID并保持这个版本号不变除非有意为之修改类结构。这样做可以避免由于版本号不匹配而导致的反序列化异常提高系统的稳定性和兼容性。
import java.io.Serializable;public class MyClass implements Serializable {private static final long serialVersionUID 123456789L; // 手动指定版本号// 类的成员和方法定义...
}通过这种方式可以明确控制类的序列化版本号避免不必要的问题并确保在类结构发生变化时能够正确地处理已经序列化的对象。
4. 序列化的好处和应用场景 持久化存储 序列化允许将对象以二进制形式存储在磁盘上这样可以实现对象的持久化存储即使程序结束或重启数据仍然可以被恢复和使用。 对象传输 序列化也是在网络上传输对象的常用方式。通过将对象序列化为字节流可以在网络上传输接收端再将字节流反序列化为对象进行处理。 跨平台通信 序列化使得不同平台上的程序可以通过序列化的方式交换数据例如Java程序序列化对象后可以通过网络传给运行在不同语言的系统的应用程序。
5. 序列化的注意事项 保存对象状态 序列化只保存对象的状态即成员变量的值不保存方法。反序列化时会重新构建对象但是类的构造方法不会被调用。 版本控制 最好为实现 Serializable 接口的类显式指定 serialVersionUID避免自动生成的版本号在类结构改变后发生变化而导致反序列化失败。 类的可序列化性 如果一个类实现了 Serializable 接口它的所有非瞬态非 transient成员变量都会被序列化包括其引用的其他可序列化的对象。 稳定性 序列化的类最好是稳定的即不经常修改其结构因为序列化的二进制数据在类结构变化后可能无法正确反序列化。
5.2 反序列化流 ObjectInputStream
当需要使用反序列化流 ObjectInputStream 时它的主要作用是将之前通过序列化保存在文件或网络中的对象数据重新转换回内存中的对象实例。这种操作常见于需要持久化存储对象数据或在不同系统间传输对象数据的场景中。
关键概念和注意事项包括 反序列化的目的 数据恢复通过反序列化可以将之前序列化的对象数据恢复为原始的对象实例使得对象的状态得以保留和重用。数据传输在网络传输中对象可以通过序列化为字节流再通过反序列化还原为对象从而实现跨平台和跨语言的数据交换。 使用方式 输入源通常是一个包含序列化数据的输入流如文件流 (FileInputStream) 或网络流。ObjectInputStream用于从输入源中读取字节并将其转换为对应的对象实例。 版本号控制 serialVersionUID是一个长整型常量用于控制对象序列化和反序列化的版本兼容性。显式指定 serialVersionUID 可以确保在类结构发生变化时仍能够正确反序列化避免因自动生成的版本号不匹配而导致的反序列化异常。 类的稳定性 序列化的类最好是稳定的即不经常修改其结构。因为一旦类的结构发生变化可能导致之前序列化的数据无法正确反序列化。 异常处理 在反序列化过程中可能会抛出 IOException 或 ClassNotFoundException 异常需要适当处理这些异常以确保程序的稳定性和可靠性。
总之ObjectInputStream 提供了重要的功能使得 Java 应用程序能够方便地将对象持久化到磁盘或通过网络传输同时保证数据的完整性和版本兼容性。
import java.io.*;public class DeserializeDemo {public static void main(String[] args) {String filename data.ser; // 序列化后的文件名try (FileInputStream fileIn new FileInputStream(filename);ObjectInputStream in new ObjectInputStream(fileIn)) {MyClass obj (MyClass) in.readObject(); // 反序列化对象System.out.println(Deserialized Object:);System.out.println(obj); // 假设 MyClass 类有合适的 toString 方法} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}