IO功能类

来源:互联网 发布:淘宝网警用手铐脚铐 编辑:程序博客网 时间:2024/06/02 07:50


IO 包的其他常用对象,常用频率不会特别高,但是会用到;

 

一、对象的序列化;不是较常用的流对象;

操作对象的流;

ObjectInputStream  和ObjectOutputStream   将堆内存中的对象存在硬盘上;

被操作的对象需要实现Serializable(标记接口);

 

可以直接操作对象的流,可以通过流的方式将堆内存中的某个对象存放在硬盘的文件上,即使程序结束后,垃圾被清除了,以后依然只需要把存储对象文件读一遍就可以用;

找一个介质能长期保存对象数据,这叫对象的持久化存储,序列化,对象的可串行性;(找一个介质即可);

 

该流的功能是把流和对象进行相结合;

这个类可以操作对象的属性  可以写入基本数据类型

 

构造函数ObjectOutputStream(OutputStream out)

ObjectOutputStream构造方法中必须要有一个目的;

writeInt()等方法可以操作基本数据类型;而write()方法中只能写一个数据的最后八位;

 

该方法操作的数据肯定是字节码数据了;

 

这些功能类字节流都是操作的更细节数据的操作,如对象,数据,基本数据类型等等;这个了解一下,后面有专门操作基本数据类型的;

 

Javac *.java;这种编译方法可以编译包内所有的Java文件;

 

实现了Serializable接口才能启用其序列化的功能;

没有方法的接口通常称为标记接口,也就是相当于给这个类盖个章;

其实是给该类标了一个uid号,以供Javac来识别的;文件改变后,uid就会改变;

uid都是根据类中的成员来定义标示的;每个成员都有一个数字对应;

可以理解为数字签名;

 

对象的持久化,可以把对象文件和类文件一起打包哪里都可以用,不需要再传值就可以直接使用;不仅提供对象,还提供了对象的具体数值;不需要再new对象了;

 

读入对象是必须要依赖对应的类文件的;不同uid的类是不能使用到不同uid的对象文件的;

 

自定义类的uid号;  uid就是给类定义一个固定标示,为了序列化方便,新的类还可以操作曾经序列化的对象;

static final long serialVersionUID = 42L;

uid号是对象与对应的类进行匹配的号码;独缺序列化的文件是通过该uid号找出对应的同号的类进行读取的;

 

静态static的成员是不能被序列化的;

静态是方法区的,而对象是堆里面的,该流只能序列化堆里面的,不能序列化方法区的;

关键字 transient  修饰成员属性,可以让该属性不会被序列化;

保证其值在堆内存中存在,而不在文本文件中存在;

 

一般序列化文件存储格式用对象Object作为后缀,类名作为前缀;

 

void writeObject(Object obj)      throw ClassNotFoundException IOException,

Object readObject()

 

ObjectInputStream  和ObjectOutputStream 必须是同时使用,只有对应的方法才能读写;

如果存有多个对象,readObject读一次就返回一个对象,读不同类的对象也行;

 

 

二、管道流:

PipedInputStream 和PipedOutputStream  输入输出可以直接进行连接,通过结合线程使用;

 

一般的读入流和写出流之间没有练习,只能通过中转工具进行连接才行,如数组等;

但是这个管道流可以把读写流连在一起,可以把流对接到一起;但会存在不同步的安全问题;

 

通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。

 

注意的是read()方法阻塞式方法,如果读不到数据,线程就会在那里等待;在键盘录入有所体现;可以理解为read方法是其他线程操作的;即没有数据就等待其他线程先写入;写完了再把read方法唤醒;

 

这个对象是设计了多线程技术的IO流对象;

 

把管道流的两个流连接到一起有两种方法,

1、构造函数直接传入;

2、空构造,然后用方法connect()连接;

 

这组对象应该是内部有一个类似数组的在进行中转;

 

三、IO包中非常特殊的对象;随机访问文件;

RandomAccessFile  自成一类,不继承任何流的类;有读和写的方法;

对象内部封装了数组和指针;

此类的实例支持对随机访问文件的读取和写入。

这个类比较重要,要掌握;

 

该类不算IO体系中的子类,而是直接继承自Object;但是它是IO包中的成员,因为它具有读和写的功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置;

底层自动封装了大型的一个byte数组;

 

 

能操作数据的对象必然是流对象,其实该对象能完成读写的原理就是内部封装了字节输入流和输出流对象;

 

通过构造函数可以看出,该类对象只能操作硬盘上的文件,什么键盘或内存输入等都不行;

而是操作文件还要有模式;只有四种模式,rrw等;

 

