当前位置:网站首页 > Java基础 > 正文

java的io流基础



序列化对象

将堆内存中的东西(对象)写到硬盘中

java的io流基础

写入对象的代码如下:

编译后运行出现以下结果:

对象写入硬盘发生的错误

通过查找API了解该异常:

异常的详细解释

Person类需要具有序列化接口。

存储着Person对象的txt文件

那么既然能将对象写进文件中,也就能将文件中得对象读出来,

调用读取文件的方法获取对象,得到如下结果:

读取文件获取对象

两个对象的UID不一样

结果显示,从文件获取到的对象和调用Person获取到的对象的UID不一样,

当把private去掉之后,运行程序,就又可以获取到对象了,

这说明UID序列号是根据类的成员算出来的。

如果想要加上私有之后,还可以从之前的文件对象中获取到修改后的对象。

就是不让java给我算UID。我用自己定义UID,就可以了

这样不管参数中有没有加私有修饰符都不会影响从文件中读取对象。

注意,静态是不能被序列化的。
因为静态成员是在方法区中存在的,是不能被序列化的
只有堆中的对象才可以被序列化。

这是存入一个对象,如果存入多个对象的话也不用着急,因为readObject方法第一次读数据返回的是第一个对象,第二次读数据返回的就是第二个对象。

管道流

输入输出可以直接进行连接,通过结合线程使用。
PipedInputStream();
PipedOutputStream();
这里写图片描述

由图上信息可知,此处应该有多线程,

是什么原理呢?

运行结果:
管道流实例

随机访问文件RandomAccessFile

但是他是IO包中的成员,因为他具备读取和写入的功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作
可以通过getFilePointer获取指针位置。
同时可以通过seek方法改变指针的位置。

其实完成读写的原理就是内部封装了字节输入流和输出流

如果模式为只读r,不会创建文件,会去读取一个已经存在的文件,如果该文件不存在,则会出现异常
如果模式为rw
而且该对象的构造函数要操作的文件不存在,会自动创建,如果存在不会覆盖。

既然随机访问文件对象可以对文件进行读取和写入的,我们就先演示一下写文件的方法。

运行结果:

写入数据实例

结果显示:可以存进去,但是写进去的97成了字符a。这是因为我写入的是字节数据,在写进记事本的时候,会按照传进来的字节数据按照查表的方式获取到相应的字符,然后写入到记事本中。

这里需要注意的是,自由访问文件流中的write方法和流中的write方法一样,也是只写八位,也就是一个字节(即使传进来的数据是4个字节或者更多),
那么就出现了局限性,要是我写入的数据长度大于一个字节的最大表示数(255)要写入的数据不仅仅是8位的数据
而write方法只能操作8位。
这样会造成数据的损失。
所以我们可以在写数据的时候用的是writeInt方法,可以一次写入4个字节的数据。

那么接下来演示读取文件中数据的方法。

重新用writInt方法写了一次人的年龄,所以人的年龄是4个字节了。

运行结果为:

自有访问文件对象获取文件中的信息

如果要同时获取到张三李四的信息,可以直接在以上代码中将readDemo方法中的read方法和readInt方法再多写一次。

这里我想要直接获取到李四的信息,应该怎么做呢?

使用到了随机访问对象的设置指针的方法,seek方法

演示:

运行结果:

直接跳转角标获取元素的值

运行结果:

使用向后跳指针的方法来获取指定位置上的元素

使用seek方法除了可以获取指定位置的元素,还可以在指定位置添加元素。包括跳过中间位置。

演示:

运行结果:

跨过区域写数据

运行结果:

这里写图片描述

发现,在第一个位置存储的元素由张三变成了周七,seek可以实现对文件中数据的修改。

这样的原理可以用到下载视频上来,一个线程下载020M,同时另一个线程下载2040M,,,,这样下载下来的视频是有连接的,可以提高效率。

DataInputStream与DataOutputStream

可以用来操作基本数据类型的数据的流对象

直接演示方法:

运行结果:

基本数据对象的写入

出现乱码,正常,因为是在记事本中,会有一个查编码表的过程。

接下来读取刚刚写入的数据

运行结果为

使用基本数据类型操作数据

可以获取到数据。

writeUTF方法的演示:

