深入理解Java IO流:掌握输入输出流的使用与应用

您所在的位置:网站首页 I/O输入输出 深入理解Java IO流:掌握输入输出流的使用与应用

深入理解Java IO流:掌握输入输出流的使用与应用

2024-07-13 01:50| 来源: 网络整理| 查看: 265

目录 引言:前置知识一、输入流与输出流1.输入流1.1.FileInputStream1.2.ByteArrayInputStream1.3.PipedInputStream1.4.SocketInputStream1.5.AudioInputStream 2.输出流 二、字节流与字符流1. 字节流的使用:2.字符流的使用: 三、高级IO流1.缓冲流的使用:2.对象流和序列化的使用:3.压缩文件处理3.1.ZipFile3.2.ZipInputStream3.3.ZipOutputStream3.4.解压文件3.5.压缩文件 结语:

引言:

在Java开发中,IO流是一个非常重要的概念,用于处理输入和输出操作。IO流提供了一种灵活的方式,使我们能够读取和写入不同类型的数据。本篇文章将深入探讨Java IO流的使用和应用场景,并提供相应的代码示例,帮助读者更好地理解和应用IO流。JAVA IO 系统的知识体系如下: 在这里插入图片描述

前置知识

输入与输出 输入和输出我们经常使用 流 这个概念,如输入流和输出流。这是个抽象的概念,代表任何与能力产出数据的数据源对象或是有能力接受数据的接收端对象。流 屏蔽了实际 I/O 设备找那个处理数据的细节! I/O 可以分为 输入 和 输出 两部分。 输入流中又分为 字节输入流(InputStream) 和 字符输入流(Reader),任何由 InputStream 或 Reader 派生而来的类都实现了 read() 这个方法,用来读取单个字节或字节数组。 输出流中又分为 字节输出流(OutputStream) 和 字符输出流(Writer),任何由 OutputStream 或 Writer 派生而来的类都实现了 write() 这个方法,用来写入单个字节或字节数组。 因此我们可以看出 Java 中的规定:与输入有关的所有类都应该从 InputStream 继承,与输出有关的所有类都应该从 OutputStream 继承。

一、输入流与输出流

在Java中,IO流可以分为输入流和输出流两种类型。输入流用于从外部读取数据,输出流用于向外部写入数据。

1.输入流

Java中的InputStream是一个抽象类,用于表示字节输入流。它的一些常见的实现类包括:

FileInputStream:从文件中读取字节流的输入流。ByteArrayInputStream:从字节数组中读取字节流的输入流。PipedInputStream:用于与PipedOutputStream进行管道通信的输入流。FilterInputStream的子类,如BufferedInputStream、DataInputStream、ObjectInputStream等,用于提供额外的功能或过滤功能。SocketInputStream:从网络套接字中读取字节流的输入流。AudioInputStream:用于读取音频数据的输入流。其他自定义的InputStream的子类,根据具体需求实现不同的输入流。 1.1.FileInputStream

FileInputStream是Java中的一个输入流实现类,用于从文件中读取字节流。以下是FileInputStream的一些基本使用方法:

创建FileInputStream对象:

File file = new File("path/to/file.txt"); // 文件路径 FileInputStream fileInputStream = new FileInputStream(file);

读取字节流:

