如何在Java中保持对序列化对象的引用?

时间:2014-01-14 18:13:27

标签: java serialization graph stack-overflow

我怎样才能继续引用一个对象,我确信它会被序列化,但我不希望它通过这个引用被序列化?

为了更清楚地说明我的Network包含Nodes列表,每个Node包含Connections列表,Connection包含引用另一个Nodes

问题是,当我尝试将此类结果的大Network序列化为StackOverflowError时,我假设的情况会导致以下情况:

  • 序列化从Network开始,并尝试序列化第一个Node
  • 然后它尝试序列化包含对另一个节点的引用的第一个连接
  • 它尝试序列化下一个Node等等,序列化递归遍历导致溢出的所有节点
  • 如果Connection中的引用标记为transient,则序列化没有问题,但反序列化后引用= null

以下是重现问题的示例

Network.java

import java.io.Serializable;
import java.util.ArrayList;

public class Network implements Serializable {
    private static final long serialVersionUID = 1399116563470735156L;

    ArrayList<Layer>layers;

    public Network() {
        layers= new ArrayList<Layer>();
    }

    //connect each layer to next layer
    public void connectAllLayers(){
        for (int i = 0; i < layers.size()-1; i++) {
            layers.get(i).connectTo(layers.get(i+1));
        }
    }
}

Layer.java

import java.io.Serializable;
import java.util.ArrayList;


public class Layer implements Serializable{
    private static final long serialVersionUID = -5519150448729707106L;

    public ArrayList<Node>nodes;

    public Layer(int nodeCount) {
        nodes = new ArrayList<Node>();
        for (int i = 0; i < nodeCount; i++) {
            nodes.add(new Node());
        }
    }

    //connect all nodes in a layer to all nodes in the other layer
    public void connectTo(Layer layer){
        for (Node myNode : nodes) {
            for (Node toNode : nodes) {
                myNode.connectTo(toNode);
            }
        }
    }
}

Node.java

import java.io.Serializable;
import java.util.ArrayList;


public class Node implements Serializable{
    private static final long serialVersionUID = 6323513316304801012L;

    public ArrayList<Connection>connections;
    public double x,y,z,a,b,c;//some variables to simulate memory usage

    public Node() {
        connections = new ArrayList<Connection>();
        x=15;
    }

    public void connectTo(Node node){
        Connection connection = new Connection(this, node);
        this.connections.add(connection);
        node.connections.add(connection);
    }
}

Connection.java

import java.io.Serializable;

public class Connection implements Serializable {
    private static final long serialVersionUID = 7578299749680304407L;

    public Node from;
    public Node to;
    public double weight;

    public Connection(Node from, Node to) {
        this.from = from;
        this.to = to;
    }
}

Main.java

import java.io.*;

public class Main {

    public static void saveNetwork(Network net, String filename) {
        try {
            // Serialize data object to a file
            ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream(filename));
            out.writeObject(net);
            out.close();
        } catch (IOException e) {
        }
    }

    public static Network loadNetwork(String filename) {
        Network net = null;
        try {
            FileInputStream door = new FileInputStream(filename);
            ObjectInputStream reader = new ObjectInputStream(door);
            net = (Network) reader.readObject();
            reader.close();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        return net;
    }

    public static void main(String[] args) {
        Network net;
        boolean load = false;//Change to true to load the saved object

        if (!load) {
            net = new Network();
            net.layers.add(new Layer(400));
            net.layers.add(new Layer(300));
            net.layers.add(new Layer(10));

            net.connectAllLayers();

            saveNetwork(net, "mynet");
        } else {
            net = loadNetwork("mynet");
        }
        Layer layer = net.layers.get(0);
        Node node = layer.nodes.get(0);
        Connection connection = node.connections.get(0);
        System.out.println(connection.to.x);
    }

}

为了使问题更加通用,假设存在一个包含对所有节点的引用的列表而不会导致溢出,那么是否可以将Graph序列化为连接的Node类?

2 个答案:

答案 0 :(得分:0)

