JAVA IO 序列化与设计模式

简介: 更多技术干货请戳:听云博客 序列化 什么是序列化 序列化:保存对象的状态 反序列化:读取保存对象的状态 序列化和序列化是Java提供的一种保存恢复对象状态的机制 序列化有什么用 将数据保存到文件或数据库中时 将数据通过套接字在网络上传输时 通过 RPC RMI等传输对象时 如何序

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

序列化

什么是序列化

序列化:保存对象的状态

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

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

 

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

目录
相关文章
|
12天前
|
设计模式 Java 开发者
设计模式揭秘:Java世界的七大奇迹
【4月更文挑战第7天】探索Java设计模式:单例、工厂方法、抽象工厂、建造者、原型、适配器和观察者,助你构建健壮、灵活的软件系统。了解这些模式如何提升代码复用、可维护性,以及在特定场景下的应用,如资源管理、接口兼容和事件监听。掌握设计模式,但也需根据实际情况权衡,打造高效、优雅的软件解决方案。
|
14天前
|
设计模式 存储 Java
23种设计模式,享元模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享技术有效地支持大量细粒度对象的重用。这个模式在处理大量对象时非常有用,特别是当这些对象中的许多实例实际上可以共享相同的状态时,从而可以减少内存占用,提高程序效率
31 4
|
14天前
|
设计模式 Java 中间件
23种设计模式,适配器模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要目标是让原本由于接口不匹配而不能一起工作的类可以一起工作。适配器模式主要有两种形式:类适配器和对象适配器。类适配器模式通过继承来实现适配,而对象适配器模式则通过组合来实现
30 4
|
17天前
|
设计模式 Java 数据库
Java设计模式精讲:让代码更优雅、更可维护
【4月更文挑战第2天】**设计模式是解决软件设计问题的成熟方案,分为创建型、结构型和行为型。Java中的单例模式确保类仅有一个实例,工厂方法模式让子类决定实例化哪个类。适配器模式则协调不兼容接口间的合作。观察者模式实现了一对多依赖,状态变化时自动通知相关对象。学习和适当应用设计模式能提升代码质量和可维护性,但需避免过度使用。设计模式的掌握源于实践与不断学习。**
Java设计模式精讲:让代码更优雅、更可维护
|
1月前
|
存储 Java 数据处理
|
21天前
|
设计模式 安全 Java
在Java中即指单例设计模式
在Java中即指单例设计模式
15 0
|
12天前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
|
2天前
|
设计模式 算法 Java
Java中的设计模式及其应用
【4月更文挑战第18天】本文介绍了Java设计模式的重要性及分类,包括创建型、结构型和行为型模式。创建型模式如单例、工厂方法用于对象创建;结构型模式如适配器、组合关注对象组合;行为型模式如策略、观察者关注对象交互。文中还举例说明了单例模式在配置管理器中的应用,工厂方法在图形编辑器中的使用,以及策略模式在电商折扣计算中的实践。设计模式能提升代码可读性、可维护性和可扩展性,是Java开发者的必备知识。
|
4天前
|
设计模式 算法 Java
小谈设计模式(30)—Java设计模式总结
小谈设计模式(30)—Java设计模式总结
|
5天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
12 1

热门文章

最新文章