使用read()方法逐个字节地读取字节流,返回一个整数表示读取的字节值,如果已经读取到末尾,则返回-1。 int data; while ((data = fileInputStream.read()) != -1) { // 处理读取到的字节流 System.out.println((char) data); } 使用read(byte[] buffer)方法批量读取字节流,将读取的字节存储到指定的字节数组buffer中,返回实际读取的字节数。 import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class IOFile { public static void main(String[] args) { // 创建一个File对象,表示要读取的文件路径 File file = new File("D:\\MyCode\\demo\\src\\main\\resources\\static\\a.txt"); try { // 创建一个文件输入流对象,用于读取文件内容 InputStream fileInputStream = new FileInputStream(file); // 创建一个字节数组,用于存储文件内容 byte[] bytes = new byte[fileInputStream.available()]; // 创建另一个字节数组,用于读取文件内容 byte[] bytes2 = new byte[1024]; // 读取文件内容到字节数组bytes中 fileInputStream.read(bytes); // 将字节数组bytes转换为字符串并打印输出 System.out.println(new String(bytes)); // 继续读取文件内容,返回下一个字节的整数表示 System.out.println(fileInputStream.read()); // 关闭文件输入流 fileInputStream.close(); } catch (IOException e) { // 如果发生IO异常,抛出运行时异常 throw new RuntimeException(e); } } } 关闭输入流: 使用close()方法关闭输入流,释放相关资源。fileInputStream.close();

通过FileInputStream,我们可以从文件中读取字节流,适用于一些需要处理文件数据的场景,如文件读取、文件复制等。在使用FileInputStream时,需要注意处理可能抛出的IOException异常,并及时关闭输入流以释放资源。

1.2.ByteArrayInputStream

ByteArrayInputStream是Java中的一个输入流实现类,用于从字节数组中读取字节流。以下是ByteArrayInputStream的使用示例:

byte[] byteArray = {65, 66, 67, 68, 69}; // 字节数组 // 创建ByteArrayInputStream对象,将字节数组作为参数传入 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray); int data; while ((data = byteArrayInputStream.read()) != -1) { // 读取字节流并进行处理 System.out.println((char) data); } // 关闭输入流 byteArrayInputStream.close();

在上述示例中,我们首先创建了一个字节数组byteArray,然后使用ByteArrayInputStream将该字节数组作为参数创建了一个输入流对象byteArrayInputStream。接下来,我们使用read()方法从输入流中读取字节流,并将其转换为字符进行输出。最后,我们关闭了输入流。

通过ByteArrayInputStream,我们可以方便地从字节数组中读取字节流,适用于一些需要处理字节数组数据的场景,如网络传输、文件读取等。

1.3.PipedInputStream

PipedInputStream是Java中的一个输入流实现类,用于与PipedOutputStream进行管道通信。以下是PipedInputStream的一些基本使用方法:

创建PipedInputStream对象:

PipedInputStream pipedInputStream = new PipedInputStream();

连接输入流和输出流: 在使用PipedInputStream之前,需要将其与对应的PipedOutputStream进行连接,以建立管道通信。

PipedOutputStream pipedOutputStream = new PipedOutputStream(); pipedInputStream.connect(pipedOutputStream);

读取字节流:

使用read()方法逐个字节地读取字节流,返回一个整数表示读取的字节值,如果已经读取到末尾,则返回-1。 int data; while ((data = pipedInputStream.read()) != -1) { // 处理读取到的字节流 } 使用read(byte[] buffer)方法批量读取字节流,将读取的字节存储到指定的字节数组buffer中,返回实际读取的字节数。 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = pipedInputStream.read(buffer)) != -1) { // 处理读取到的字节流 }

关闭输入流: 使用close()方法关闭输入流,释放相关资源。

pipedInputStream.close();

通过PipedInputStream和PipedOutputStream的组合,我们可以实现线程间的管道通信,适用于一些需要在不同线程之间传递数据的场景。在使用PipedInputStream时,需要注意处理可能抛出的IOException异常,并及时关闭输入流以释放资源。

1.4.SocketInputStream

在Java中,SocketInputStream是Socket类的一个成员,用于从网络套接字中读取字节流。以下是SocketInputStream的一些基本使用方法:

获取SocketInputStream对象:

Socket socket = new Socket("host", port); // 创建Socket对象 InputStream inputStream = socket.getInputStream(); // 获取Socket的输入流 SocketInputStream socketInputStream = (SocketInputStream) inputStream; // 将输入流转换为SocketInputStream

读取字节流:

使用read()方法逐个字节地读取字节流,返回一个整数表示读取的字节值,如果已经读取到末尾,则返回-1。 int data; while ((data = socketInputStream.read()) != -1) { // 处理读取到的字节流 } 使用read(byte[] buffer)方法批量读取字节流,将读取的字节存储到指定的字节数组buffer中,返回实际读取的字节数。 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = socketInputStream.read(buffer)) != -1) { // 处理读取到的字节流 }

关闭输入流: 使用close()方法关闭输入流,释放相关资源。

socketInputStream.close();

通过SocketInputStream,我们可以从网络套接字中读取字节流,适用于一些需要进行网络通信的场景,如客户端与服务器之间的数据传输。在使用SocketInputStream时,需要注意处理可能抛出的IOException异常,并及时关闭输入流以释放资源。

1.5.AudioInputStream

AudioInputStream是Java中用于处理音频数据的输入流类。它是javax.sound.sampled包中的一部分。以下是AudioInputStream的一些基本使用方法:

创建AudioInputStream对象:

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);

这里的file是音频文件的路径或File对象。

获取音频格式:

AudioFormat audioFormat = audioInputStream.getFormat();

可以使用getFormat()方法获取音频流的格式信息,如采样率、位深度、声道数等。

读取音频数据:

使用read(byte[] buffer, int offset, int length)方法批量读取音频数据,将读取的字节存储到指定的字节数组buffer中,从offset位置开始存储,最多读取length个字节。 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = audioInputStream.read(buffer, 0, buffer.length)) != -1) { // 处理读取到的音频数据 }

关闭输入流: 使用close()方法关闭输入流,释放相关资源。

audioInputStream.close();

通过AudioInputStream,我们可以读取音频文件的数据,并进行相应的处理,如播放、转码、分析等。在使用AudioInputStream时,需要注意处理可能抛出的IOException异常,并及时关闭输入流以释放资源。

2.输出流

Java中的OutputStream是一个抽象类,用于表示字节输出流。它的一些常见的实现类包括:

FileOutputStream:向文件中写入字节流的输出流。ByteArrayOutputStream:向字节数组中写入字节流的输出流。PipedOutputStream:用于与PipedInputStream进行管道通信的输出流。FilterOutputStream的子类,如BufferedOutputStream、DataOutputStream、ObjectOutputStream等,用于提供额外的功能或过滤功能。SocketOutputStream:向网络套接字中写入字节流的输出流。AudioOutputStream:用于写入音频数据的输出流。其他自定义的OutputStream的子类,根据具体需求实现不同的输出流。

这只是一些常见的OutputStream实现类,还有其他的实现类可以根据具体需求进行选择和使用。

下面简单介绍FileOutputStream实现类的使用:

/** * 将字符串写入文件 */ public static void outputStreamFile(){ // 创建文件对象 File file = new File("D:\\MyCode\\demo\\src\\main\\resources\\static\\a.txt"); try { // 创建输出流对象 OutputStream outputStream = new FileOutputStream(file); // 将字符串转换为字节数组,并写入文件 outputStream.write("Hello, World!".getBytes()); // 刷新输出流,确保数据被写入文件 outputStream.flush(); // 关闭输出流 outputStream.close(); } catch (IOException e) { // 抛出运行时异常,以便上层处理 throw new RuntimeException(e); } } 二、字节流与字符流

在Java中,IO流可以分为字节流和字符流两种类型。字节流主要用于处理二进制数据,而字符流则用于处理文本数据。

字节流(Byte Stream):

InputStream和OutputStream是字节流的抽象基类,用于读取和写入字节数据。InputStream的常见实现类有FileInputStream、ByteArrayInputStream、SocketInputStream等,用于从文件、字节数组、网络套接字等读取字节数据。OutputStream的常见实现类有FileOutputStream、ByteArrayOutputStream、SocketOutputStream等,用于向文件、字节数组、网络套接字等写入字节数据。

字符流(Character Stream):

Reader和Writer是字符流的抽象基类,用于读取和写入字符数据。Reader的常见实现类有FileReader、InputStreamReader、StringReader等,用于从文件、字节流、字符串等读取字符数据。Writer的常见实现类有FileWriter、OutputStreamWriter、StringWriter等,用于向文件、字节流、字符串等写入字符数据。

字符流相对于字节流的优势在于能够更方便地处理文本数据,因为它们会自动处理字符编码和解码的过程。而字节流则更适合处理二进制数据,如图片、音频、视频等。

在实际开发中,根据需要选择合适的流类型来处理数据,如果处理的是文本数据,通常会使用字符流;如果处理的是二进制数据,通常会使用字节流。

1. 字节流的使用: import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class IOFile { public static void main(String[] args) { // 创建一个文件对象,指定文件路径 File file = new File("D:\\MyCode\\demo\\src\\main\\resources\\static\\a.txt"); try { // 创建一个文件输入流对象,用于读取文件内容 InputStream fileInputStream = new FileInputStream(file); // 创建一个字节数组,用于存储文件内容 byte[] buffer = new byte[fileInputStream.available()]; int data; // 循环读取文件内容,直到文件末尾 while ((data = fileInputStream.read(buffer, 0, buffer.length)) != -1) { // 将字节数组转换为字符串,并打印输出 System.out.println(new String(buffer)); } // 关闭文件输入流 fileInputStream.close(); } catch (IOException e) { // 如果发生IO异常,抛出运行时异常 throw new RuntimeException(e); } } } 2.字符流的使用: // 创建一个File对象,表示要读取的文件路径 File file = new File("D:\\MyCode\\demo\\src\\main\\resources\\static\\a.txt"); try (Reader reader = new FileReader(file)) { // 创建一个字符数组作为缓冲区,用于存储从文件中读取的字符数据 char[] buffer = new char[1024]; int charsRead; // 循环读取文件中的字符数据,直到读取到文件末尾 while ((charsRead = reader.read(buffer)) != -1) { // 将缓冲区中的字符数据转换为字符串,并打印输出 System.out.println(new String(buffer)); } } catch (IOException e) { // 捕获可能发生的IO异常,并打印异常信息 e.printStackTrace(); } 三、高级IO流

Java还提供了一些高级的IO流,如缓冲流、对象流和序列化等,用于更方便地处理数据。

1.缓冲流的使用: try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) { String line; while ((line = reader.readLine()) != null) { // 处理读取的数据 } } catch (IOException e) { e.printStackTrace(); } 2.对象流和序列化的使用: try (ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("data.ser"))) { MyObject obj = new MyObject(); // 自定义对象 outputStream.writeObject(obj); } catch (IOException e) { e.printStackTrace(); } 3.压缩文件处理 3.1.ZipFile

ZipFile是Java中用于读取ZIP文件的类。它提供了一些方法来获取ZIP文件中的条目(文件或目录)以及读取条目的内容。以下是ZipFile的介绍以及基本的使用方法:

创建ZipFile对象:

ZipFile zipFile = new ZipFile("example.zip"); // 创建ZipFile对象,指定ZIP文件路径

获取ZIP文件中的条目信息:

使用entries()方法获取ZIP文件中的所有条目。使用getName()方法获取条目的名称。使用isDirectory()方法判断条目是否为目录。 Enumeration entries = zipFile.entries(); // 获取ZIP文件中的所有条目 while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); // 获取条目的名称 boolean isDirectory = entry.isDirectory(); // 判断条目是否为目录 System.out.println("Name: " + name + ", Is Directory: " + isDirectory); }

读取ZIP文件中的条目内容:

使用getInputStream()方法获取条目的输入流。使用输入流读取条目的内容。关闭输入流。 ZipEntry entry = zipFile.getEntry("file1.txt"); // 获取指定名称的条目 InputStream inputStream = zipFile.getInputStream(entry); // 获取条目的输入流 byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) >= 0) { // 处理条目的内容 } inputStream.close(); // 关闭输入流

关闭ZipFile: 使用close()方法关闭ZipFile,释放相关资源。

zipFile.close();

通过ZipFile,我们可以读取ZIP文件中的条目信息以及读取条目的内容。在使用ZipFile时,需要注意处理可能发生的IO异常,并及时关闭输入流以释放资源。

3.2.ZipInputStream

ZipInputStream是Java中用于解压缩ZIP文件的输入流类。它可以逐个读取ZIP文件中的条目,并提供了方法来读取条目的内容。以下是ZipInputStream的介绍以及基本的使用方法:

创建ZipInputStream对象:

InputStream inputStream = new FileInputStream("example.zip"); // 创建输入流,指定ZIP文件路径 ZipInputStream zipInputStream = new ZipInputStream(inputStream); // 创建ZipInputStream对象

读取ZIP文件中的条目信息:

使用getNextEntry()方法获取下一个ZIP文件条目。使用getName()方法获取条目的名称。使用isDirectory()方法判断条目是否为目录。 ZipEntry entry = zipInputStream.getNextEntry(); // 获取下一个ZIP文件条目 while (entry != null) { String name = entry.getName(); // 获取条目的名称 boolean isDirectory = entry.isDirectory(); // 判断条目是否为目录 System.out.println("Name: " + name + ", Is Directory: " + isDirectory); entry = zipInputStream.getNextEntry(); // 获取下一个ZIP文件条目 }

读取ZIP文件条目的内容:

使用read()方法从输入流中读取ZIP文件条目的内容。使用缓冲区来存储读取的数据。关闭当前ZIP文件条目。 ZipEntry entry = zipInputStream.getNextEntry(); // 获取下一个ZIP文件条目 while (entry != null) { String name = entry.getName(); // 获取条目的名称 boolean isDirectory = entry.isDirectory(); // 判断条目是否为目录 System.out.println("Name: " + name + ", Is Directory: " + isDirectory); if (!isDirectory) { byte[] buffer = new byte[1024]; int length; while ((length = zipInputStream.read(buffer)) >= 0) { // 处理条目的内容 } } zipInputStream.closeEntry(); // 关闭当前ZIP文件条目 entry = zipInputStream.getNextEntry(); // 获取下一个ZIP文件条目 }

关闭ZipInputStream: 使用close()方法关闭ZipInputStream,释放相关资源。

zipInputStream.close();

通过ZipInputStream,我们可以逐个读取ZIP文件中的条目,并读取条目的内容。在使用ZipInputStream时,需要注意处理可能发生的IO异常,并及时关闭输入流以释放资源。

3.3.ZipOutputStream

ZipOutputStream是Java中用于创建ZIP文件的输出流类。它可以将多个文件或目录压缩成一个ZIP文件。以下是ZipOutputStream的介绍以及基本的使用方法:

创建ZipOutputStream对象:

OutputStream outputStream = new FileOutputStream("example.zip"); // 创建输出流,指定ZIP文件路径 ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream); // 创建ZipOutputStream对象

添加文件或目录到ZIP文件:

使用putNextEntry()方法创建一个新的ZIP文件条目,并指定条目的名称。使用write()方法将文件内容写入ZIP文件。使用closeEntry()方法关闭当前ZIP文件条目。 File file1 = new File("file1.txt"); ZipEntry entry1 = new ZipEntry("file1.txt"); // 创建ZIP文件条目 zipOutputStream.putNextEntry(entry1); // 添加ZIP文件条目 FileInputStream fileInputStream1 = new FileInputStream(file1); byte[] buffer1 = new byte[1024]; int length1; while ((length1 = fileInputStream1.read(buffer1)) >= 0) { zipOutputStream.write(buffer1, 0, length1); // 写入文件内容到ZIP文件 } fileInputStream1.close(); zipOutputStream.closeEntry(); // 关闭ZIP文件条目 File directory = new File("directory"); File[] files = directory.listFiles(); for (File file : files) { if (file.isDirectory()) { // 如果是目录,创建对应的ZIP文件条目 String entryName = file.getName() + "/"; ZipEntry entry = new ZipEntry(entryName); zipOutputStream.putNextEntry(entry); // 添加ZIP文件条目 zipOutputStream.closeEntry(); // 关闭ZIP文件条目 } else { // 如果是文件,创建对应的ZIP文件条目,并写入文件内容 String entryName = file.getName(); ZipEntry entry = new ZipEntry(entryName); zipOutputStream.putNextEntry(entry); // 添加ZIP文件条目 FileInputStream fileInputStream = new FileInputStream(file); byte[] buffer = new byte[1024]; int length; while ((length = fileInputStream.read(buffer)) >= 0) { zipOutputStream.write(buffer, 0, length); // 写入文件内容到ZIP文件 } fileInputStream.close(); zipOutputStream.closeEntry(); // 关闭ZIP文件条目 } }

关闭ZipOutputStream: 使用close()方法关闭ZipOutputStream,释放相关资源。

zipOutputStream.close();

通过ZipOutputStream,我们可以将多个文件或目录压缩成一个ZIP文件。在使用ZipOutputStream时,需要注意处理可能发生的IO异常,并及时关闭输出流以释放资源。

3.4.解压文件 public class zipUtils { /** * 解压缩zip文件 * @param zipFilePath zip文件路径 * @param destDirectory 解压目标目录 * @throws IOException */ public static void unzip(String zipFilePath, String destDirectory) throws IOException { // 创建目标目录 File destDir = new File(destDirectory); createParentFile(destDir); if (!destDir.exists()) { destDir.mkdir(); } // 创建ZipInputStream对象 ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath)); ZipEntry entry = zipInputStream.getNextEntry(); while (entry != null) { String entryName = entry.getName(); boolean directory = entry.isDirectory(); String filePath = destDirectory + File.separator + entryName; createParentFile(new File(filePath)); System.out.println("文件名字:"+entryName+" 是否为目录:"+directory); if (!entry.isDirectory()) { // 如果是文件,创建对应的目录并写入文件内容 extractFile(zipInputStream, filePath); } else { // 如果是目录,创建对应的目录 File dir = new File(filePath); dir.mkdir(); } zipInputStream.closeEntry(); // 如果解压出来的文件是zip文件,则递归解压 if(filePath.endsWith(".zip")) { unzip(filePath,new File(filePath).getParent()); new File(filePath).delete(); } entry = zipInputStream.getNextEntry(); } zipInputStream.close(); } /** * 递归创建父目录 * @param file 目标文件 */ public static void createParentFile(File file) { File parentFile = file.getParentFile(); if(!parentFile.exists()) { createParentFile(parentFile); } parentFile.mkdirs(); } /** * 从ZipInputStream中提取文件并写入目标路径 * @param zipInputStream ZipInputStream对象 * @param filePath 目标文件路径 * @throws IOException */ private static void extractFile(ZipInputStream zipInputStream, String filePath) throws IOException { BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(filePath)); byte[] buffer = new byte[1024]; int length; while ((length = zipInputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } outputStream.close(); } } 3.5.压缩文件 /** * 将指定目录下的文件压缩为Zip文件 * @param sourceDirectory 源目录路径 * @param zipFilePath Zip文件保存路径 * @throws IOException IO异常 */ public static void zip(String sourceDirectory, String zipFilePath) throws IOException { FileOutputStream fileOutputStream = new FileOutputStream(zipFilePath); ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); addDirectoryToZip("", sourceDirectory, zipOutputStream); zipOutputStream.close(); fileOutputStream.close(); } /** * 将指定目录下的文件及子目录压缩到ZipOutputStream中 * @param parentPath 父目录路径 * @param sourceDirectory 源目录路径 * @param zipOutputStream Zip输出流 * @throws IOException IO异常 */ private static void addDirectoryToZip(String parentPath, String sourceDirectory, ZipOutputStream zipOutputStream) throws IOException { File directory = new File(sourceDirectory); File[] files = directory.listFiles(); byte[] buffer = new byte[1024]; int length; for (File file : files) { if (file.isDirectory()) { addDirectoryToZip(parentPath + file.getName() + "/", file.getAbsolutePath(), zipOutputStream); } else { FileInputStream fileInputStream = new FileInputStream(file); zipOutputStream.putNextEntry(new ZipEntry(parentPath + file.getName())); while ((length = fileInputStream.read(buffer)) > 0) { zipOutputStream.write(buffer, 0, length); } fileInputStream.close(); } } } 结语:

本文深入介绍了Java IO流的使用和应用场景,包括输入流、输出流、字节流、字符流以及高级IO流的使用。通过学习和实践这些知识,读者将能够更好地处理输入输出操作,并应用于实际开发中。希望本文对读者有所帮助,让我们一起深入探索Java IO流的奥秘



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3