`

JAVA IO 序列化与设计模式

    博客分类:
  • Java
阅读更多

原文出自【听云技术博客】http://blog.tingyun.com/web/article/detail/460

 

序列化

什么是序列化

序列化:保存对象的状态

反序列化:读取保存对象的状态

序列化和序列化是Java提供的一种保存恢复对象状态的机制

 

序列化有什么用

将数据保存到文件或数据库中时

将数据通过套接字在网络上传输时

通过 RPC RMI等传输对象时

 

如何序列化

实现Serializable接口

实现Externalizable接口

serialVersionUID的作用serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败

 

默认序列化机制

如果仅仅只是让某个类实现Serializable接口,而没有其它任何处理的话,则就是使用默认序列化机制。使用默认机制,在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,同样地,这些其它对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。

(01) 序列化对static和transient变量,是不会自动进行状态保存的。

transient的作用就是,用transient声明的变量,不会被自动序列化。

(02) 对于Socket, Thread类,不支持序列化。若实现序列化的接口中,有Thread成员;在对该类进行序列化操作时,运行会出错。

这主要是基于资源分配方面的原因。如果Socket,Thread类可以被序列化,但是被反序列化之后也无法对他们进行重新的资源分配。

示例:

package ioEx; 
import java.io.FileInputStream;   
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream;   
import java.io.ObjectOutputStream;   
import java.io.Serializable; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.Map.Entry;   
public class Serial { 
    private static final String TMP_FILE = "text.txt";
    public static void main(String[] args) {   
        testWrite();
        testRead();
    }
    private static void testWrite() {   
        try {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(TMP_FILE)); 
            AnObject box = new AnObject(1, 1, "1");
             out.writeObject(box); 
            out.writeBoolean(true); 
            out.writeByte((byte)65); 
            out.writeChar('a'); 
            out.writeInt(20160415); 
            out.writeFloat(3.14F); 
            out.writeDouble(Math.PI); 
            HashMap<String,String> map = new HashMap<String,String>();
            map.put("a", "a");
            map.put("b", "b");
            map.put("c", "c");
            out.writeObject(map); 
            out.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
private static void testRead() {
        try {
                    ObjectInputStream in = new ObjectInputStream(new FileInputStream(TMP_FILE)); 
            AnObject box = (AnObject) in.readObject();
            System.out.println("testWrite box: " + box); 
            System.out.println("boolean:"+ in.readBoolean());
            System.out.println("byte:" + (in.readByte()&0xff)); 
            System.out.println("char:" + in.readChar());
            System.out.println("int:" + in.readInt());
            System.out.println("float:" + in.readFloat());
            System.out.println("double:" + in.readDouble());
            // 读取HashMap对象
            HashMap<String,String> map = (HashMap<String,String>) in.readObject();
            Iterator<Entry<String, String>> iter = map.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String,String> entry = (Entry<String, String>)iter.next();
                System.out.println(entry.getKey()+"--"+ entry.getValue());
            }
            in.close();
        } catch (Exception e) {
         e.printStackTrace();
        }
    }
}
class AnObject implements Serializable {

    private int obja;   
    private int objb; 
    private String objc;   
    private static int statica;   
    private transient int transienda; 
    
    //必须用static或transient修饰才可能序列化,否则运行报错
    private static transient Thread  thread = new Thread() {
        @Override
        public void run() {
            System.out.println("Serializable");
             }
    };

    public AnObject(int obja, int objb, String objc) {
        this.obja = obja;
        this.objb = objb;
        this.objc = objc;
        this.statica=obja;
        this.transienda=obja;
    }
    
    //如果要使transient序列化要重写writeObject,和readObject 方法
//    private void writeObject(ObjectOutputStream out) throws IOException{ 
//        out.defaultWriteObject();
//        out.writeInt(transienda); 
//    }
//
//    private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ 
//        in.defaultReadObject();
//        transienda = in.readInt(); 
//    }

    @Override
    public String toString() {
        return "obja:"+obja+","+ "objb:"+objb+","+ "objc:"+objc+","+ "statica:"+statica+","+ "transienda:"+transienda;
    }
}

JAVA IO的设计模式

JAVA IO框架主要使用的两种设计模式 装饰模式和适配器模式

装饰模式又名包装(Wrapper)模式

装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

  • 装饰模式通过创建一个包装对象,也就是装饰,来包裹真实的对象。

  • 装饰模式以对客户端透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。

  • 装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展。

  • 装饰模式把客户端的调用委派到被装饰类。装饰模式的关键在于这种扩展是完全透明的。 

装饰模式的角色

  • 抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。

  • 具体构件角色(Concrete Component):定义将要接收附加责任的类。

  • 装饰角色(Decorator):持有一个构件(Component)对象的引用,并定义一个与抽象构件接口一致的接口。

  • 具体装饰角色(Concrete Decorator):负责给构件对象“贴上”附加的责任。

装饰模式的特点

  • 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。

  • 装饰对象包含一个真实对象的引用(reference)。

  • 装饰对象接收所有来自客户端的请求,它把这些请求转发给真实的对象。

  • 装饰对象可以在转发这些请求之前或之后附加一些功能。

这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。

示例:

 

package Test;

public class Wrapper {
    public static void main(String[] args) {
        Component c = new RealizeComponent();
        Component c1 = new RealizeDecorator1(c);
        c1.say();
        System.out.println("--");
        Component c2 = new RealizeDecorator2(c1);
        c2.say();
    }
}
interface Component{
    public void say();
}
class RealizeComponent implements Component{
    @Override
    public void say() {
        System.out.println("A");
        }
}
class Decorator implements Component{
    private Component component;
    public Decorator(Component component) {
        this.component = component;
    }
    @Override
    public void say(){
        component.say();
    }
}
class RealizeDecorator1 extends Decorator{
    public RealizeDecorator1(Component component){
        super(component);
    }
    @Override
    public void say(){
        super.say();
        this.sayAnother();
    }
     private void sayAnother(){
        System.out.println("B");
    }
}
class RealizeDecorator2 extends Decorator{
    public RealizeDecorator2(Component component){
        super(component);
    }
    @Override
    public void say(){
        super.say();
        this.sayAnother();
    }
    private void sayAnother(){
        System.out.println("C");
    }
}

 装饰模式的优点

装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。

通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

装饰模式的缺点

由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

JAVA IO中的装饰模式

抽象构件(Component)角色:由InputStream扮演。这是一个抽象类,为各种子类型提供统一的接口。

具体构件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。它们实现了抽象构件角色所规定的接口。

抽象装饰(Decorator)角色:由FilterInputStream扮演。它实现了InputStream所规定的接口。

具体装饰(ConcreteDecorator)角色:由几个类扮演,分别是BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。

适配器模式

34.4.png 

上图是适配器模式的类图。Adapter 适配器设计模式中有 3 个重要角色:被适配者 Adaptee,适配器 Adapter 和目标对象 Target。其中两个现存的想要组合到一起的类分别是被适配者 Adaptee 和目标对象 Target 角色,按照类图所示,我们需要创建一个适配器 Adapter 将其组合在一起。

最简单的适配器示例:

package AdapterEx;

class Apple {
    public void getAColor(String str) {
        System.out.println("Apple color is: " + str);
    }
}
class Orange {
    public void getOColor(String str) {
        System.out.println("Orange color is: " + str);
    }
}
class AppleAdapter extends Apple {
    private Orange orange;
 
    public AppleAdapter(Orange orange) {
        this.orange = orange;
    }
 
    public void getAColor(String str) {
        orange.getOColor(str);
    }
    }
public class AdapterEx {
    public static void main(String[] args) {
        Apple apple = new Apple();
        apple.getAColor("green");
        Orange orange = new Orange();
        AppleAdapter aa = new AppleAdapter(orange);
        aa.getAColor("red");
    }
}

Java I/O 库大量使用了适配器模式,例如 ByteArrayInputStream 是一个适配器类,它继承了 InputStream 的接口,并且封装了一个 byte 数组。换言之,它将一个 byte 数组的接口适配成 InputStream 流处理器的接口。

我们知道 Java 语言支持四种类型:Java 接口,Java 类,Java 数组,原始类型(即 int,float 等)。前三种是引用类型,类和数组的实例是对象,原始类型的值不是对象。也即,Java 语言的数组是像所有的其他对象一样的对象,而不管数组中所存储的元素类型是什么。这样一来的话,ByteArrayInputStream 就符合适配器模式的描述,是一个对象形式的适配器类。FileInputStream 是一个适配器类。在 FileInputStream 继承了 InputStrem 类型,同时持有一个对 FileDiscriptor 的引用。这是将一个 FileDiscriptor 对象适配成 InputStrem 类型的对象形式的适配器模式。

同样地,在 OutputStream 类型中,所有的原始流处理器都是适配器类。ByteArrayOutputStream 继承了 OutputStream 类型,同时持有一个对 byte 数组的引用。它一个 byte 数组的接口适配成 OutputString 类型的接口,因此也是一个对象形式的适配器模式的应用。

FileOutputStream 继承了 OutputStream 类型,同时持有一个对 FileDiscriptor 对象的引用。这是一个将 FileDiscriptor 接口适配成 OutputStream 接口形式的对象型适配器模式。

Reader 类型的原始流处理器都是适配器模式的应用。StringReader 是一个适配器类,StringReader 类继承了 Reader 类型,持有一个对 String 对象的引用。它将 String 的接口适配成 Reader 类型的接口。

装饰模式和适配器模式的对比

(1)装饰模式和适配器模式,都是通过封装其他对象达到设计目的的。

(2)理想的装饰模式在对被装饰对象进行功能增强时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致;而适配器模式则不然,一般而言,适配器模式并不要求对源对象的功能进行增强,只是利用源对象的功能而已,但是会改变源对象的接口,以便和目标接口相符合。

(3)装饰模式有透明和半透明两种,区别就在于接口是否完全一致。关于装饰模式的重要的事实是,很难找到理想的装饰模式。一般而言,对一个对象进行功能增强的同时,都会导致加入新的行为,因此,装饰角色的接口比抽象构件角色的接口宽是很难避免的,这种现象存在于Java I/O库中多有的类型的链接流处理器中。一个装饰类提供的新的方法越多,它离纯装饰模式的距离就越远,离适配器模式的距离也就越近。

 

 

0
7
分享到:
评论

相关推荐

    Java IO 与 装饰模式

    Java IO 与 装饰模式 在Java编程中,输入输出(IO)处理是程序设计中的重要组成部分,用于读取、写入和处理数据。Java IO库提供了一整套类来支持各种类型的输入输出操作,包括文件、网络、内存等。而装饰模式是一种...

    Java IO处理类的汇总

    过滤流(Filter Stream)是Java IO的一个重要设计模式,它在原有流的基础上增加额外功能,如数据转换、压缩、加密等。例如,DataInputStream和DataOutputStream可以处理基本数据类型,GZIPOutputStream和...

    Java_IO完全总结

    ### Java IO系统全面解析 #### 一、Java IO系统概述 ...无论是文件操作、网络通信还是对象序列化,Java IO系统都能提供合适的方法和技术支持。理解和掌握Java IO系统对于任何Java开发者来说都是非常重要的。

    Java SE编程入门教程 java序列化(共14页).pptx

    Java SE编程入门教程 java设计模式(共17页).pptx Java SE编程入门教程 java数组(共33页).pptx Java SE编程入门教程 java网络编程(共29页).pptx Java SE编程入门教程 java线程(共61页).pptx Java SE编程入门...

    java全栈工程师-java io

    Java IO系统设计遵循了一些常见的设计模式,比如: #### 1. 工厂模式 - `InputStream`和`OutputStream`及其子类分别通过工厂方法创建,如`new FileInputStream()`和`new FileOutputStream()`。 #### 2. 装饰器模式...

    JAVA设计模式

    在Java中,可以使用枚举、双重检查锁定或静态内部类来实现单例,以保证线程安全和防止反序列化创建新实例。 2. **工厂模式(Factory)** 工厂模式提供了一个创建对象的接口,但让子类决定实例化哪一个类。它封装了...

    《Java设计模式》课后习题参考答案-刘伟(20180723).pdf

    - javax.xml.bind.annotation.adapters.XmlAdapter#marshal()和#unmarshal(),分别用于进行数据的序列化和反序列化。 以上是根据给定文件内容提取出的设计模式相关知识点。此外,为了确保参考资料的权威性,建议...

    java对象实现序列化

    8. **序列化代理(Serialization Proxy Pattern)**:对于复杂的序列化需求,可以考虑使用Java的序列化代理模式,这是一种设计模式,可以在序列化时生成一个简化的代理对象,以减少序列化数据的复杂性。 在实际开发...

    Java的23种设计模式百度云下载链接.rar

    Java中的`Serializable`接口可以实现对象的序列化,实现数据备份和恢复。 19. **状态模式**:允许对象在其内部状态改变时改变它的行为,对象看起来好像修改了它的类。Java中Socket的连接状态管理就是状态模式的应用...

    java.io包详细解说

    ObjectInputStream和ObjectOutputStream则支持序列化和反序列化对象。除此之外,还有FilterInputStream和FilterOutputStream等过滤流,它们可以被用来组合多个装饰器,实现更复杂的流处理逻辑。 此外,Java NIO...

    Java设计模式入门闲谈

    ### Java设计模式入门详解 #### 一、设计模式概述 设计模式是软件工程领域中一种通用的解决方案,它描述了在特定情况下解决常见问题的方法。《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements ...

    java io流PPT课件.pptx

    Java的I/O设计遵循了一种称为“装饰者模式”的组织模式,允许在不改变原有类的基础上增加新的功能。 在进行I/O操作时,一般需要以下步骤: 1. 创建一个与特定介质(如文件、网络连接等)相关的输入/输出对象。 2. ...

    java的设计模式 《设计模式-java语言中的应用.pdf》

    Java中的序列化机制可以实现备忘录模式。 以上就是Java设计模式的一些基本概念和应用,理解并熟练掌握这些模式对于编写高质量、易于维护的Java代码至关重要。通过阅读《设计模式——Java语言中的应用》这本书,你...

    《JAVA_IO流学习总结》

    Java IO流的设计采用了装饰者模式,形成了丰富的层次结构。通过组合不同的装饰流,可以实现各种复杂的数据处理需求,如转换编码、压缩、加密等。 三、管道流和缓冲流 - PipedInputStream和PipedOutputStream允许在...

    JAVA设计模式在JDK中的应用

    ### JAVA设计模式在JDK中的应用 #### 一、引言 在软件开发过程中,设计模式作为一套被广泛接受的解决方案,能够帮助开发者解决常见的设计问题。Java作为一门流行的编程语言,其标准库(JDK)中巧妙地融入了多种设计...

    设计模式java 版本

    Java中,可以使用序列化或自定义方法来实现。 23. **中介者模式**:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 以上23种...

    java jdk io包类详解

    Java平台提供的`io`包为开发者提供了访问文件系统、对象序列化等功能的基本输入输出能力。它主要包括字节流、字符流及对象流(字节流的包装类)。此外,还提供了一些基本的文件访问功能。虽然这些功能在性能和灵活性...

    史上最强java4大模块33种设计模式

    4. **IO与网络模块**:Java的IO流模型支持读写文件和网络通信,包括输入/输出流、缓冲流、对象序列化等。网络编程方面,如Socket编程和HTTP通信,是开发分布式系统的基础。 接下来,33种设计模式是软件设计的精华,...

    搜搜移动大厅(XML/序列化/IO流实现)

    通过XML来结构化数据和实现数据交换,依靠序列化技术来持久化和恢复对象状态,利用IO流作为数据存储和网络通信的基础设施,搜搜移动大厅在数据管理与通信方面构建了坚实的基础。这不仅保证了应用的功能完整性,还极...

Global site tag (gtag.js) - Google Analytics