首页 > 基础资料 博客日记
JAVA I/O流(超详细)
2024-08-14 04:00:05基础资料围观157次
目录
1.DataOutputStream类和DataInputStream类
1.对象输出流ObjectOutputStream实现序列化
2.对象输入流ObjectInputStream实现反序列化
1.File类
在程序运行的过程中,输入/输出是常用的操作,也是必需的部分。允许程序读取外部数据,包括来自磁盘,光盘、文件等存储设备的数据,这类操作属于输入数据,数据的输入和输出离不开文件,经常使用到的文件类型有很多,如扩展名为.txt、.doc、.bmp、.class的文件等。
File类是java.io包下代表操作与平台无关的文件和目录的类,也就是说,如果需要在程序中操作文件或目录,就可以通过File类实现对文件或目录的新建、删除、重命名等操作。下面学习如何使用File类操作文件和目录。
1.操作文件和目录
在程序中,一个File对象可以代表一个文件或目录。它可查出与文件相关的信息,如名称、修改日期、文件大小等。File类的构造方法如下。
public File(String pathName)
其中,pathName指定文件路径。因此,在创建File对象的时候必须设置文件路径。例如,操作C盘下的test.txt文件,创建File对象的语句如下。
File file=new File("C:\\test.txt");
File类的常用方法如下表所示。
2.I/O流概述
File类能够实现对文件和目录的创建、删除等基础性操作,但是无法实现对文件内容的操作,也就是无法实现对文件的读写。而java中的I/O流能够方便地实现数据的输入和输出,是实现文件读写的基础。流,是指一连串流动的字符,是以先进先出的方式发送和接收数据的通道。既然是流,必然有源端和目的的端,它们可以是计算机内存的某些区域,也可以是磁盘、文件等,甚至可以是Internet上的某个统一资源定位符。所谓I/O流就是实现数据输入和输出的流,在I/O流的基础上,可以实现对文件的读写操作。那么文件的读写指什么?
读文件,是指把文件中的数据读取到内存;反之,写文件是把内存中的数据写到文件中。
java把所有流型都封装到java.io包中,用以实现I/O操作。下面从不同的角度对流进行分类,它们在概念上会有重叠的地方。
1.按流向划分:输入流和输出流
流具有明确的方向性,按照流的流向来分,可以分为输入流和输出流。
- 输入流:只能从中读取数据,而不能写入数据的流,实现程序从数据源中读数据,其中,数据源指文件,磁盘等能够存储数据的媒介。
- 输出流:只能向其写入数据,而不能从中读数据的流,实现程序向目标数据 源中写数据。
- java的输入流主要以InputStream和Reader作为基类,而输出流则主要以OutputStream和Writer作为基类。它们都是一些抽象类,无法直接实例化对象。
2.按处理单元划分:字节流和字符流
按照处理单元划分,java中的流可分为字节流和字符流,它们的区别在于操作的数据单元不同。
- 字节流:以8位字节为操作数据单元的流,可用于操作二进制数据。
- 字符流:以16位的字符为操作数据单元的流,可用于操作文本数据。
- 通俗地理解,可以把I/O流看作一个水管,这个水管中依次排列着许多水滴,每滴水滴就是一个处理单元,即一个字节或字符。在字节流中每滴水滴是一个字节,在字符流中每滴水滴是一个字符。
- 字节流和字符流的用法几乎相同。它们向下细分,还可以划分为字节输入流、字节输入流、字符输出流。
3.按流的角色划分:节点流和处理流
按照流的角色划分,java中的流可分为节点流和处理流。
- 节点流:可以直接向一个特定的存储介质(如磁盘、文件)读写数据的流。当使用节点流进行读写数据操作时,程序直接连接到数据源。
- 处理流:用于对一个已存在的流进行连接和封装,是通过封装后的流实现数据读写操作流。
当使用处理流进行读写操作时,程序并不会直接连接到实际的数据源。使用处理流包装节点流,程序通过处理流执行输入和输出功能,让节点流与文件或磁盘等存储介质交互。它可以隐藏底层节点的差异,并且对外提供更加方便的输入和输出的方法,因此节点流也被称为包装流。它的优势如下。
- 对开发人员来说,使用处理流进I/O操作更简单。
- 使用处理流的执行效率高。
3.字节流
java.io包中的流按存储单元分类主要有字节流和字符流两大类,两类都具有输入/输出操作。它们各自的基类如下表所示,这些基类均为抽象类。
下面分别介绍使用字节流和字符流实现读/写操作。字节流主要操作的是byte类型数据,其基类就是OutputStream类和InputStream类。
1.字节输入流基类:OutputStream
OutputStream类为抽象类,必须使用该类的子类进行实例化对象。如果需要操作文件,则使用FileOutputStram实例化。OutputStream类中的主要操作方法如下表所示。
2.字节输出流FileOutputStream类
在实际应用中,通常使用OutputStream类的FileOutputStream子类向文本文件写入数据,常用的构造方法如下表所示。
3.字节输入流基类:InputStream
即然可以使用OutputStream类向文件中写数据,那么也可以通过InputStream类从文件中读数据。InputStream类同样也是抽象类,该类中定义的主要文件如下表所示。
4.字节输入流FileInputStream类
在实际开发中,通常使用InputStream类的FileInputStream子类实现文本文件内容的读取,其常用构造方法如下表所示。
在使用字节输入流读取数据时,除可以以字节为读取单元外,还可以以字节数组作为一个读取单元。理解为,在向窗口中运输水时,除可以一滴一滴运输外,还可以一桶一桶地运输。使用read()方法重载方法,如read(byte[] b)可以实现每次读多个字节。
除此之外,在使用字节数组存储数据时,需定义整形变量len记录实际读取到的字节长度;否则,当字节数组长度大于读取到的实际数据长度时,如果将其直接转换成字符串输出,那么会有多余的空白空间。因此,需要记录读取到数据的实际长度,根据实际数据长度输出。
4.字符流
在java中,一个字符占用内存的两个字节。如果用字节流处理文本文件,则程序内部需要将字节转换成字符,降低了执行效率,所以当输入和输出文本文件时,尽量使用字符流。java提供了Writer类和Reader类来操作字符。
1.字符输出流基类:Writer
Writer类是字符输出流的抽象基类,Writer类中定义的使用方法如下表所示。
2.字符输出流FileWriter类
FileWriter类是Writer类常用的子类,使用该类可以以字符为数据处理单元向文本文件中写数据。
3.字符输入流基类:Reader
Reader类是字符输入流的基类,它是抽象类,Reader类中的常用方法如下表所示。
对比Reader类和InputStream类所提供的方法,会发现它们基本是相同的。如果将输入流比作水管,那么可以通过read()方法每次读取一滴"水滴" ,也可以通过read(byte[] c)方法或read(char[] c,int off,int len)方法每次读取多滴“水滴”,多次重复执行取水的过程,当以上方法返回修值为-1时,表示到了输入流的结束点,也就是取水完毕。
4.字符输入流FileReader
Reader类同样是抽象类,可以使用其子类FileReader创建对象。
5.缓冲流
前面介绍的IO流全部属于节点流,能够与存储介质(如磁盘、文件等)直接进行数据交互。为提高读写数据的执行效率,java.io包提供了缓冲流,缓冲流属于处理流。下面以字符缓冲为例介绍缓冲流的用法。
1.字符缓冲输出流BufferedWriter类
BufferedWriter类是Writer类的子类,BufferedWriter类可以把一批数据写到缓冲区,默认情况下,只有在缓冲区满的时候,才会把缓冲区的数据真正写到目的地,这样能减少数据的次数,从而提高I/O操作的执行效率。BufferedWriter类的常用构造方法如下表所示。
2.字符缓冲输入流BufferedReader类
BufferedReader类是Reader类的子类,它与FileReader类的区别在于BufferedReader类有缓冲区,它可以先把一批数据读到缓冲区中,接下来的读操作都是从缓冲区内获取数据,避免每次都从数据源读取数据进行字符编码转换,从而提高读取操作的效率。BufferedReader类常用的构造方法如下表所示。
6.数据操作流
前面学习的各类流实现了对文本文件的读写操作,但常见的文件读写操作中还有一种二进制文件的读写操作。接下来介绍数据操作流DataOutputStream和DataInputStream,使用它们实现对二进制文件的读写操作。
1.DataOutputStream类和DataInputStream类
DataOutputStream类是OutputStream类的子类,利用DataOutputStream类写二进制文件的实现步骤与使用FileOutputStream类写文件的步骤极其相似,而且用到了FileOutputStream类。同样DataInputStream类是InputStream类的子类,在使用上也与FileInputStream类很相似。
7.序列化与反序列化
在前面的内容中实现了通过java程序读写文件。事实上,在感叹词中经常需要将对象的信息保存到磁盘中便于以后检索。可以将对象中的属性信息逐一记录到文本文件中,但这样程序员不得不为每一个对象编写代码;当使用时再将这些信息从文本文件中还原,这样需要对对象中的信息进行逐一处理,过程很烦琐且非常容易出错,而序列化提供了实现这个目标的便捷方法。
简单地说,序列化就是将对象的状态更轻松简单地存储到特定的存储介质中的过程,也就是将对象状态转换为可保持或可传输格式的过程。使用序列化的意义在于将java对象序列之后,可以将其转换为字节序列,这些字节序列可以被保存在磁盘上,也可以借助网络进行传输,同时序列化后的对象保存的二进制状态,这样实现了平台无关性,即可以将在Windows操作系统中实现的一个对象,传输到Linux操作系统的机器上。反序列化是将特定的存储介质中的数据重新构建对象的过程,通过反序列化后得到相同对象,而无需担心数据因平台不同出现异常。序列化和反序列化的过程如下图所示。
实现序列化和反序列化操作,需要使用对象操作流, 以下分别介绍对象输出流(ObjectOutputStream),和对象输入流(ObjectInputStream)。
1.对象输出流ObjectOutputStream实现序列化
简单地说,对象的序列化,是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便地实现对象的传输和存储。如果一个类的对象需要被序列化 ,则这个对象所属类必须实现java.io.Serializable接口,引接口定义如下。
public interface Serializable{}
因为在此接口中并没有定义任何方法,所以此接口是一个标识接口。一旦某个类实现 了Serializable接口,该类的对象就是可序列化的。在JDK1.8类库中有些类(如String类、包装类和日期时间类等)实现Serializable接口。现创建一个Person类,并标记该类的对象是可序列化的。
一个对象如果进行序列化输出,则需要使用ObjectOutputStream类,此类的常用方法如下表所示。
2.对象输入流ObjectInputStream实现反序列化
既然能将对象的状态保存到存储介质中,那么如何将这些对象状态读取出来呢?这就要用到反序列化。反序列化就是序列化的相反操作,序列化是将对象的状态信息保存到存储介质中的过程,反序列化则是从特定存储介质中读取数据并重新构建成对象的过程。通反序列化,可以将存储在文件上的对象信息读取出来,然后重新构建为对象。这样就不需要再将文件上的信息——读取、并分析,重新组织为对象。
执行反序列化操作需使用对象输出流ObjectInputStream,它可以直接把序列化后的对象还原。ObjectInputStream类的常用方法如下表所示。
到此为止,大家学习java体系中主要的I/O流,当然,除此之外,还有其他流,这些流看上去杂乱,但实际上是很有规律的。java体系中常用流的分类如下表所示。
从上表中可以看出以下几点。
- 所有的基类都抽象类,无法直接创建实例,需要借助其实现类。
- 所有的输出流用于实现写数据操作,所有的输入流用于实现读取操作。这个输入和输出是相对程序而言的。
- 所有的文件流直接与存储介质关联,也就是需指定物理节点,属于节点流,其他流的创建需要在节点流的基础上进行封装,使其具有特殊的功能。例如,缓冲流在节点流的基础上增加了缓冲区,对象流在节点流的基础上可实现序列化对象。
- 在操作文本文件时,应使用字符流,字节流可以处理二进制数据,它的功能比字符流理强大。因为计算机里所有的数据都是二进制数据,如果用字节流处理文本文件,还需要把这些字节转换成字符,就增加了编程的复杂度,降低了执行效率。所以当输入和输出是文本文件时,尽量使用字符流。如果是二进制文本,则应考虑使用字节流。
- 在实际开发中,可能会使用到其他类型的I/O流,它们也是四个抽象基类的实现类,用到时可查阅API帮助文档自行学习。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: