七、对象流
把一个对象持久化到物理介质,或者把对象传输到另一个独立的程序中,可以使用
JAVA 的对象流。
序列化:把对象保存到文件中,可以称为对象序列化到文件
反序列化:把对象从文件中读取到程序中,重新组装成对象,可以称为对象的反序
列化。
什么情况下,对象需要序列化:
(1)对象要永久保存
(2)对象要进行传输
在JAVA中如何实现对象序列化:
(1)把要进行序列化的类,实现Serializable标记接口
此接口中无任何定义,目的是告诉虚拟机此类的对象需要被序列化
(2)构造一个字节输入输出流
(3)通过字节输入输出流构造对象流(ObjectOutputStream/ObjectInputStream)
(4)调用writeObject()/readObject()写入或读取对象
(5)关闭流
注意:对象的反序列化操作,只能读取一次,同一个文件要写入多个对象,使用对象数组封装后写入,同一个文件不要出现多个不同类型的对象。
对象序列化过程:
把类名、属性的类型、属性的值以二进制方式写入文件保存,在需要的时候读取该文件
,再根据保存的信息重新创建一个对象到内存中,称为还原。
序列化不仅仅是把有用的属性值保存下来,还要保存类型和类信息。
在实现对象保存操作时,必须使用 Serializable方式的序列化
在实现对象传输操作时,如果使用Serializable方式的序列化,不是最优的方式,因为
这种方式会传输除了必须要的数据,还额外传输了类信息和属性信息,从这个点考虑
就是浪费内存。牺牲性能,降低传输效率。
八、字节数组流
ByteArrayInputStream: 字节数组输入流
ByteArrayOutputStream: 字节数组输出流
字节数组流内部维护一个缓冲区(字节数组),不关联文件,无需关闭,所有的操作
都基于内存。内部的缓冲区是一个动态数组(算法是原来长度*2)
字节数组流的本质就是使用 IO 流的操作方式来管理动态数组。
private static void byteArrayStream(byte[] bytes){
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
int count = 0;
int b = -1;
while((b=bais.read())!=-1){
if(b>=97 && b<=123){
count++;
}
}
System.out.println(count);
}
public static void main(String[] args) {
//统计一个文本中的字母个数
String info = "12832fdsjlfje3830fefefez";
byteArrayStream(info.getBytes());
}
九、数据流
DataInputStream/DataOutputStream: 数据输入输出流
以 JAVA基本数据类型形式,与底层机器无关,这样的好处是我们可以忽略底层操作系统
,按存储数据的大小来写入或读取数据。
OutputStream out = new FileOutputStream("c:\\test\\my.data");
DataOutputStream dos = new DataOutputStream(out);
dos.writeInt(10); //写入一个整数,按4个字节
dos.writeByte(1); //写入一个数,按1字节
//写入字符串时,会额外增加2个字节,用于表示字符串,
//字符按不定长计算(1-6)
dos.writeUTF("moliying"); //写入一个字符串
读取时的顺序要和写入的顺序一致。
InputStream out = new FileInputStream("c:\\test\\my.data");
DataInputStream dis = new DataInputStream(out);
dis.readInt(); //按4个字节读一个整数
dis.readByte(); //按1字节读一个数
dis.readUTF(); //按 UTF 编码读取字符串
十、字符串流
StringReader
StringWriter
把一个字符串传入一个字符串流,好处是用流的形式来处理字符
内部操作都在内存中,无需关闭,不会有 IOException
例如,
XML/JSON 是数据传输的格式(最常见),用字符串来表示
xml:
<person id="1">
<name>黄笙</name>
<age>30</age>
<sex>未知</sex>
</person>
JSON:
{'id':1,'name':'黄笙','age':30,'sex':'未知'}
//当连接服务器后,返回的数据以 JSON 或 XML 字符串的格式,当我们接到这样的数据
//需要使用对应的技术去解析成JAVA 中的对象
private void parseJSON(String json){
StringReader sr = new StringReader(json);
//把sr作为参数传入到对应数据解析器中
}
字符串流与字节数组流类似,都是内存操作流。
十一、RandomAccessFile
一个可以随机读写操作的工具类
RandomAccessFile ra = new RandomAccessFile(file,"rw");
//创建一个可读可写的RandomAccessFile对象
模式:
r:读模式
rw :读写模式
可以通过getFilePointer() 返回此文件中的当前偏移量。
seek(long pos)
设置到此文件开头测量到的文件指针偏移量,
在该位置发生下一个读取或写入操作。
skipBytes(int n) 尝试跳过输入的 n 个字节以丢弃跳过的字节。
十二、装饰者设计模式
问题:为了给一个对象附加额外的功能,使用直接继承的方式,会带来大量的子类,
造成难以维护,使用装饰者设计模式,可以让这样的关系更加灵活,并利于扩展。
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,
Decorator模式相比生成子类更为灵活。
该模式以对客户端透明的方式扩展对象的功能。
适用环境:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。
一种情况是,可能有大量独立的扩展,
为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
装饰者设计模式的相关角色分配:
Component(被装饰对象基类)
定义对象的接口,可以给这些对象动态增加职责;
ConcreteComponent(具体被装饰对象)
定义具体的对象,Decorator可以给它增加额外的职责;
Decorator(装饰者抽象类)
维护指向Component实例的引用,定义与Component一致的接口;
ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象增加具体的
装饰者模式小结:
OO原则:动态地将责任附加到对象上。
想要扩展功能,装饰者提供有别于继承的另一种选择。
要点:
1、继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案。
2、在我们的设计中,应该允许行为可以被扩展,而不须修改现有的代码。
3、组合和委托可用于在运行时动态地加上新的行为。
4、除了继承,装饰者模式也可以让我们扩展行为。
5、装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
6、装饰者类反映出被装饰的组件类型(实际上,他们具有相同的类型,
都经过接口或继承实现)。
7、装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,
甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8、你可以有无数个装饰者包装一个组件。
9、装饰者一般对组建的客户是透明的,除非客户程序依赖于组件的具体类型。
十三、常见编码格式
1、iso-8859-1:单字节的编码格式,一般用于英文0-255之间
2、GBK/GB2312:中文国际编码,以双字节的编码格式,GBK 比 GB2312字符集更大
3、unicode:java的默认编码,以双字节的编码格式,不兼容iso-8859-1,单字节也
用双字节来表示。浪费存储空间。
4、UTF-8:以不定长(1-6)字节的编码格式,可以表示国际上的所有文字,更有利于存储空间的合理利用。
通常情况下,在程序造成乱码的:
(1)程序编码与本机编码不一致
(2)客户端程序与服务器程序的编码不一致
转换编码的方式:
比如把一个iso-8859-1编码的字符串转成 GBK 编码
String s = new String(data.getBytes("iso-8859-1"),"GBK");