Java将精确地序列化图形,仅存储对已经序列化的对象的引用。但是,它确实以深度优先的方式使用递归,并且您的图形非常深(在我发生堆栈溢出之前548个节点深度)。

按深度对图层进行排序,并按深度的降序序列化它们。这将阻止序列化期间的深度递归。

答案 1 :(得分:0)

我相信这段代码对您有用,如果用于生产,请注意仔细测试它。首先,它不会在类上寻找自定义序列化例程。但我认为它符合目的。

它由两个类SequentialObjectOutputStream和SequentialObjectInputStream

组成
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import android.util.*;

public class SequentialObjectOutputStream extends DataOutputStream
implements ObjectOutput
{
    interface FieldGetAction
    {
        void get(Object obj, Field field) throws IllegalAccessException, IOException;
    }

    interface ArrayGetAction
    {
        void get(Object array, int Index) throws ArrayIndexOutOfBoundsException, IOException;       
    }

    public HashMap<Class, FieldGetAction> Primatives;
    public HashMap<Class, ArrayGetAction> ArrayPrimatives;

    public SequentialObjectOutputStream(OutputStream stream)
    {
        super(stream);

        Primatives = new HashMap<Class, FieldGetAction>();

        try
        {
            Primatives.put(boolean.class,
            new FieldGetAction()
            {
                public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                {
                    boolean x = field.getBoolean(obj);
                    writeBoolean(x);

                }
            });

            Primatives.put(byte.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        byte x = field.getByte(obj);
                        writeByte(x);

                    }
                });


            Primatives.put(short.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        short x = field.getShort(obj);
                        writeShort(x);

                    }
                });


            Primatives.put(int.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        int x = field.getInt(obj);
                        writeInt(x);

                    }
                });


            Primatives.put(long.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        long x = field.getLong(obj);
                        writeLong(x);

                    }
                });


            Primatives.put(char.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        char x = field.getChar(obj);
                        writeChar(x);

                    }
                });


            Primatives.put(float.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        float x = field.getFloat(obj);
                        writeFloat(x);

                    }
                });


            Primatives.put(double.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        double x = field.getDouble(obj);
                        writeDouble(x);
                    }
                });


            Primatives.put(String.class,
                new FieldGetAction()
                {
                    public void get(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        String x = (String) field.get(obj);
                        writeUTF(x);

                    }
                });
        } catch(Exception e)
        {
            Log.e("SOb", Log.getStackTraceString(e));
        }



        ArrayPrimatives = new HashMap<Class, ArrayGetAction>();

        try
        {
            ArrayPrimatives.put(boolean.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        boolean x = Array.getBoolean(obj, index);
                        writeBoolean(x);

                    }
                });

            ArrayPrimatives.put(byte.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        byte x = Array.getByte(obj, index);
                        writeByte(x);

                    }
                });


            ArrayPrimatives.put(short.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        short x = Array.getShort(obj, index);
                        writeShort(x);

                    }
                });


            ArrayPrimatives.put(int.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        int x = Array.getInt(obj, index);
                        writeInt(x);

                    }
                });


            ArrayPrimatives.put(long.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        long x = Array.getLong(obj, index);
                        writeLong(x);

                    }
                });


            ArrayPrimatives.put(char.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        char x = Array.getChar(obj, index);
                        writeChar(x);

                    }
                });


            ArrayPrimatives.put(float.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        float x = Array.getFloat(obj, index);
                        writeFloat(x);

                    }
                });


            ArrayPrimatives.put(double.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        double x = Array.getDouble(obj, index);
                        writeDouble(x);
                    }
                });


            ArrayPrimatives.put(String.class,
                new ArrayGetAction()
                {
                    public void get(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        String x = (String) Array.get(obj, index);
                        writeUTF(x);

                    }
                });
        } catch(Exception e)
        {
            Log.e("SOb", Log.getStackTraceString(e));
        }

    }

    class State
    {
        public ArrayList<Object> OStack = new ArrayList<Object>();

        public long currentId = 1;

        public HashMap<Object, Long> References = new HashMap<Object, Long>();

    }

    public void writeObject(Object A) throws IOException, NotSerializableException
    {
        final State state = new State();

        state.OStack.add(0, A);

        LinkedList<Object> ForStack = new LinkedList<Object>();

        while (!(state.OStack.size() == 0))
        {
            Object Current = state.OStack.get(0);
            state.OStack.remove(0);

            if (((Serializable) Current) == null)
            {
                throw new NotSerializableException();
            }


            //Type C = Current.getClass();

            Class C = Current.getClass();

            Log.i("SOb", "placing #"+Long.toString(state.currentId)+" of "+C.getCanonicalName()+" on graph"); 
            state.References.put(Current, state.currentId);
            state.currentId++;

            ForStack.add(Current);

            if (C.isArray())
            {
                //Array array = (Array) Current;
                Class Ctype = C.getComponentType();

                if (ArrayPrimatives.keySet().contains(Ctype) == false)
                {
                    for (int I=0; I<Array.getLength(Current); I++)
                    {
                        Object o = Array.get(Current, I);

                        if ((o != null) && (state.References.keySet().contains(o) == false))
                        {
                            if (state.OStack.contains(o) == false) state.OStack.add(state.OStack.size(), o);
                        }

                    }
                }
            } else
            {
                for (Class Cur = C; Cur != null; Cur = Cur.getSuperclass())
                {

                    Field[] fields = Cur.getDeclaredFields();

                    for (Field f : fields)
                    {
                        if (Modifier.isStatic(f.getModifiers()))
                        {
                            continue;
                        }

                        f.setAccessible(true);

                        if (f.isAccessible() == false)
                        {
                        //  Log.i("SOb", "     isAccessible = false");
                            continue;
                        }

                        Class type = f.getType();
                        //Log.i("SOb", "     field \""+f.getName()+"\" of "+type.getCanonicalName());

                        if (Primatives.keySet().contains(type) == false)
                        {       
                            try
                            {
                                Object o = f.get(Current);

                                if ((o != null) && (state.References.keySet().contains(o) == false))
                                {
                                    if (state.OStack.contains(o) == false) state.OStack.add(state.OStack.size(), o);
                                }

                            } catch (IllegalAccessException e)
                            {
                                Log.e("SOb", Log.getStackTraceString(e));
                            }
                        }
                    }
                }
            }
        }

        writeLong(state.References.size());

        for (Object O : ForStack )
        {
            Serializable s = (Serializable) O;

        //  if (s != null)
            {
                Class cl = O.getClass();

                String name = cl.getName();

                writeUTF(name);

                if (cl.isArray())
                {
                    Class components = cl.getComponentType();

                    ArrayGetAction action;

                    //Array array = (Array) O;

                    if (ArrayPrimatives.keySet().contains(components))
                    {
                        action = ArrayPrimatives.get(components);
                    } else
                    {
                        action = new ArrayGetAction()
                        {
                            public void get(Object array, int index) throws ArrayIndexOutOfBoundsException, IOException     
                            {
                                Object O = Array.get(array, index);
                                if (O==null)  writeLong(0);
                                else writeLong(state.References.get(O));
                            }
                        };
                    }

                    int length = Array.getLength(O);

                    writeInt(length);

                    for (int I=0; I<length; I++)
                    {
                        action.get(O, I);
                    }

                } else
                {
                    for (Class Cur = cl; Cur != null; Cur = Cur.getSuperclass())
                    {
                        Field[] fields = Cur.getDeclaredFields();

                        for (Field F : fields)
                        {
                            Class FieldType = F.getType();

                            F.setAccessible(true);

                            if (F.isAccessible() && (Modifier.isStatic(FieldType.getModifiers())))
                            {
                                FieldGetAction action;

                                //Array array = (Array) O;

                                if (Primatives.keySet().contains(FieldType))
                                {
                                    action = Primatives.get(FieldType);
                                } else
                                {
                                    action = new FieldGetAction()
                                    {
                                        public void get(Object obj, Field index) throws IllegalAccessException, IOException     
                                        {
                                            Object O = index.get(obj);
                                            if (O==null)  writeLong(0);
                                            else writeLong(state.References.get(O));
                                        }
                                    };
                                }

                                try
                                {
                                    action.get(O, F);
                                } catch (IllegalAccessException e)
                                {
                                    Log.e("SOb", Log.getStackTraceString(e));
                                }

                            }
                        }

                    }
                }
            }   
        }
    }
}