这个类有些特殊,可以稍掌握一下;

 

OutputStream write方法的特点就是只写最后八位;不是数字类型转型,而是只写后面的八位;

 

这个流对象可以操作基本数据类型;

 

writeInt()写入的是是四个字节;

注意,这个流对象也是自动覆盖已有文件,在实例文件流对象的时候;

注意的是读和写的模式是不一样的;一般地,一个是r一个是rw。可以用来屏蔽写的动作;

writeInt()   readInt()  读写基本数据类型;

seek() 想走到哪里就走到哪里了;

skipBytes(int)  不能往回跳,只能往后跳;跳完后就走不回去了;这个方法用的不是很多,了解一下;

 

流在操作数据时候都是按顺序来读写的,

这个对象还可以随机的写数据;在指定的位置写入数据;

 

这个类还可以修改覆盖已有文件中的数据,通过给定位置写入就把之前的数据给覆盖了;

 

这个类不覆盖文件,如果指定位置了就从指定位置写,没指定,就从第一个位置修改;

 

该对象的构造函数如果要操作的对象不存在,会自动创建,如果存在,则不会覆盖;

 

只读模式r不会创建文件,只读取已有文件,如果该文件不存在,则会报异常;

如果模式为rw,文件不存在就创建,如果存在,但不会覆盖;

 

用途:

这个类对象可以实现数据的分段写入;用多个线程负责写入;实现多线程下载;

也就是下载软件的原理;一般流就搞不定,因为不同的线程放的位置不一样;只有这个对象才可以指定位置写入;通过seek方法,但是要注意的是,数据要有规律才可以来进行指定写入的位置;

 

用空补位;  可以把位数放大一点,用空格补位,这样就容易让数据比较有规律;

 

 

疑问:不是说rw可以读么,试一下;

 

四、流对象操作基本数据类型,

DataInputStream 和  DataOutputStream 这个流对象用的频率还是比较高的;

流和数据相结合的流对象,实例化时传一个流进来,完成把流和数据相结合的功能;

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

 

里面有很多的方法在Object的流对象中也有,不过用操作基本数据类型就用这个,因为那个特点主要在于操作对象;

 

 

注意,只有File的流对象才可以操作文本;

布尔型的数据占位一个字节;

 

注意用这个数据读写流的对象里面的方法时,要读写的顺序是一致的,不然就会读错;

double readDouble()

int readInt()

double readDouble() 

 

void writeInt(int v)  void writeDouble(double v) 

void writeUTF(Stringstr)  String readUTF()  用这种方式读写的字符串只能用对应的方式读写;用转换流读不出来;

 

转换流指定码表即可用常规的方法来指定;”utf-8”

 

DataOutputStream 看名字就知道他是把功能和流相结合,这样就需要一个流对象,这样可以用来记忆该流对象的用法;

 

凡是操作基本数据类型就用以上这对流;

 

五、用来操作字节数组的流对象;

 

该组流对象用来操作内存当中的数据;

 

ByteArrayInputStream  它不会调用底层资源,不会产生任何的IO异常;close关闭无效;

ByteArrayOutputStream 也不会调用底层资源,不用刷新,他里面封装一个可变长度的字节数组,,可以用toByteArray 和toString方法直接输出;

关闭该流后,这两个对象还可以操作,因为关闭失效;

 

ByteArrayInputStream :在构造的时候,需要接收数据源,而且数据源是一个字节数组;

ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地;

 

因为这两个对象都操作的是数组,并没有使用系统资源,所以不用进行close关闭;而且关不关闭都可以继续使用;

 

这对流对象的源和目的都是一个字节数组;而且也不需要自定义数组,因为其内部都封装数组了;

 

注意的是,这对流对象的源和目的都是内存;即一关闭就没有了;

 

源设备:

键盘(System.in)硬盘(fileStream)内存(arrayStream)

目的设备:

键盘(System.out)硬盘(fileStream)内存(arrayStream)

 

这对流对象是用流的读写思想来操作数组,因为数组的操作无非就是设置和获取的功能,正如同流的读写方法;

遍历数组——读;

设置——写;

 

writerTo()方法;写到某一个目的当中去;参数为字节流;

 

还有几对一样的数组流对象

CharArrayReader  CharArrayWriter

StringReader  StringWriter

以上三个是以内存为源和目的的基本操作;

 

 

以上五种功能流对象,我们自己也可以进行封装,但是Java封装之后我们使用起来就更加方便了,这五种功能流对象的应用是非常的实用;

 

(1)对象的序列化,非常实用,可以不重新实例对象,直接读取就可以进行各种运算了;

