通过与C++程序对比,彻底搞清楚JAVA的对象拷贝

2020-06-11 16:07:22来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

通过与C++程序对比,彻底搞清楚JAVA的对象拷贝

目录

  • 一、背景
  • 二、JAVA对象拷贝的实现
    • 2.1 浅拷贝
    • 2.2 深拷贝的实现方法一
    • 2.3 深拷贝的实现方法二
      • 2.3.1 C++拷贝构造函数
      • 2.3.2 C++源码
      • 2.3.3 JAVA通过拷贝构造方法实现深拷贝
  • 四、总结

一、背景

JAVA编程中的对象一般都是通过new进行创建的,新创建的对象通常是初始化的状态,但当这个对象某些属性产生变更,且要求用一个对象副本来保存当前对象的“状态”,这时候就需要用到对象拷贝的功能,以便封装对象之间的快速克隆。

二、JAVA对象拷贝的实现

2.1 浅拷贝
  • 被复制的类需要实现Clonenable接口;
  • 覆盖clone()方法,调用super.clone()方法得到需要的复制对象;
  • 浅拷贝对基本类型(boolean,char,byte,short,float,double.long)能完成自身的复制,但对于引用类型只对引用地址进行拷贝。
    -- 下面我们用一个实例进行验证:
    在这里插入图片描述
/**
 * 单只牌
 *
 * @author zhuhuix
 * @date 2020-06-10
 */
public class Card implements Comparable, Serializable,Cloneable {

    // 花色
    private String color = "";
    //数字
    private String number = "";

    public Card() {
    }

    public Card(String color, String number) {
        this.color = color;
        this.number = number;
    }

