zoukankan      html  css  js  c++  java
  • java對象序列化的兩種使用方法

    首先,需要紀錄的是在項目中出現的一個問題,序列化一個內部類,出現NotSerializableException.
    解決方法有兩個,第一種方法是最開始自己的處理,使用writeObject和readObject來實現自定義序列化,這個有些
    繁雜,需要對每個實例字段進行序列化,不然,當你反序列化時,得到的對象的字段都是默認值,如int=0,string=null.
    ... ...
    public class MsgBean implements Serializable {

            /** *//**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;

            private int msgType;

            private String mName;

            public String getMName() {
                return mName;
            }

            public void setMName(String name) {
                mName = name;
            }

            public int getMsgType() {
                return msgType;
            }

            public void setMsgType(int msgType) {
                this.msgType = msgType;
            }

            private void writeObject(ObjectOutputStream out) throws IOException {
                out.writeObject(getMName());
                out.writeObject(new Integer(getMsgType()));
            }

            private void readObject(ObjectInputStream in) throws IOException,
                    ClassNotFoundException {
                setMName((String) in.readObject());
                setMsgType((Integer)in.readObject());
            }

            public String toString() {
                return "The object's class is MsgBean,field:mName=" + mName
                        + ",msgType=" + msgType;
            }
        }
    ... ...

    ,後來在網上找找,發現只需要將該內部類聲明為static,即可.這是因為內部非靜態類會存在一個隱含的到外部
    類的引用(外部類.this),所以當序類化內部類時,會關聯序列化外部類,恰恰外部類沒有實現Serializable接口,就會
    出現這個錯誤.下面是改過後的.
    public static class MsgBean implements Serializable {

            /** *//**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;

            private int msgType;

            private String mName;

            public String getMName() {
                return mName;
            }

            public void setMName(String name) {
                mName = name;
            }

            public int getMsgType() {
                return msgType;
            }

            public void setMsgType(int msgType) {
                this.msgType = msgType;
            }

            public String toString() {
                return "The object's class is MsgBean,field:mName=" + mName
                        + ",msgType=" + msgType;
            }
        }


    好了,上面只是題外話.

    我們在規劃序列化對象時,一般要考慮序列化的性能,所以對象不能太大,層次也不能太多.從這方面來考慮,
    可以選擇序列化完整的一個類,或是只是針對字段來進行序列化.若是用于分布式中數據的傳輸,那應該是後
    一種最好了.
    下面來看第一種方法的一個例子
    序列化對象

     
    try {
                MsgBean msgBean = new MsgBean();
                msgBean.setMName(this.mName);
                msgBean.setMsgType(msgtype);
                ByteArrayOutputStream bytearrayoutputstream = null;
                ObjectOutputStream objectoutputstream = null;
                try {
                    bytearrayoutputstream = new ByteArrayOutputStream();
                    objectoutputstream = new ObjectOutputStream(
                            bytearrayoutputstream);
                    objectoutputstream.writeObject(msgBean);
                    this.channel.send(new Message(null, null, bytearrayoutputstream
                            .toByteArray()));
                } finally {
                    if (objectoutputstream != null)
                        objectoutputstream.close();
                    if (bytearrayoutputstream != null)
                        bytearrayoutputstream.close();
                }
            } catch (Exception e) {
                logger.error("Jgroups send msg exception,msgtype=" + msgtype
                        + ",mName=" + mName, e);
            }
     
    反序列化
     
    public void receive(Message arg0) {
        byte[] buf = arg0.getRawBuffer();
    ByteArrayInputStream bytearrayinputstream = null;
            ObjectInputStream objectinputstream = null;
            try {
                try {
                    bytearrayinputstream = new ByteArrayInputStream(buf, arg0
          .getOffset(), arg0.getLength());
                    objectinputstream = new ObjectInputStream(bytearrayinputstream);
                    MsgBean msgBean = (MsgBean) objectinputstream.readObject();
                    if (logger.isDebugEnabled())
                        logger.debug(msgBean.toString());         
                } finally {
                    if (objectinputstream != null)
                        objectinputstream.close();
                    if (bytearrayinputstream != null)
                        bytearrayinputstream.close();
                }
            } catch (Exception e) {
                logger.error("Received message and process exception.", e);
            }
    }
     
    另外一種方法,是在jgroups的demo中看到的,
    首先定義了一個公共的序列化接口
    package org.jgroups.util;

    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;

    /** *//**
     * Implementations of Streamable can add their state directly to the output stream, enabling them to bypass costly
     * serialization
     * @author Bela Ban
     * @version $Id: Streamable.java,v 1.2 2005/07/25 16:21:47 belaban Exp $
     */
    public interface Streamable {

        /** *//** Write the entire state of the current object (including superclasses) to outstream.
         * Note that the output stream <em>must not</em> be closed */
        void writeTo(DataOutputStream out) throws IOException;

        /** *//** Read the state of the current object (including superclasses) from instream
         * Note that the input stream <em>must not</em> be closed */
        void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException;
    }
    然後將需要序列化的類實現這個方法
    // $Id: DrawCommand.java,v 1.6 2006/10/09 11:35:46 belaban Exp $

    package org.jgroups.demos;

    import org.jgroups.util.Streamable;

    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.DataInputStream;

    /** *//**
     * Encapsulates information about a draw command.
     * Used by the {@link Draw} and other demos.
     *
     */
    public class DrawCommand implements Streamable {
        static final byte DRAW=1;
        static final byte CLEAR=2;
        byte mode;
        int x=0;
        int y=0;
        int r=0;
        int g=0;
        int b=0;

        public DrawCommand() { // needed for streamable
        }

        DrawCommand(byte mode) {
            this.mode=mode;
        }

        DrawCommand(byte mode, int x, int y, int r, int g, int b) {
            this.mode=mode;
            this.x=x;
            this.y=y;
            this.r=r;
            this.g=g;
            this.b=b;
        }


        public void writeTo(DataOutputStream out) throws IOException {
            out.writeByte(mode);
            out.writeInt(x);
            out.writeInt(y);
            out.writeInt(r);
            out.writeInt(g);
            out.writeInt(b);
        }

        public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException {
            mode=in.readByte();
            x=in.readInt();
            y=in.readInt();
            r=in.readInt();
            g=in.readInt();
            b=in.readInt();
        }


        public String toString() {
            StringBuffer ret=new StringBuffer();
            switch(mode) {
                case DRAW: ret.append("DRAW(" + x + ", " + y + ") [" + r + '|' + g + '|' + b + ']');
                    break;
                case CLEAR: ret.append("CLEAR");
                    break;
                default:
                    return "<undefined>";
            }
            return ret.toString();
        }

    }
    序列化
     
    public void sendClearPanelMsg() {
            int                  tmp[]=new int[1]; tmp[0]=0;
            DrawCommand          comm=new DrawCommand(DrawCommand.CLEAR);

            try {
                byte[] buf=Util.streamableToByteBuffer(comm);
                channel.send(new Message(null, null, buf));
            }
            catch(Exception ex) {
                System.err.println(ex);
            }
        }
     
    反序列化
     
    public void receive(Message msg) {
            byte[] buf=msg.getRawBuffer();
            if(buf == null) {
                System.err.println("received null buffer from " + msg.getSrc() + ", headers: " + msg.getHeaders());
                return;
            }

            try {
                DrawCommand comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, msg.getOffset(), msg.getLength());
                switch(comm.mode) {
                    case DrawCommand.DRAW:
                        if(panel != null)
                            panel.drawPoint(comm);
                        break;
                    case DrawCommand.CLEAR:
                        clearPanel();
                        break;
                    default:
                        System.err.println("***** received invalid draw command " + comm.mode);
                        break;
                }
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
     
    上面兩個方法用到的兩個util類的方法
     
     private static final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512);
     
     public static byte[] streamableToByteBuffer(Streamable obj) throws Exception {
            byte[] result=null;
            synchronized(out_stream) {
                out_stream.reset();
                DataOutputStream out=new DataOutputStream(out_stream);
                obj.writeTo(out);
                result=out_stream.toByteArray();
                out.close();
            }
            return result;
        }

    public static Streamable streamableFromByteBuffer(Class cl, byte[] buffer, int offset, int length) throws Exception {
            if(buffer == null) return null;
            Streamable retval=null;
            ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length);
            DataInputStream in=new DataInputStream(in_stream); // changed Nov 29 2004 (bela)
            retval=(Streamable)cl.newInstance();
            retval.readFrom(in);
            in.close();
            if(retval == null)
                return null;
            return retval;
        }
     


    就是這麼多了.
    寫到這裡,突然想到,是否java默認的序列化,也就是只將實例字段進行序列化,類結構等等應該不會也序列化的.
    那麼這樣,上面兩種方法實際上區別不大(第一種是我這種懶惰的人比較喜歡的,^_^).
    不過默認的序列化,即使只將實例字段進行序列化,應該也要帶上字段的名稱等信息吧,這樣的話,序列後的字節數應該還是比第二種多.

    補充
    看了一下jdk關于ObjectOutputStream中的注釋,了解到,默認的序列化,也沒有將整個類的代碼都序列化,而是序列
    化可變的數據等等,下面是jdk的一部份內容:
    对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用(瞬态和静态字段除外)也会导致写入那些对象。可使用引用共享机制对单个对象的多个引用进行编码,这样即可将对象的图形还原为最初写入它们时的形状。

    writeObject 方法负责写入特定类的对象状态,以便相应的 readObject 方法可以还原它。该方法本身不必与属于对象的超类或子类的状态有关。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。

    基本数据(不包括 serializable 字段和 externalizable 数据)以块数据记录的形式写入 ObjectOutputStream 中。块数据记录由头部和数据组成。块数据部分包括标记和跟在部分后面的字节数。连续的基本写入数据被合并在一个块数据记录中。块数据记录的分块因子为 1024 字节。每个块数据记录都将填满 1024 字节,或者在终止块数据模式时被写入。调用 ObjectOutputStream 方法 writeObject、defaultWriteObject 和 writeFields 最初只是终止所有现有块数据记录。

  • 相关阅读:
    pow()函数结果强制转化为int造成误差的分析
    warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
    博客园鼠标点击特效代码
    codeblocks更改颜色主题
    codeblocks1712设置中文
    SQl
    项目中nodejs包高效升级插件npm-check-updates
    正则表达式的整理(将金钱数变成带有千分位)
    从一个数组中过滤出另外一个数组中相关字段相等的数据
    IONIC3 打包安卓apk详细过程(大量图文)
  • 原文地址:https://www.cnblogs.com/oisiv/p/593399.html
Copyright © 2011-2022 走看看