(2)管道流,即写即读,不需要任何的中转站,典型的聊天模式的流;

(3)随机读写文件的流:用于指定位置的数据的增删改查的操作中很实用;

(4)操作基本数据类型的流,那比直接用文件流就方便多了,如需要做一个判断,直接用boolean的方法即可,用基本的读写方法还要各种转换;

(5)数组流对象,把其他设备上的数据先转存在内存中以备操作,也很实用很常用;

 

 

 

六、字符编码的问题;

 

1、了解:

加入编码表的四个流对象有

InPutStream OutputStream   PrintStream  PrintWriter

后两者只能打印,不能读取;

 

1010的排列组合对应的文字就形成了编码表;

ASCII 美国标准信息交换码;用一个字节的7位;

ISO8859-1拉丁码表,欧洲码表;用一个字节的8位表示;

GB2312 中国的中文编码表;

用两个字节表示;避免重复,所以字节的高位都是1;中文码表与ASCII是兼容的;

GBK   中国码表正在扩容中。。.

Unicode码表,国际标准码,融合了多种文字;  16个二进制位65535个数;

所有文字都用两个字节来表示,Java语言使用的就是Unicode  (指char类型的)

UTF-8,最多用三个字节来表示一个字符,最小一个字节;Unicode的升级,字节大小自动变化,不是固定的两个字节表示一个文字;该编码在每个字节中都有一个独特的标示头;

 

一般的,编码表前面都是加1的组合,所以他们打印出来的字节码就是负数;

 

如果使用UTF-8和GBK两个码表都使用就可能出现乱码;

 

 

在编码中用到的流对象几乎都是InPutStream  OutputStream

 

2、编码转码

 

把内容写到硬盘上都是变成字节码保存的;即计算机上的数据都是以二进制位的字节形式存在的;

中文编码表的过程中其实走过一个先转成Unicode编码的过程,现转为字符,然后再转中文编码;utf-8中找单个字符的都要这样的;

 

编码:字符串编程字节数组;就是写进去;               

解码:字节数组编程字符串;就是读出来;    

 

String——》byte[] str.getBytes(charsetName)

byte[]——》String  newString(byte[] , charsetName)

 

注意,传入指定码表的时候会抛出不支持码表异常;

 

能识别的码表就两个 GBK 和UTF-8,如果码表编错了,就没有必要去解了,因为是肯定解不出来的;

但是如果解码解错了,还是可以再编和解是可以还原的;

 

常会见到:

服务端,里面有Tomcat服务器,里面也有他自己的解码方式;他里面只有iso8859-1,因为他是可以识别老美和欧洲的,但是不识别中文的;

又因为不同的网站的编码都不一样,所以都要经常的编码再加码;

因此只能要重新解码等;服务器中的get 方法pop方法

 

要想不出乱码,而不确定用哪个码进行解码,都是先用的iso8859-1解码,然后重新编码和解码;这是最安全的;

 

UTF-8码表中有一个未知字符区域,要注意;这种情况,只要解码错误就编不回来了;

因为这两个表都识别中文,所以就不能再编回来;

因为utf-8中有一个未知字符区域,如果在编码当中找不到,就到未知字符区域去找了;而不是按照中文几个字节一起去找的;他是分开去找的;

如果不识别中文的码表就不会有问题了;

为什么会这样呢?先记住吧。硬盘上的字节码咋玩的还不清楚;

 

3、特殊的文字

GBK码表和UTF-8码表的区别;联通

UTF-8码表需要用几个字节表示时,他前面的标示符号是不同的,见DataOutputStream中writeUTF方法的utf-8的API解释;即UTF-8是有自己的字节的头部特征码的;Utf-8是按照他自己的标示头来告诉是一次要读几个字节;

联通是GBK和UTF-8共同的地方,其他的没有;

 

在打印的时候,要把int的负数只取后八位,就直接与上255即可;

取哪几位就与对应的数字即可;

 

4、练习:有五个学生,每个学生有3门课的成绩,

从键盘输入以上数据(包括姓名,三门课成绩),

输入的格式:如:zhagnsan,30,40,60计算出总成绩,

并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。

 

思想:在键盘上先输入数据,然后对录入的数据进行分割操作并分别把他们赋值给对应的属性即可;基本上操作键盘录入数据的工具类都要这么来进行操作定义;

 

1,描述学生对象。

2,定义一个可操作学生对象的工具类。

 

 

 

公用的工具就都用静态的方法来做,因为没有特有数据;

读键盘时因为停不下来,那么就要自定义结束方式;

 

0 0
原创粉丝点击