运行结果:
修改后UTF编码的写入数据文件大小为8字节

使用DataOutputStream的writeUTF方法写入的数据,只有同类的readUTF方法获取到数据。

读取数据

运行结果:
使用readUTF读取数据

获取成功

当需要使用GBK或者UTF-8编码写入数据的时候用到了转换流:

写入成功:文件大小为4字节:

使用GBK编码表

使用utf-8呢?

运行结果:写入成功,文件大小为6字节
utf-8编码写入数据

当用readUTF(使用修改版的UTF_8编码表)方法读取用utf_8编码的文件时,会出现这样的效果:

抛出异常

该异常的具体信息是:

这里写图片描述

所以说用writeUTF写入的数据,只有用readUTF才能读取的出来。

那么utf-8编码表和readUTF码表中用到的utf_8修改版有什么不一样的呢?

utf-8修改版和没修改版的区别

操作字节数组

ByteArrayInputStream与ByteArrayOutputStream

用于操作字节数组的流对象

演示一下字节数组的操作方法。

运行结果为:

使用字节数组流进行读写操作

在流操作规律讲解时:

有时候要将一个文件中的内容放到一个数组里先存起来,这个时候,源就是硬盘,目的就是内存。就用到了字节数组对象。

其实字节数组流对象就是用流的思想来操作数组的,
设定自己的源和目的,判断读取的值是否为-1.也就是相当于判断有没有到了数组的末尾。
一次一次的读取操作也就是数组中的遍历元素。
写入操作也就是数组中的设置某一个位置上的元素的值。

只不过是把读取的方法封装起来了。

writeTo 方法

writeTo(OutputStream os):将数组中的数据写入到硬盘中取。

里边也存在源,就是要接收一个字符数组,
目的就是新建一个空的缓冲区。
里边的方法有toCharArray,append,write,toString

同时想操作数组的话还有StringReader和StringWriter

都是同样的道理源接收一个字符串作为源,目的是一个空的字符串。

字符编码

  • 字符流的出现是为了方便操作字符
  • 更重要的是加入了编码转换
  • 通过子类转换流来完成
    • InputStreamReader
    • OutputStreamWriter
  • 在两个对象进行构造的时候可以加入字符集

编码表的由来

  • 计算机只能识别二进制数据,早期由来是电信号
  • 为了方便应用计算机,让它可以识别各个国家的文字
  • 就将各个国家的文字用数字来表示,并一一对应。形成一张表。
  • 这就是编码表

常见的编码表

  • ASCII美国标准信息交换码
    • 用一个字节的7为就可以表示
  • ISO8859-1:拉丁码表,欧洲码表
    • 用一个字节 的8为表示
  • GB2312:中国的中文编码表
  • GBK:中国的中文编码表升级,融合了更多的中文文字符号
  • Unicode:国际标准码,融合了多种文字
    • 所有文字都用两个字节来表示,Java语言使用的就是Unicode
  • UTF-8:最多用三个字节来表示一个字符。

那么现在,问题来了,GBK可以表示汉字的编码,UTF-8也可以表示汉字编码,但是相同的字在这两个编码表中不一样,这样会出什么问题呢?

用一段代码来演示:

接下来使用各自的读取方式去读文件

运行结果为:

使用各自的编码表读取到的数据

那如果对文件进行读取的时候指定的编码表出现了错误的时候呢?

例如,使用GBK码表去读取UTF-8编码的文件,用UTF-8码表去读取GBK编码的文件

运行结果:

交替编码表读取出来的数据

为什么会出来这种情况呢?

我们知道,GBK编码表中一个汉字占用的两个字节
而UTF-8码表中,一个汉字占用了三个字节
在具体读取中,过程是这样的。
使用编码表读取文件过程详解

字符编码

以上方法都是按照默认的编码表(GBK)来进行转换的。

先看看两种编码形式返回来的字节数组是什么样的:

运行结果:
这里写图片描述

运行结果:
这里写图片描述

发现默认编码和指定的GBK编码出来的字节数组是一样的,也就证实了,默认的编码方式是GBK的。

接下来用utf-8的方式进行编码,看看出来的字节数组是什么样的?

运行结果:

这里写图片描述

发现,使用utf-8编码之后返回一共有6个字节,也就是说一个汉字由三个字节组成。

