You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.3 KiB

Data stored in a text file is represented in human-readable form. Data stored in a binary file is represented in binary form. You cannot read binary files. They are designed to be read by programs. For example, Java source programs are stored in text files and can be read by a text editor, but Java classes are stored in binary files and are read by the JVM. The advantage of binary files is that they are more efficient to process than text files.

Java的I/O抽象

网络通讯对象的抽象

我们考虑一个问题,如果要实现两个程序之间的通讯应该如何使用面向对象的思维来设计?首先我们需要把通讯进行抽象化,如果使用一个对象来进行通讯,应该如何操作? Alt text

在上图中,Side_ASide_B分别代表通讯系统的两个端点,如果这两个端点要实现通讯,最简单的方式需要实现两个函数:

public byte[] read(){...};					// 读取
public void[] write(byte buf[]){...}		// 写入

这里无论是读取还是写入都使用到一个最基本的结构-字节数组;因为在计算机中,字节是处理数据的基本单位。当然,我们没有考虑到实际通讯当中的复杂性,例如:连接、断开、错误处理等等。但是这两个基本的函数可以是我们通讯的基础。

那么要实现一个可以通讯的类就需要对这两个函数进行实现完成函数体中的算法。但是问题又来了通讯可以是基于以太网的也可以是基于无线的或者是光通讯等多种通讯媒介。那么我们势必为每个通讯类都实现这两个函数。这样就有了下面的这个UML

classDiagram
      class ComEthernet{
          +read()
          +write()
      }
      class ComWifi{
          +read()
          +write()
      }
      class ComFiber{
          +read()
          +write()
      }

通过前面的知识,我们可以看出,这三个类都有同样的函数签名,应该进行抽象,因此我们增加了一个抽象父类-Com

classDiagram
	  Com <|-- ComEthernet
	  Com <|-- ComWifi
	  Com <|-- ComFiber

	  class Com{
	  <<Abstract>>
          +read()*
          +write()*
      } 	

      class ComEthernet{
          +read()
          +write()
      }
      class ComWifi{
          +read()
          +write()
      }
      class ComFiber{
          +read()
          +write()
      }

从上个例子中我们看到了如何从面向对象的思维来解决实际的问题。在即将学到的网络课程当中会发现网络的构建真的也有面向对象的思想。

文件的抽象

好了现在我们来看看主题文件系统。文件中存放的最小单元也是字节byte因此可以沿用上面网络的例子。只不过这是一个真实的

环境了,比上述的网络的例子要包含更多的内容,但是基本的思路是一致的。

image-20230326142026039

在文件系统中有个流Stream的概念初学者可能对这个概念理解起来比较困难。上述两个抽象类InputStreamOutputStream其实主要的目的就是定义读和写的抽象方法InputStreamOutputStream。后面的派生类都需要实现这两个方法。只不过加入了更多的内容。

分离读/写主要目的是因为有些类只需要读取或者写入功能。

注意InputStreamOutputStream这两个类不是只针对文件系统凡是可以进行字节读写的类都可以由这两个类进行扩展例如网络中的通讯。从这里可以看到Java不仅仅是面向对象的语言由于Java提供了一套基本的API使得java也是一套编码规范或者是框架。多数情况下需要满足Java中基本类型和接口的继承才能使程序更具有通用性。

InputStream

image-20230326144650268

注意:read()函数后面的返回标注的是int其实返回的值是0255。因为java没有unsigned类型byte类型的取值范围达不到0255所以使用int作为返回。

可以看到有多个关于读的函数,具体含义看书上的解释。

提高为什么只是read()函数是抽象函数其几个重载的read函数并不是抽象函数可以通过分析源码来理解。

OutputStream

image-20230326151620467

同样write函数的参数类型虽然是int实际是写入一个字节。

FileInputStream/FileOutputStream

image-20230326151754475

这两个类型实现了对文件的读和写操作。

import java.io.*;

public class TestFileStream {
	public static void main(String[] args) throws IOException {
		try (
				// Create an output stream to the file
				FileOutputStream output = new FileOutputStream("/home/danny/temp.txt");) {
			// Output values to the file
			for (int i = 1; i <= 10; i++)
				output.write(i);
		}

		try (
				// Create an input stream for the file
				FileInputStream input = new FileInputStream("/home/danny/temp.txt");) {
			// Read values from the file
			int value;
			while ((value = input.read()) != -1)
				System.out.print(value + " ");
		}
	}
}

还记得try(...){...}的语法形式吗小括号中只能是构造函数的语句大括号中可以是任意语句小括号中打开的文件不用关闭close(),该语法可以保证打开的文件自动关闭。

注意这两个类实现都是字节的操作因此文件是二进制文件。虽然后缀名是txt如果尝试打开会得到乱码。

A java.io.FileNotFoundException would occur if you attempt to create a FileInputStream with a nonexistent file.

当使用 FileInputStream 的构造函数打开一个不存在的文件的时候,将会抛出java.io.FileNotFoundException异常。

构造函数也是函数,因此也可以抛出异常。如果阅读源码,会发现FileOutputStream的构造函数也可以抛出异常。

注意这两个类的构造函数是重载的有一个版本可以接受一个File对象作为需要打开的文件。其实在Java中大多预定义类都可以接受字符串或者是File对象具体情况需要查看源码实现。

DataInputStream/DataOutputStream

image-20230326154038645