前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过与C++程序对比,彻底搞清楚JAVA的对象拷贝

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

作者头像
智慧zhuhuix
发布2020-08-14 16:32:44
3670
发布2020-08-14 16:32:44
举报
文章被收录于专栏:智慧zhuhuix的开发专栏

一、背景

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

二、JAVA对象拷贝的实现
2.1 浅拷贝
  • 被复制的类需要实现Clonenable接口;
  • 覆盖clone()方法,调用super.clone()方法得到需要的复制对象;
  • 浅拷贝对基本类型(boolean,char,byte,short,float,double.long)能完成自身的复制,但对于引用类型只对引用地址进行拷贝。 -- 下面我们用一个实例进行验证:
代码语言:javascript
复制
/**
 * 单只牌
 *
 * @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();
    }
}
代码语言:javascript
复制
/**
 * 扑克牌常量定义
 *
 * @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();
    }
}
代码语言:javascript
复制
/**
 * 测试程序
 *
 * @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方法,让引用类型成员实现复制:
代码语言:javascript
复制
/**
 * 整副副扑克牌--自主实现引用变量的复制
 *
 * @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++源码
代码语言:javascript
复制
// 单只牌的类定义
// 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();
}
代码语言:javascript
复制
// 扑克牌类的定义
// 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();
}
代码语言:javascript
复制
// 主测试程序
// 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类,实现拷贝构造方法:
代码语言:javascript
复制
/**
 * 整副副扑克牌--实现拷贝构造方法
 *
 * @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();
    }
}
  • 对测试主程序进行修改:
代码语言:javascript
复制
/**
 * 测试程序
 *
 * @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++程序的拷贝构造函数,实现拷贝构造方法进行对象的复制
  • 通过序列化与反序化手段可实现对象的深拷贝
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-06-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
    • 二、JAVA对象拷贝的实现
      • 四、总结
      相关产品与服务
      文件存储
      文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档