以上是对汉字进行编码的过程,接下来演示解码GBK的过程。

运行结果:

这里写图片描述

接下来演示解码UTF-8文件

运行结果:

这里写图片描述

那么如果使用不同的编码表读取数据呢?

运行结果:
这里写图片描述

如果在写数据的时候使用了错误的编码形式,比如说ISO8859-1(欧洲语言码表),就没有办法了,因为发送了错误的编码形式,对方不知道这到底是什么样的编码形式。

运行结果:
这里写图片描述

这是怎么样的一个过程呢?
当“你好”被解码的时候,解码成为-60,-29,-70,-61这是没有错的,
但是当解析的时候,用这些字节数组去查表的时候查的是ISO8859-1,
查一个,找不到,返回来一个?,后边的都一样找不到,所以会返回来4个?,

想要能把数据解析出来,可以这样做:

运行结果为:

这里写图片描述

过程是这样的:

乱码转换流程图解

这种方法适用在tomcat服务器的使用中,因为服务器中的默认编码形式为ISO8859-1,所以这就涉及到一个编码转换的问题了。

同样的道理,我们可不可以用GBK编码,用UTF-8解码,得到的乱码再进行编码,解码得到原来的数据呢?

运行结果为:

这里写图片描述

在上边运行结果中我们发现,第一次编码没有问题,得到了GBK对应的字节数组,
之后使用Utf-8码表去解析的时候出错了,得到了乱码字符,也没有问题
到对乱码在进行编码的时候,出现了和源码不一样的字节数组,
问题就出在了这里,

从GBK的编码获得的字节数组查Utf-8码表的时候找不到,返回了“?”
这个“?”在Utf-8中是存在了未知字符区域,
当再进行编码的时候就会给?重新编一个值,编成了一个三个字节的?

为什么Utf-8会重新编值呢?而ISO8859-1就不会重新编值呢?

因为Utf-8和GBK都可以识别中文,而ISO8859-1不识别中文,不会对中文进行编码,读到什么,解出来的还是什么。

联通的示例

我在记事本上写下一个联通,然后保存再打开,结果成了这个样子:

这里写图片描述

这里写图片描述

默认的GBK编码为什么会变成Utf-8编码的呢?

这个时候就要了解一下Utf-8中的编码格式了:

Utf-8中的编码形式如下图:

这里写图片描述

解读一下就是:

当读到的字节的首位是0的时候,就只读一个字节,回去查表
当读到的字节的首位是110,第二个字节的首位是10的时候,就读取两个字节,回去查表
当读到的字节的首位是1110,第二个字节的首位是10,第三个字节的首位是10的时候,就读取三个字节,回去查表

而联通这两个字,在内存中的二进制码是这样的:

四个字节的二进制码如下:
这里写图片描述

他是怎么样从GBK变啊变成Utf-8编码的呢?
当记事本将联通写入到文件中的时候,用GBK码表进行编码,编成如上图所示的二进制表现形式,
然后再打开记事本软件的时候,这就是一个解析编码的过程,
记事本一看,第一个字节首位是110,符合Utf-8编码的形式,继续往下读,发现是10,完全符合Utf-8编码形式,这就转变成Utf-8编码形式了。

这么多字符里边,只联通字符是这样的,其他字符并不这样,

解决方式:就是写联通的时候,在联通之前加上一个汉字,

这里写图片描述

练习:
有五个学生,每个学生有三门课程的成绩
从键盘输入以上数据,(包括姓名,三门课成绩)
输入的格式如:zhagnsanm30,40,32计算出总成绩
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件“stud.txt”中

运行结果为:

版权声明


相关文章:

  • 花溪区java基础知识2024-10-18 21:42:04
  • java基础编程游戏2024-10-18 21:42:04
  • 零基础测试和java哪个好2024-10-18 21:42:04
  • 长沙java入门零基础2024-10-18 21:42:04
  • java基础语法代码题2024-10-18 21:42:04
  • javascript是以JAVA为基础吗2024-10-18 21:42:04
  • 递归java基础程序2024-10-18 21:42:04
  • w3c java 基础2024-10-18 21:42:04
  • java程序开发基础答案解析2024-10-18 21:42:04
  • 零基础学Java(第3版)2024-10-18 21:42:04