    public String getColor() {
        return this.color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getNumber() {
        return this.number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return this.color + this.number;
    }

    @Override
    public int compareTo(Object o) {
        if (o instanceof Card) {
            int thisColorIndex = Constant.COLORS.indexOf(this.getColor());
            int anotherColorIndex = Constant.COLORS.indexOf(((Card) o).getColor());
            int thisNumberIndex = Constant.NUMBERS.indexOf(this.getNumber());
            int anotherNumberIndex = Constant.NUMBERS.indexOf(((Card) o).getNumber());

            // 大小王之间相互比较: 大王大于小王
            if ("JOKER".equals(this.color) && "JOKER".equals(((Card) o).getColor())) {
                    return thisColorIndex > anotherColorIndex ? 1 : -1;
            }

            // 大小王与数字牌之间相互比较:大小王大于数字牌
            if ("JOKER".equals(this.color) && !"JOKER".equals(((Card) o).getColor())) {
                return 1;
            }
            if (!"JOKER".equals(this.color) && "JOKER".equals(((Card) o).getColor())) {
                return -1;
            }

            // 数字牌之间相互比较: 数字不相等,数字大则牌面大;数字相等 ,花色大则牌面大
            if (thisNumberIndex == anotherNumberIndex) {
                return thisColorIndex > anotherColorIndex ? 1 : -1;
            } else {
                return thisNumberIndex > anotherNumberIndex ? 1 : -1;
            }

        } else {
            return -1;
        }
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
/**
 * 扑克牌常量定义
 *
 * @author zhuhuix
 * @date 2020-06-10
 */
public class Constant {

    // 纸牌花色:黑桃,红心,梅花,方块
    final static List<String> COLORS = new ArrayList<>(
            Arrays.asList(new String[]{"?", "?", "?", "?"}));
    // 纸牌数字
    final static List<String> NUMBERS = new ArrayList<>(
            Arrays.asList(new String[]{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"}));
    // 大王小王
    final static List<String> JOKER = new ArrayList<>(
            Arrays.asList(new String[]{"小王","大王"}));
}

/**
 * 整副副扑克牌
 *
 * @author zhuhuix
 * @date 2020-06-10
 */
public class Poker implements Cloneable, Serializable {

    private List<Card> cards;

    public Poker() {
        List<Card> cardList = new ArrayList<>();
        // 按花色与数字组合生成52张扑克牌
        for (int i = 0; i < Constant.COLORS.size(); i++) {
            for (int j = 0; j < Constant.NUMBERS.size(); j++) {
                cardList.add(new Card(Constant.COLORS.get(i), Constant.NUMBERS.get(j)));
            }
        }
        // 生成大小王
        for (int i = 0; i < Constant.JOKER.size(); i++) {
            cardList.add(new Card("JOKER", Constant.JOKER.get(i)));
        }

        this.cards = cardList;
    }

   
    // 从整副扑克牌中抽走大小王
    public void removeJoker() {
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            Card cardJoker = iterator.next();
            if (cardJoker.getColor() == "JOKER") {
                iterator.remove();
            }
        }
    }

    public List<Card> getCards() {
        return cards;
    }

    public void setCards(List<Card> cards) {
        this.cards = cards;
    }

    public Integer getCardCount() {
        return this.cards.size();
    }

    @Override
    public String toString() {
        StringBuilder poker = new StringBuilder("[");
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            poker.append(iterator.next().toString() + ",");
        }
        poker.setCharAt(poker.length() - 1, ']');
        return poker.toString();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

/**
 * 测试程序
 *
 * @author zhuhuix
 * @date 2020-6-10
 */
public class PlayDemo {

    public static void main(String[] args) throws CloneNotSupportedException {

        // 生成一副扑克牌并洗好牌
        Poker poker1 = new Poker();
        System.out.println("新建:第一副牌共 "+poker1.getCardCount()+" 张:"+poker1.toString());

        Poker poker2= (Poker) poker1.clone();
        System.out.println("第一副牌拷页生成第二副牌,共 "+poker2.getCardCount()+" 张:"+poker2.toString());

        poker1.removeJoker();

        System.out.println("====第一副牌抽走大小王后====");
        System.out.println("第一副牌还有 "+poker1.getCardCount()+" 张:"+poker1.toString());
        System.out.println("第二副牌还有 "+poker2.getCardCount()+" 张:"+poker2.toString());

    }

}
  • 运行结果:
    -- 在第一副的对象中抽走了“大小王”,克隆的第二副的对象的“大小王”竟然也被“抽走了”
    在这里插入图片描述
    在这里插入图片描述
2.2 深拷贝的实现方法一
  • 被复制的类需要实现Clonenable接口;
  • 覆盖clone()方法,自主实现引用类型成员的拷贝复制。
    -- 我们只要改写一下Poker类中的clone方法,让引用类型成员实现复制:
/**
 * 整副副扑克牌--自主实现引用变量的复制
 *
 * @author zhuhuix
 * @date 2020-06-10
 */
public class Poker implements Cloneable, Serializable {

    private List<Card> cards;

    public Poker() {
        List<Card> cardList = new ArrayList<>();
        // 按花色与数字组合生成52张扑克牌
        for (int i = 0; i < Constant.COLORS.size(); i++) {
            for (int j = 0; j < Constant.NUMBERS.size(); j++) {
                cardList.add(new Card(Constant.COLORS.get(i), Constant.NUMBERS.get(j)));
            }
        }
        // 生成大小王
        for (int i = 0; i < Constant.JOKER.size(); i++) {
            cardList.add(new Card("JOKER", Constant.JOKER.get(i)));
        }

        this.cards = cardList;
    }

    // 从整副扑克牌中抽走大小王
    public void removeJoker() {
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            Card cardJoker = iterator.next();
            if (cardJoker.getColor() == "JOKER") {
                iterator.remove();
            }
        }
    }

    public List<Card> getCards() {
        return cards;
    }

    public void setCards(List<Card> cards) {
        this.cards = cards;
    }

    public Integer getCardCount() {
        return this.cards.size();
    }

    @Override
    public String toString() {
        StringBuilder poker = new StringBuilder("[");
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            poker.append(iterator.next().toString() + ",");
        }
        poker.setCharAt(poker.length() - 1, ']');
        return poker.toString();
    }

	// 遍历原始对象的集合,对生成的对象进行集合复制
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Poker newPoker = (Poker)super.clone();
        newPoker.cards = new ArrayList<>();
        newPoker.cards.addAll(this.cards);
        return newPoker;
    }
}

  • 输出结果:
    -- 通过自主实现引用类型的复制,原对象与对象的拷贝的引用类型成员地址不再关联
    在这里插入图片描述
2.3 深拷贝的实现方法二
  • 在用第二种方式实现JAVA深拷贝之前,我们首先对C++程序的对象拷贝做个了解:
2.3.1 C++拷贝构造函数

C++拷贝构造函数,它只有一个参数,参数类型是本类的引用,且一般用const修饰
在这里插入图片描述

2.3.2 C++源码
// 单只牌的类定义
// Created by Administrator on 2020-06-10.
//

#ifndef _CARD_H
#define _CARD_H

#include <string>

using namespace std;

class Card {
private :
    string color;
    string number;
public:
    Card();

    Card(const string &color, const string &number);

    const string &getColor() const;

    void setColor(const string &color);

    const string &getNumber() const;

    void setNumber(const string &number);

    string toString();

};


#endif //_CARD_H

// 单只牌类的实现
// Created by Administrator on 2020-06-10.
//

#include "card.h"

Card::Card(){}

Card::Card(const string &color, const string &number) : color(color), number(number) {}

const string &Card::getColor() const {
    return color;
}

void Card::setColor(const string &color) {
    Card::color = color;
}

const string &Card::getNumber() const {
    return number;
}

void Card::setNumber(const string &number) {
    Card::number = number;
}


string Card::toString() {
    return getColor()+getNumber();
}




// 扑克牌类的定义
// Created by Administrator on 2020-06-10.
//

#ifndef _POKER_H
#define _POKER_H

#include <vector>
#include "card.h"

using namespace std;

const int COLOR_COUNT=4;
const int NUMBER_COUNT=13;
const int JOKER_COUNT=2;

const string COLORS[COLOR_COUNT] = {"?", "?", "?", "?"};
const string NUMBERS[NUMBER_COUNT]={"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
const string JOKER[JOKER_COUNT] ={"小王","大王"};

class Poker {
private:
    vector<Card> cards;
public:
    Poker();

    Poker(const Poker &poker);

    const vector<Card> &getCards() const;

    void setCards(const vector<Card> &cards);

    int getCardCount();

    void toString();

    void clear();
};


#endif //_POKER_H

// 扑克牌类的实现
// Created by zhuhuix on 2020-06-10.
//

#include "Poker.h"
#include <iostream>

const vector<Card> &Poker::getCards() const {
    return this->cards;
}

void Poker::setCards(const vector<Card> &cards) {
    Poker::cards = cards;
}

// 构造函数
Poker::Poker() {
    for (int i = 0; i < NUMBER_COUNT; i++) {
        for (int j = 0; j < COLOR_COUNT; j++) {
            this->cards.emplace_back(COLORS[j], NUMBERS[i]);
        }
    }
    for (int i = 0; i < JOKER_COUNT; i++) {
        this->cards.emplace_back("JOKER", JOKER[i]);
    }
}

// 拷贝构造函数
Poker::Poker(const Poker &poker) {
    for (int i = 0; i < poker.getCards().size(); i++) {
        this->cards.emplace_back(poker.cards[i].getColor(), poker.cards[i].getNumber());
    }
}

int Poker::getCardCount() {
    return this->cards.size();
}

void Poker::toString() {
    cout << "共" << getCardCount() << "张牌:";
    cout << "[";
    for (int i = 0; i < this->cards.size(); i++) {
        cout << this->cards[i].toString();
        if (i != getCardCount() - 1) {
            cout << ",";
        }
    }
    cout << "]" << endl;

}

void Poker::clear() {
    this->cards.clear();
}

// 主测试程序
// Created by Administrator on 2020-06-10.
//

#include "Poker.h"
#include <iostream>

using namespace std;

int main() {
    Poker poker1;
    cout << "第一副牌:";
    poker1.toString();
    // 通过拷贝构造函数生成第二副牌
    Poker poker2(poker1);
    cout << "第二副牌:";
    poker2.toString();
    // 清除扑克牌1
    poker1.clear();
    cout << "清空后,第一副牌:";
    poker1.toString();
    cout << "第二副牌:";
    poker2.toString();
    return 0;
}
  • 输出:
    在这里插入图片描述
2.3.3 JAVA通过拷贝构造方法实现深拷贝
  • JAVA拷贝构造方法与C++的拷贝构造函数相同,被复制对象的类需要实现拷贝构造方法:
    --首先需要声明带有和本类相同类型的参数构造方法
    --其次拷贝构造方法可以通过序列化实现快速复制
  • 拷贝对象通过调用拷贝构造方法进行创建。
    -- 我们再改写一下Poker类,实现拷贝构造方法:
/**
 * 整副副扑克牌--实现拷贝构造方法
 *
 * @author zhuhuix
 * @date 2020-06-10
 */
public class Poker implements Serializable {

    private List<Card> cards;

    public Poker() {
        List<Card> cardList = new ArrayList<>();
        // 按花色与数字组合生成52张扑克牌
        for (int i = 0; i < Constant.COLORS.size(); i++) {
            for (int j = 0; j < Constant.NUMBERS.size(); j++) {
                cardList.add(new Card(Constant.COLORS.get(i), Constant.NUMBERS.get(j)));
            }
        }
        // 生成大小王
        for (int i = 0; i < Constant.JOKER.size(); i++) {
            cardList.add(new Card("JOKER", Constant.JOKER.get(i)));
        }

        this.cards = cardList;
    }

    // 拷贝构造方法:利用序列化实现深拷贝
    public Poker(Poker poker) {

        try {

            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeObject(poker);

            ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(is);
            this.cards = ((Poker) ois.readObject()).getCards();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    // 从整副扑克牌中抽走大小王
    public void removeJoker() {
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            Card cardJoker = iterator.next();
            if (cardJoker.getColor() == "JOKER") {
                iterator.remove();
            }
        }
    }

    public List<Card> getCards() {
        return cards;
    }

    public void setCards(List<Card> cards) {
        this.cards = cards;
    }

    public Integer getCardCount() {
        return this.cards.size();
    }

    @Override
    public String toString() {
        StringBuilder poker = new StringBuilder("[");
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            poker.append(iterator.next().toString() + ",");
        }
        poker.setCharAt(poker.length() - 1, ']');
        return poker.toString();
    }
}

  • 对测试主程序进行修改:
/**
 * 测试程序
 *
 * @author zhuhuix
 * @date 2020-6-10
 */
public class PlayDemo {

    public static void main(String[] args) throws CloneNotSupportedException {

        // 生成一副扑克牌并洗好牌
        Poker poker1 = new Poker();
        System.out.println("新建:第一副牌共 "+poker1.getCardCount()+" 张:"+poker1.toString());

        Poker poker2 = new Poker(poker1);
        System.out.println("第一副牌拷页生成第二副牌,共 "+poker2.getCardCount()+" 张:"+poker2.toString());

        poker1.removeJoker();

        System.out.println("====第一副牌抽走大小王后====");
        System.out.println("第一副牌还有 "+poker1.getCardCount()+" 张:"+poker1.toString());
        System.out.println("第二副牌还有 "+poker2.getCardCount()+" 张:"+poker2.toString());


        Poker poker3 = new Poker(poker1);
        System.out.println("第三副牌还有 "+poker3.getCardCount()+" 张:"+poker3.toString());
    }

}
  • 输出结果:
    --通过序列化的有手段,同样也能实现对象的深拷贝
    在这里插入图片描述

四、总结

  • java程序进行对象拷贝时,如果对象的类中存在引用类型时,需进行深拷贝
  • 对象拷贝可以通过实现Cloneable接口完成
  • java编程也可仿照 C++程序的拷贝构造函数,实现拷贝构造方法进行对象的复制
  • 通过序列化与反序化手段可实现对象的深拷贝

原文链接:https://www.cnblogs.com/zhuhuix/p/13091177.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:项目经理说这种代码必须重构,我同意了,这代码是写的是有多烂!

下一篇:Java--反射(框架设计的灵魂)