【问题标题】:Deep copying an array of objects深度复制对象数组
【发布时间】:2014-11-03 07:32:04
【问题描述】:

我对 Java 还是很陌生,现在我正在尝试制作 Menu 的副本。我想我已经做了一点,我在其中创建了一个新的 Menu 对象,其中包含新的 MenuItems。 MenuItems 是另一个类,它具有两个字符串变量和一个双变量,即 itemName 和 itemDescription 以及 itemPrice。所以我试图将内容,原始MenuItems的三个变量复制到MenuItems副本中,但我不知道如何。我一直在尝试将克隆副本的名称设置为原始名称。

public class Menu 
{
    Menu()
    {

    }

    final int maxItems = 50;

    MenuItem[] food = new MenuItem[maxItems + 1];


    public Object clone()
    {
        Menu menuClone = new Menu();
        MenuItem[] foodClone = new MenuItem[maxItems + 1];

        for(int i = 1; i <= maxItems + 1; i++)
        {  
            foodClone[i] = new MenuItem();
            foodClone[i] = food[i].setItemName();
        }

    }

这是 MenuItem 类:

public class MenuItem 
{
    private String name;
    private String descrip;
    private double price;


    MenuItem()
    {

    }


    public String getItemName()
    {
        return name;
    }

    public String getItemDescrip()
    {
        return descrip;
    }

    public double getPrice()
    {
        return price;
    }

    public void setItemName(String itemName)
    {
        name = itemName; 
    }

    public void setItemDescrip(String itemDescrip)
    {
        descrip = itemDescrip;
    }

    public void setPrice(double itemPrice) throws IllegalArgumentException
    {
        if(itemPrice >= 0.0)
            price = itemPrice;
        else
            throw new IllegalArgumentException("Enter only positive values");
    }

    public String toString(){
        return "Name: " + name + ", Desc: " + descrip;
    }
}

【问题讨论】:

  • 如果你想克隆一个对象,我建议尝试使用clone()方法。
  • 你必须正确覆盖clone对象并实现Cloneable..或者简单地制作一个复制构造函数..和tip..java中的数组是基于0的..所以第一个元素是数组[ 0] 不是 [1]

标签: java arrays clone


【解决方案1】:

你就快到了,你有的地方:

foodClone[i] = food[i].setItemName();

您可能想要(除了 MenuItem 的其他变量)

foodClone[i].setItemName(food[i].getItemName())`

但是,最好使用克隆方法或复制构造函数(嗯,copy constructor arguably might be best)。

我更喜欢使用复制构造函数,例如:

MenuItem(MenuItem menuItemToClone)
{
     this.name = menuItemToClone.name;
     this.descrip = menuItemToClone.descrip;
     this.price = menuItemToClone.price;
}

那么你会这样做:

foodClone[i] = new MenuItem(food[i]);

【讨论】:

    【解决方案2】:

    尽管之前有一些建议,但克隆仅提供浅层副本。

    深拷贝问题的一个常见解决方案是使用 Java 对象序列化 (JOS)。这个想法很简单:使用 JOS 的 ObjectOutputStream 将对象写入数组,然后使用 ObjectInputStream 重构对象的副本。结果将是一个完全不同的对象,具有完全不同的引用对象。 JOS 负责所有细节:超类字段、跟随对象图以及处理对图中同一对象的重复引用。图 3 显示了使用 JOS 进行深度复制的实用程序类的初稿。

    import java.io.IOException;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectOutputStream;
    import java.io.ObjectInputStream;
    
    /**
     * Utility for making deep copies (vs. clone()'s shallow copies) of 
     * objects. Objects are first serialized and then deserialized. Error
     * checking is fairly minimal in this implementation. If an object is
     * encountered that cannot be serialized (or that references an object
     * that cannot be serialized) an error is printed to System.err and
     * null is returned. Depending on your specific application, it might
     * make more sense to have copy(...) re-throw the exception.
     *
     * A later version of this class includes some minor optimizations.
     */
    public class UnoptimizedDeepCopy {
    
    /**
     * Returns a copy of the object, or null if the object cannot
     * be serialized.
     */
    public static Object copy(Object orig) {
        Object obj = null;
        try {
            // Write the object out to a byte array
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);
            out.writeObject(orig);
            out.flush();
            out.close();
    
            // Make an input stream from the byte array and read
            // a copy of the object back in.
            ObjectInputStream in = new ObjectInputStream(
                new ByteArrayInputStream(bos.toByteArray()));
            obj = in.readObject();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        catch(ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        return obj;
    }
    

    }

    不幸的是,这种方法存在一些问题:

    1. 仅当被复制的对象以及该对象直接或间接引用的所有其他对象都可序列化时,它才会起作用。 (换句话说,它们必须实现 java.io.Serializable。)幸运的是,简单地声明给定类实现 java.io.Serializable 并让 Java 的默认序列化机制完成它们的工作通常就足够了。

    2. Java 对象序列化很慢,使用它进行深度复制需要序列化和反序列化。有一些方法可以加快速度(例如,通过预先计算串行版本 ID 并定义自定义 readObject() 和 writeObject() 方法),但这通常是主要瓶颈。

    3. java.io 包中包含的字节数组流实现被设计为足够通用,可以很好地处理不同大小的数据,并且可以在多线程环境中安全使用。然而,这些特性会减慢 ByteArrayOutputStream 和(在较小程度上)ByteArrayInputStream。

    来源:http://javatechniques.com/blog/faster-deep-copies-of-java-objects/

    【讨论】:

    • 4. Java 序列化将私有状态暴露给外部世界,并允许在读回对象时违反不变量。
    猜你喜欢
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2015-05-06
    • 1970-01-01
    • 2012-07-03
    • 2018-01-28
    相关资源
    最近更新 更多