Java输入输出流
java存在在我们程序中的每一个地方,只要涉及到数据的传输,它就存在。比如我们想把一个MV文件复制粘贴到别的文件夹里,就是数据转移的过程。
此外当我们注册新的app,然后需要上传各种头像时,就相当于把数据由本地上传到了网络,也属于数据的传输过程
输出流
比如,我们通过System.out.println("kilig");输入到屏幕上,实际上是这样进行的:
程序通过写操作,将kilig字符依次通过流,形成一个流操作。显示到屏幕上
流就是指一连串的流动字符,以先进先出的方式发送信息的通道。
输入流
我们的输入流,就是从键盘接入数据。我们常用的有 Scanner sc =new Scanner(System.in); 主要就是靠System.in这个输入流来完成的。
程序由数据源(键盘)读取数据,通过流输入到程序中。
其中文件既可以作为输入设备也可以作为输出设备。
- 文件输入-----读取
- 文件输出-----写入
File类
文件,文件可认为是相关记录或放在一起的数据的集合
在Java中,使用java.io.File类对文件进行操作
我们打开Java API的文档,来更加详细的了解一下💁💁💁点击打开JavaAPI
可以看到,在API中,我们的file类来自java.io。这是它的构造方法和一些常用的方法。可读,可写,创建目录,创建多级目录等等
首先,我们创造一个示例:
可以看到,我们的主页面有Kilig的文件夹,打开后分别有io和set两个文件夹,io里面还包括score.txt的文件
1.首先创建一个针对针对score.txt的file类的对象
- 在window系统中:C:\\...
- 在LIiux系统中:C:/...
2.展示方法的使用
2.1创建目录
我们尝试在set的文件下创建一个文件。首先需要判断是否存在改文件名
可以看到到,我们之前set下是什么都没有的,现在已经被创建了HashSet的文件夹了!
- 创建单一文件夹用到的是mkdir()方法
- 创建多级目录用到的是mkdirs()方法
2.2创建文件
可以看到,确实在这个文件下被创建了这个文件。
字节流
- 字节数输入流:InputStream
- 字节输出流:OutputStream
字节输入流的分类:
字节输出流的分类:
再次针对介绍几个输入流的使用方法,其他的使用起来是相似的。
FileInputStream
- 从文件系统中的某个文件中获得输入字节 (比如类似将文件a粘贴复制的读写过程)
- 用于读取诸如图像数据之类的原始字节流(字节流用途用于读取图像数据的二进制数据)
让我们打开JavaAPI看一看相关的几个类💁💁💁点击打开JavaAPI
在构造方法中,我们可以看到
- 方法FileInputStream(File file)表明它的参数可以是一个File文件&目录的对象。表示我们的输入设备是文件而不是网络或者扫描仪
- 方法FileInputStream(String name)是与上面相似的,是字符串作为对象
然后一些方法的总结如下:(如果返回值为-1,则表示已经到达文件的末尾!)
方法名 | 描述 |
public int read() | 从输入流中读取一个数据字节 |
public int read(byte[] b) | 从输入流中将最多b.length个字节的数据读入到一个byte数组中 |
public int read(byte[] b,int off,int len) | 从输入流中将最多len个字节的数据读入byte数组中 |
public void close() | 关闭此文件输入流并释放与次流有关的所有系统资源 |
怎样使用:
在开始代码使用之前,我们先在我们的工程目录下创建一个已经存在的目录文件kilig.txt。内容是“Hello!KIlig!”
它的相对路径是“/Users/kilig/Kilig/io/kilig.txt”
那么让我们看看怎样在程序中读取这个文件的内容
public int read();
1.首先创建一个FileInputStream的对象,此时会抛出一个FileNotFoundException的异常,找不到文件的异常,因为有啃呢个在读时会发生找不到异常的情况
2.读取数据并且进行输出,可以看到是一个整数72。
3.然而文档的数据时字符型的,不是整形。所以我们需要将字符型的数据转化为整型。同时记得在读取完毕后关闭输入流,释放资源。
4.可以看到,我们现在可以成功的读取文档的 字符H了。那么我们现在需要循环将文档中的内容读出来,那么什么是循环的结束标志呢?
前面的文档我们看到了,当.read() 方法返回值是-1 的时候,就表示文档已经到末尾了
或者这样写也可以!将整合在一起
切记,在输出一个字符后,要记得在进行读取,否则会进入死锁
5.看看其他的read()方法怎么使用
public int read(byte[] b);
读取数据存放到byte字节数组中。
或者限制输出字符的长度:
public int read(byte[] b,int off,int len);
文件的拷贝
首先我们导入两个图片文件,一个是cat.jpg 一个是happy.gif
让我们尝试对文件进行复制和粘贴,首先需要读取已经存在的数据,将数据读入到字节数组当中,然后写入到新的拷贝文件中。
运行完,查看工作台,发现,我们的工作台路径下存在这个文件了。
【注意】仔细观察两个文件的内存:
可以发现同样是复制过来的文件,为什么happyCopy的内存就要比happ的大1kb呢?
【Because】那是因为在我们的程序中,我们定义的byte类型的数组,在读取数据时,前几次读数据,是会填充满字节数a的,所以在进行写操作时不会发生问题的。但是最后一次如果数据读取不够1024,那么将最后的数据写入时,就肯定会比之前的原文件数据大了!
可以看到,现在这两个文件的内存容量就是一样的了。
字节缓冲流
- 缓冲输入流:BufferedInputStream
- 缓冲输出流:BufferedOutputStream
之前的文件读写,是直接从硬盘读取数据的,这样的读写速度,是比较慢的,如果直接从内存中读写的话,速度会很快。使用缓冲流进行读取数据时,会提高读写速度。
BufferedInputStream是不能直接读取文件数据的,需要与FileInputStream互相结合使用。加入缓冲流读取文件,读的过程如下:
- 首先FileInputStream先将数据由数据源读取出来,此时不是直接放入程序中进行接收,
- 通过缓冲流BufferedInputStream通道继续读取,通过缓冲流读入字节数组当中
让我们看看缓冲流的文档💁♀️💁♀️💁♀️点击打开JavaAPI
通过构造方法可以看出,它的参数可以使任何InputStream的子类,第二个构造方法是创造一个指定大小的InputStream。
方法的使用和FileInputStream差不多
注意的是在BufferOutputStream的方法中:
缓冲区的大小实际时候很大的,假设缓冲区的大小为20字节,当存放到缓冲区的数据刚好为20字节时,它会自动执行写操作,将数据自动写入到FileOutoutstream
中,并且通过文件输出流写入文件。但是如果当存入文件的数据很小为10个字节时,那么此时不会触发write方法的写操作,数据就不会写入文件中。所以在缓冲区不满情况下,会调用faush()方法自动清空。
缓冲流的案例使用:
我们需要将输入输出流全部注入,然后进行些操作
让我们运行看一看!
可以看到,我们的score.txt文本是没有内容的。因为我们的这个操作只是将数据写到了缓冲区中,还没有将它卸乳文件中,表明缓冲区未满。此时就需要进行强制清空。
运行看一看!
现在我们进行读操作:
记得程序运行完要关闭输入输出流哦
字符流
- 字符输入流:Reader
- 字符输出流:Writer
字符输入流分类:
字节输出流分类:
字符和字节输入输出流的区别在与用途:
字节的输入输出流主要用于读取和写入数据,都是需要二进制格式的数据。
字符输入输出流适应:通过QQ发送消息给另外一个人,发送的是一串字符,在网络上传输时时一串二进制(写),在传输成功后,对方从网络获取数据时页不会是二进制(读)。
字节字符转换流
- InputStreamReader
- OutputStreamWriter
让我们打开文档看看这两个流的方法💁💁💁点击打开JavaAPI
可以看到它的参数是InputStream是字节输入流,表示所有的字节输入流都可以作为它的参数。
方法中有获取字符集,close(),read(),等等。值得注意的是之前我们学的字节输入流中read(byte[])类型,而在字符流中为read(char[])类型。
在字符输出流的介绍中:
可以看到,有些方法还是和字节输出流比较类似的
怎样使用?
输入流的使用
为了方便看看怎么去使用这些方法,我们在eclipse的工作台导入一份文件
进行读操作
可以调用.read(int)方法,此时在输出时要记得有转译字符
也可以调用.read(char[])的方法,此时要记得给字符组转换为字符串,
此时为什么会发生这样的输出内容呢,因为我们在定义数组的长度时为[10],当读取最后一次时,无法保证字符数组为满,此时剩余的末尾的就不是我们想要的。此时就需要我们转为实际长度。
输出流的使用
可以看到,我们的复制文件刷新出来了:
【补充】我们可以看到,我们的数组字符串的编码格式是UTF-8格式,那么如果我们想在复制的同事转换为其他的编码格式怎么办呢?
我们可以在创建写对象的同时,将需要的字符编码格式放入其中
此时打开看看写文件的数据内容
可以看到,出现了一堆中文乱码,这是因为编码不一致造成的,解决办法:
- 我们可以删除“GBK”
- 在进行读取数据时,就加入编码格式
让我们加入一个缓冲区提高字符的读写速度,首先看看JavaAPI的文档介绍💁♀️💁♀️💁♀️点击打开JavaAPI文档
在构造方法中我们可以看到,Reader类对象,表示可以和其他的字符流进行连接
运行结果如下:
其实字符流也提供了自己的读写类
对象序列化
【场景】
比如在进行聊天,那么我们在聊天的过程当中是把一个人的信息发送给另外一个人。那么这个信息的发送过程是:我们的聊天内容不仅仅是内容信息,实际上还包括发送方的ip,接收方的ip,端口号,以及呢聊天内容或者是昵称等等等等,那么这些信息如果是一条一条的发送过去是没有办法接收的,所以呢我们要把这些信息呢封装到一个类当中。然后把这个类的对象发送出去,对方在接收到你的信息以后呢就会通过解析知道啊我这个信息呢是什么内容,然后来自谁,他的昵称是什么。接收方就都知道了。
那么在这个场景当中,如何去发送对象的内容以及呢如何接收解析对象的内容,就是对象的序列化来解决。
【步骤】
- 创建一个类。继承Serializable接口,当实现了这个接口,才可以开始读写
- 创建对象,将对象写入文件
- 从文件读取信息
所涉及的类:
- 对象输入流类:ObjectInputStream
- 对象输出流类:ObjectOutputStream
让我们继续打开JavaAPI的文档,看一看对象的输入输出流所有的方法:💁♀️💁♀️💁♀️点击打开JavaAPI
看看案例:
定义一个商品Goods,属性有id,name,price,然后将Goods类的对象写入文件中,然后读取出来。
1.首先编写Goods类,完成属性,构造方法,set&get方法,以及toString显示商品信息的方法。
2.根据上面的步骤我们需要将我们的Goods类继承Serializable接口,当实现了这个接口,才可以进行下一步的读写操作
3.创建测试类GoodsTest,并且创造gs1对象
4.格式化文件 “kilig.TXT”,并将数据写入文件 “kilig.TXT”中
5.分析可以知道,我们先写后读,并且这是个字节流的对象。
此时就将对象存储到了文件中,实现了持久化的存储(存到文件中,而非运行结束后没有了数据)
6.进行读操作
可以看到,我们可以成功读取导数据。并且文本也存入了这个数据。