SequentialObjectInputStream

import java.io.*;
import java.util.*;
import java.lang.reflect.*;

import android.util.*;

public class SequentialObjectInputStream extends DataInputStream implements ObjectInput
{
    interface FieldPutAction
    {
        void put(Object obj, Field field) throws IllegalAccessException, IOException;
    }

    interface ArrayPutAction
    {
        void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException;
    }

    public HashMap<Class, FieldPutAction> Primatives;
    public HashMap<Class, ArrayPutAction> ArrayPrimatives;

    public SequentialObjectInputStream(InputStream stream)
    {
        super(stream);

        Primatives = new HashMap<Class, FieldPutAction>();

        try
        {
            Primatives.put(boolean.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        boolean x = readBoolean(); 
                        field.setBoolean(obj, x);

                    }
                });

            Primatives.put(byte.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        byte x = readByte(); 
                        field.setByte(obj, x);

                    }
                });


            Primatives.put(short.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        short x = readShort(); 
                        field.setShort(obj, x);

                    }
                });


            Primatives.put(int.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        int x = readInt(); 
                        field.setInt(obj, x);

                    }
                });


            Primatives.put(long.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        long x = readLong(); 
                        field.setLong(obj, x);

                    }
                });


            Primatives.put(char.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        char x = readChar(); 
                        field.setChar(obj, x);

                    }
                });


            Primatives.put(float.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        float x = readFloat(); 
                        field.setFloat(obj, x);

                    }
                });


            Primatives.put(double.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        double x = readDouble(); 
                        field.setDouble(obj, x);

                    }
                });


            Primatives.put(String.class,
                new FieldPutAction()
                {
                    public void put(Object obj, Field field) throws IllegalAccessException, IOException 
                    {
                        String x = readUTF(); 
                        field.set(obj, x);

                    }
                });
        } catch(Exception e)
        {
            Log.e("SOb", Log.getStackTraceString(e));
        }

        ArrayPrimatives = new HashMap<Class, ArrayPutAction>();

        try
        {
            ArrayPrimatives.put(boolean.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        boolean x = readBoolean();
                        Array.setBoolean(obj, index, x);
                    }
                });

            ArrayPrimatives.put(byte.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        byte x = readByte(); 
                        Array.setByte(obj, index, x);

                    }
                });


            ArrayPrimatives.put(short.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        short x = readShort(); 
                        Array.setShort(obj, index, x);

                    }
                });


            ArrayPrimatives.put(int.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        int x = readInt(); 
                        Array.setInt(obj, index, x);

                    }
                });


            ArrayPrimatives.put(long.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        long x = readLong(); 
                        Array.setLong(obj, index, x);

                    }
                });


            ArrayPrimatives.put(char.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        char x = readChar(); 
                        Array.setChar(obj, index, x);

                    }
                });


            ArrayPrimatives.put(float.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        float x = readFloat(); 
                        Array.setFloat(obj, index, x);

                    }
                });


            ArrayPrimatives.put(double.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        double x = readDouble(); 
                        Array.setDouble(obj, index, x);

                    }
                });


            ArrayPrimatives.put(String.class,
                new ArrayPutAction()
                {
                    public void put(Object obj, int index) throws ArrayIndexOutOfBoundsException, IOException   
                    {
                        String x = readUTF(); 
                        Array.set(obj, index, x);

                    }
                });
        } catch(Exception e)
        {
            Log.e("SOb", Log.getStackTraceString(e));
        }
    }


    @Override
    public Object readObject() throws ClassNotFoundException, IOException
    {
        long Total = readLong();

        Log.i("SOb", "readObject : " + Long.toString(Total) + " objects in graph");

        HashMap<Long, Object> References = new HashMap<Long, Object>();

        long currentId = 1;

        HashMap<Object, HashMap<Field, Long>> refCache =
            new HashMap<Object, HashMap<Field, Long>>();
        final HashMap<Object, HashMap<Integer, Long>> arefCache =
            new HashMap<Object, HashMap<Integer,Long>>();

        for (int I=0; I < Total; I++)
        {
            String Name = readUTF();
            Class C = Class.forName(Name);

            Log.i("SOb", "Object of "+C.getCanonicalName() +" on graph");

            int adim = 0;

            Object O = null;

            if (C.isArray())
            {
                Class ComponentType = C.getComponentType();

                int Size = readInt();

                Log.i("SOb", "array of "+ComponentType.getCanonicalName() + ", " + Long.toString(Size) + " elements");          
                O = Array.newInstance(ComponentType, Size);

                References.put(currentId, O);
                currentId++;

                ArrayPutAction action = null;

                if (ArrayPrimatives.keySet().contains(ComponentType))
                {
                    action = ArrayPrimatives.get(ComponentType);
                } else
                {
                    arefCache.put(O, new HashMap<Integer, Long>());

                    action = new ArrayPutAction()
                    {
                        public void put(Object O, int Index) throws ArrayIndexOutOfBoundsException , IOException
                        {
                            long Ref = readLong();

                            arefCache.get(O).put(Index, Ref);
                        }
                    };
                }

                for (int index=0; index< Size; index++)
                {
                    action.put(O,index);
                }

            } else
            {

            try
            {

                O = 
                    C.getConstructor(new Class[0]).newInstance(new Object[0]);
            } catch(InstantiationException e)
            {
                Log.e("SOb", Log.getStackTraceString(e));
            } catch(NoSuchMethodException e)
            {
                Log.e("SOb", Log.getStackTraceString(e));
            } catch(IllegalAccessException e)
            {
                Log.e("SOb", Log.getStackTraceString(e));
            } catch(InvocationTargetException e)
            {
                Log.e("SOb", Log.getStackTraceString(e));
            }

            References.put(currentId, O);
            currentId++;
            refCache.put(O, new HashMap<Field, Long>());

            for (Field F : C.getFields())
            {
                if (F.isAccessible())
                {
                    Class T = F.getType();

                    if (Primatives.containsKey(T))
                    {
                        try
                        {
                            Primatives.get(T).put(O, F);
                        } catch (IllegalAccessException e)
                        {

                        }
                    } else
                    {
                        refCache.get(O).put(F, readLong());
                    }
                }
            }

        }
        }
        for (long I=0; I < Total; I++)
        {

            Object O = References.get(I+1);

            Class C = O.getClass();

            //Log.i("SOb", "get reference "+Long.toString(I)+" "+C.getCanonicalName());


            if (C.isArray())
            {
                HashMap<Integer,Long> aref_table = arefCache.get(O);

                if (ArrayPrimatives.containsKey(C.getComponentType()) == false)
                {

                    int len = Array.getLength(O);

                    for (int index=0; index<len; index++)
                    {
                        long r = aref_table.get(index);
                        Object ref = r == 0 ? null : References.get(r);

                        Array.set(O, index, ref);   
                    }
                }

            } else
            {

            HashMap<Field, Long> ref_table = refCache.get(O);

            for (Field F : C.getFields())
            {
                if (F.isAccessible())
                {
                    Class T = F.getType();

                    if (Primatives.containsKey(T) == false)
                    {
                        try
                        {
                            long r = ref_table.get(F);
                            Object ref = r == 0 ? null : References.get(r);

                            F.set(O, ref);
                        } catch (IllegalAccessException e)
                        {
                            Log.e("SOb", Log.getStackTraceString(e));
                        }

                    }
                }
            }
            }

        }


        return References.get((Long) (long) 1);
    }

}

SequentialObjectOutputStream