JAVA IO 序列化与设计模式

?更多技术干货请戳:听云博客

序列化

什么是序列化

序列化:保存对象的状态

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

序列化和序列化是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。

适配器模式

上图是适配器模式的类图。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库中多有的类型的链接流处理器中。一个装饰类提供的新的方法越多,它离纯装饰模式的距离就越远,离适配器模式的距离也就越近。

原文链接:http://blog.tingyun.com/web/article/detail/460

时间: 2024-10-12 20:54:53

JAVA IO 序列化与设计模式的相关文章

Java IO: 序列化与ObjectInputStream、ObjectOutputStream

作者:Jakob Jenkov  译者: 李璟([email protected]) 本小节会简要概括Java IO中的序列化以及涉及到的流,主要包括ObjectInputStream和ObjectOutputStream. Serializable 原文链接 如果你希望类能够序列化和反序列化,必须实现Serializable接口,就像所展示的ObjectInputStream和ObjectOutputStream例子一样. 对象序列化本身就是一个主题.Java IO系列教程主要关注流.read

Java IO 序列化 transient关键字

Java IO 序列化 transient关键字 @author 敏敏Alexia 转自:http://www.cnblogs.com/lanxuezaipiao/p/3369962.html 1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化. 然而在实际开发过程

JAVA IO中的设计模式

在java语言 I/O库的设计中,使用了两个结构模式,即装饰模式和适配器模式.       在任何一种计算机语言中,输入/输出都是一个很重要的部分.与一般的计算机语言相比,java将输入/输出的功能和使用范畴做了很大的扩充.因此输入输出在java语言中占有极为重要的位置.       java语言采用流的机制来实现输入/输出.所谓流,就是数据的有序排列,流可以是从某个源(称为流源,或者 Source of Stream)出来,到某个目的(Sink of Stream)地去.根据流的方向可以将流分

Java IO中的设计模式--装饰器和适配器模式

参考:http://my.oschina.net/gao0516/blog/136103 装饰器模式:是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能 适配器模式:将一个类的接口转换成客户希望的另外一个接口 直接上代码分析: package com.think.cla; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class

基于Java IO 序列化方案的memcached-session-manager多memcached节点配置

通过nginx将请求负载均衡,而后台的几组tomcat的session通过memcached(non-sticky模式)进行统一管理,这几组tomcat部署的web app是同一应用,session的变化要统一,项目组最后采用memcached-session-manager来对tomcat的session进行管理. session的序列化方案官方推荐的有4种 java serialization msm-kryo-serializer msm-javolution-serializer msm

Java IO和NIO文章目录

1.java IO详尽解析 2.深入分析 Java I/O 的工作机制 3.InputStream类详解 4.OutputStream类详解 5.JAVA的节点流和处理流 6.FileInputStream和FileOutputStream详解 7.JAVA IO中的设计模式

Java IO 学习总结 学习手册总结

Java IO 是一套Java用来读写数据(输入和输出)的API.大部分程序都要处理一些输入,并由输入产生一些输出.Java为此提供了java.io包. 代码 github地址:https://github.com/loveincode/StudyTest/tree/master/src/IO Java.io 包的范围 java.io 包并没有涵盖所有输入输出类型.例如,并不包含GUI或者网页上的输入输出,这些输入和输出在其它地方都涉及,比如Swing工程中的JFC (Java Foundati

Java IO教程

1  Java IO 教程 2 Java IO 概述 3 Java IO: 文件 4 Java IO: 管道 5 Java IO: 网络 6 Java IO: 字节和字符数组 7 Java IO: System.in, System.out, and System.error 8 Java IO: 流 9 Java IO: Input Parsing(暂无翻译,处理中) 10 Java IO: Readers and Writers 11 Java IO: 并发IO 12 Java IO: 异常

高级Java工程师必备 ----- 深入分析 Java IO (三)

概述 Java IO即Java 输入输出系统.不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO(文件.控制台.网络),我们还要考虑具体和它们的通信方式(顺序.随机.二进制.按字符.按字.按行等等).Java类库的设计者通过设计大量的类来攻克这些难题,这些类就位于java.io包中. 在JDK1.4之后,为了提高Java IO的效率,Java又提供了一套新的IO,Java New IO简称Jav