面试题——浅克隆深克隆

类的成员变量分为值类型和引用类型。

浅克隆

浅克隆是将原型对象的成员变量为值类型复制一份给克隆对象,而成员变量为引用对象的引用地址复制给克隆对象,说白了就是共享引用对象。
在这里插入图片描述

深克隆

深克隆就是原型对象不管成员变量是什么类型都要复制一份克隆对象,原型对象和克隆对象是两个完全独立的对象。
在这里插入图片描述

JAVA实现克隆条件

1、实现Cloneable接口。
2、重写Ojbect类中clone()方法。

Object.clone()几点特点

1、对于所有对象来说,x.clone() !=x 应当返回 true,因为克隆对象与原对象不是同一个对象;
2、对于所有对象来说,x.clone().getClass() == x.getClass() 应当返回 true,因为克隆对象与原对象的类型是一样的;
3、对于所有对象来说,x.clone().equals(x) 应当返回 true,因为使用 equals 比较时,它们的值都是相同的。

Arrays.copyOf()的克隆方式

@Data
public class Student {
    private int id;
    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static void main(String[] args) {
        Student[] students = {new Student(1,"哈哈哈")};
        Student[] students1 = Arrays.copyOf(students,students.length);
        students1[0].setId(2);
        System.out.println("原型对象:"  +  students[0].getId());
        System.out.println("克隆对象:"  + students1[0].getId() );
    }
}

在这里插入图片描述
结果显示,修改克隆对象的值,原型对象的值也随着改变。

数组属于比较特殊的引用类型, Arrays.copyOf()只是把引用地址复制了一份给克隆对象。 Arrays.copyOf()属于浅克隆。资源共享。

浅克隆的实现

Object的clone()可以直接实现浅克隆。

深克隆的实现

深克隆可以有很多种方式进行实现:

  • 所有对象都实现克隆方法;

  • 通过构造方法实现深克隆;

  • 使用 JDK 自带的字节流实现深克隆;

  • 使用第三方工具实现深克隆,比如 Apache Commons Lang;

  • 使用 JSON 工具类实现深克隆,比如 Gson、FastJSON 等。

1、所有对象都实现克隆方法;
import lombok.Data;

public class CloneExample1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address(1,"广州");
        People people = new People(1,"Java", address);
        //  克隆
        People people1 = people.clone();
        //  修改原型
        people1.address.setCity("深圳");
        //  输出比对结果
        System.out.println("原型对象:" + people.address.getCity());
        System.out.println("克隆对象:" + people1.address.getCity());
    }


    //  用户类
    @Data
    static class People implements Cloneable{
        private Integer id;
        private String name;
        private Address address;

        public People(Integer id, String name, Address address) {
            this.id = id;
            this.name = name;
            this.address = address;
        }

        @Override
        protected People clone() throws CloneNotSupportedException {
            People people = (People)super.clone();
            System.out.println(this.address.clone());
            people.setAddress(this.address.clone());
            return people;
        }
    }


    //  地址类
    @Data
    static class Address implements Cloneable{
        private Integer id;
        private String city;
        public Address(Integer id, String city) {
            this.id = id;
            this.city = city;
        }

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

}

在这里插入图片描述
让所有引用对象都是实现克隆,从而实现克隆对象实现深克隆。

2、通过构造方法实现深克隆;
 // 调用构造函数克隆对象
        People p2 = new People(p1.getId(), p1.getName(),
                new Address(p1.getAddress().getId(), p1.getAddress().getCity()));

创建克隆对象的时候直接赋值,是一种笨方法。

3、使用 JDK 自带的字节流实现深克隆;
import lombok.Data;
import java.io.*;

public class CloneExample2 {

    public static void main(String[] args) {
        Address address = new Address(1,"广州");
        People people1 = new People(1,"Java", address);
        //  通过字节流实现克隆
        People people2 = (People) StreamClone.clone(people1);
        //  修改原型
        people1.getAddress().setCity("深圳");
        //  输出比对结果
        System.out.println("原型对象:" + people1.address.getCity());
        System.out.println("克隆对象:" + people2.address.getCity());
    }


    static class StreamClone{
        /**
          * 通过字节流实现克隆
          */
        public static <T extends Serializable > T clone(People people) {
            T cloneObj = null;
            try {
                ByteArrayOutputStream bo = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bo);
                oos.writeObject(people);
                oos.close();
                //  分配内存,写入原型对象,生成新对象
                ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
                ObjectInputStream oi = new ObjectInputStream(bi);
                //  返回生成的新对象
                cloneObj = (T) oi.readObject();
                oi.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return cloneObj;
        }
    }
    //  用户类
    @Data
    static class People implements Serializable{
        private Integer id;
        private String name;
        private Address address;
        public People(Integer id, String name, Address address) {
            this.id = id;
            this.name = name;
            this.address = address;
        }
    }


    //  地址类
    @Data
    static class Address implements Serializable {
        private Integer id;
        private String city;
        public Address(Integer id, String city) {
            this.id = id;
            this.city = city;
        }
    }
}

在这里插入图片描述

通过字节流将原型对象的值写进内存,然后再从内存中读取数据进行克隆。因为是从内存中读取值所以不存在共享内存地址。
注意:原型对象以及引用对象都要实现Serializable 接口,标识自己可以被序列化,否则会抛出异常 (java.io.NotSerializableException)。

4、使用第三方工具实现深克隆,比如 Apache Commons Lang;
import org.apache.commons.lang3.SerializationUtils;
import lombok.Data;
import java.io.*;

/**
  * 深克隆实现方式四:通过 apache.commons.lang 实现
  */
public class CloneExample3 {

    public static void main(String[] args) {
        Address address = new Address(1,"广州");
        People people1 = new People(1,"Java", address);
        //  通过字节流实现克隆
        People people2 = (People) SerializationUtils.clone(people1);
        //  修改原型
        people1.getAddress().setCity("深圳");
        //  输出比对结果
        System.out.println("原型对象:" + people1.address.getCity());
        System.out.println("克隆对象:" + people2.address.getCity());
    }



    //  用户类
    @Data
    static class People implements Serializable{
        private Integer id;
        private String name;
        private Address address;
        public People(Integer id, String name, Address address) {
            this.id = id;
            this.name = name;
            this.address = address;
        }
    }


    //  地址类
    @Data
    static class Address implements Serializable {
        private Integer id;
        private String city;
        public Address(Integer id, String city) {
            this.id = id;
            this.city = city;
        }
    }
}

在这里插入图片描述

可以看出此方法和第三种实现方式类似,都需要实现 Serializable 接口,都是通过字节流的方式实现的,只不过这种实现方式是第三方提供了现成的方法,让我们可以直接调用。

5、使用 JSON 工具类实现深克隆,比如 Gson、FastJSON 等。

import lombok.Data;
import com.google.gson.Gson;
import java.io.Serializable;

public class CloneExample4 {

    public static void main(String[] args) {
        Address address = new Address(1,"广州");
        People people1 = new People(1,"Java", address);
        //  通过字节流实现克隆
        Gson gson = new Gson();
        People people2 = gson.fromJson(gson.toJson(people1), People.class);
        //  修改原型
        people1.getAddress().setCity("深圳");
        //  输出比对结果
        System.out.println("原型对象:" + people1.address.getCity());
        System.out.println("克隆对象:" + people2.address.getCity());
    }



    //  用户类
    @Data
    static class People implements Serializable {
        private Integer id;
        private String name;
        private Address address;
        public People(Integer id, String name, Address address) {
            this.id = id;
            this.name = name;
            this.address = address;
        }
    }


    //  地址类
    @Data
    static class Address implements Serializable {
        private Integer id;
        private String city;
        public Address(Integer id, String city) {
            this.id = id;
            this.city = city;
        }
    }
}

在这里插入图片描述
这种方式就是将原型对象装成JSON格式数据,再有JSON格式数据生成克隆对象,借助第三方数据存储,进行克隆。

本文是根据下面链接进行学习编写:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=59#/detail/pc?id=1767

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是对大数据面试题——spark面试题(一)的回答: 1. 什么是Spark?它与Hadoop有什么区别? Spark是一个快速、通用、可扩展的大数据处理引擎,它可以在内存中进行数据处理,因此比Hadoop更快。与Hadoop相比,Spark的优点在于它可以在内存中进行数据处理,因此速度更快,而且它支持更多的数据处理方式,例如流处理、图形处理等。 2. Spark的核心组件有哪些? Spark的核心组件包括Spark Core、Spark SQL、Spark Streaming、MLlib和GraphX。 3. 什么是RDD?它有哪些特点? RDD是Spark中的一个基本概念,它代表一个不可变的分布式数据集合。RDD具有以下特点: - 可以在内存中进行计算,因此速度快; - 支持多种操作,例如map、reduce、filter等; - 可以进行持久化,以便在后续计算中重复使用。 4. Spark中的map和flatMap有什么区别? map和flatMap都是RDD中的转换操作,它们的区别在于: - map操作对每个元素进行转换,返回一个新的元素; - flatMap操作对每个元素进行转换,返回一个包含多个元素的序列。 5. 什么是Spark的shuffle操作? Spark的shuffle操作是指将数据重新分区的操作,它通常发生在reduce操作之前。Shuffle操作会将数据从多个节点上收集到一个节点上,然后重新分区,以便进行后续的计算。 6. Spark中的cache和persist有什么区别? cache和persist都是将RDD持久化到内存中,以便在后续计算中重复使用。它们的区别在于: - cache操作默认将数据持久化到内存中,而persist操作可以指定将数据持久化到内存、磁盘或者其他存储介质中; - cache操作是persist操作的一种简化形式,它默认将数据持久化到内存中,并且只能持久化到内存中。 7. Spark中的reduceByKey和groupByKey有什么区别? reduceByKey和groupByKey都是对键值对RDD进行操作的函数,它们的区别在于: - reduceByKey操作会在每个分区内先进行本地聚合,然后再进行全局聚合,因此效率更高; - groupByKey操作会将所有的键值对都进行网络传输,然后在一个节点上进行聚合,因此效率较低。 8. Spark中的broadcast变量有什么作用? broadcast变量是一种只读的变量,它可以在所有节点上共享,以便在计算过程中使用。使用broadcast变量可以避免在网络上传输大量的数据,从而提高计算效率。 9. 什么是Spark的checkpoint操作? Spark的checkpoint操作是将RDD持久化到磁盘上,以便在后续计算中重复使用。与cache和persist不同的是,checkpoint操作会将数据持久化到磁盘上,以便在内存不足时可以从磁盘上恢复数据。 10. Spark中的Task是什么? Task是Spark中的一个基本概念,它代表一个可以在一个节点上执行的计算任务。Spark将一个RDD分成多个分区,每个分区对应一个Task,这些Task可以并行执行,以提高计算效率。 ### 回答2: 今天我们来讨论一下关于Spark大数据面试的一些常见问题。Spark是一种基于Hadoop的开源计算系统,它能够快速处理大规模数据,并且支持多种编程语言,包括Java、Scala和Python等。以下是一些Spark面试题及其答案: 1. Spark有哪几种部署模式? Spark有三种部署模式,分别是本地模式、集群模式和分布式模式。本地模式指的是在本地运行Spark应用程序,不需要连接到外部计算机。集群模式指的是单个Spark集群环境,它由一组Spark节点组成,可以在数据中心或云中运行。分布式模式指的是使用多个Spark集群并行处理大规模数据。 2. Spark和Hadoop的区别是什么? Spark和Hadoop都是处理大规模数据的工具,但它们有一些区别。首先,Spark处理数据速度快,因为它将数据存储在内存中,而Hadoop则将数据存储在磁盘中。其次,Spark支持更多的编程语言,包括Java、Scala和Python等,而Hadoop只支持Java。此外,Spark具有更好的机器学习和图形处理功能,可以更好地支持大规模数据分析。 3. Spark的RDD是什么? RDD是Spark中重要的概念,全称为Resilient Distributed Dataset。它是一个不可变的分布式数据集合,可以分区存储在不同节点上,并且每个分区都可以在并行处理中进行处理。RDD支持两种操作,即转化操作和行动操作。转化操作将一个RDD转换为另一个RDD,而行动操作返回一个结果或将结果输出至外部系统。 4. Spark的优化技术有哪些? Spark优化技术包括数据本地化、共享变量、宽依赖和窄依赖、缓存和持久化,以及数据分区等技术。数据本地化将数据存储在尽可能接近计算节点的位置,以减少网络传输的开销。共享变量将常用的变量通过广播或累加器的方式在节点中共享,从而减少网络传输量。宽依赖和窄依赖指的是在转化操作中RDD之间的依赖关系,窄依赖表示每个父分区最多与一个子分区有关联,而宽依赖则表示多个子分区可能与多个父分区关联。缓存和持久化技术可将RDD保存在内存中,从而加速访问速度。数据分区可以将数据划分为较小的块进行并行处理。 5. Spark Streaming是什么? Spark Streaming是Spark的一个扩展模块,它支持实时数据流处理。Spark Streaming可以将实时数据流以微批次方式处理,每个批次的数据处理平均耗时只有几秒钟。Spark Streaming可以将数据存储在内存或磁盘中,同时支持多种数据源和数据输出方式。 以上是关于Spark大数据面试题的一些回答,希望能够对大家有所帮助。如果你想入学习Spark和大数据处理技术,可以考虑参加相关的培训课程或在线课程。 ### 回答3: Spark是一个分布式计算框架,它可以使大规模数据处理更加高效和便捷。因此,在企业招聘大数据领域的人才时,对Spark的技术能力要求越来越高。以下是Spark面试题的回答: 1. Spark有哪些组件? Spark框架由三个核心组件组成:Spark Core、Spark SQL和Spark Streaming。此外,还有Spark MLlib、Spark GraphX、Spark R等个别不同的子组件。 2. 什么是RDD?与Dataframe有什么区别? RDD(弹性分布式数据集)是Spark的核心数据抽象,是不可变的分布式对象集合。RDD可以从文件中读取数据、从内存中读取数据、并行修改数据等。而Dataframe和RDD类似,但是Dataframe更加强大,因为它是带有结构化的RDD。Dataframe在处理大规模结构化数据时非常有效和便捷。 3. Spark如何处理缺失数据? Spark提供了两种处理缺失数据的方法:第一种是使用DataFrame API中的na函数,可以删除或替换缺失值;第二种是使用MLlib中的Imputer类,可以将缺失值替换为均值或中位数。 4. 什么是Spark的任务(task)? 一个任务是Spark作业中的最小执行单位。Spark集群上的作业被划分为多个任务,这些任务可以并行执行。 5. Spark的shuffle操作是什么?它为什么是昂贵的? Spark的shuffle操作是将一组数据重新分配到不同计算节点上的操作。Shuffle操作可能会导致大量数据的磁盘写入、网络传输和数据重组,这些都是非常昂贵的操作。因此,它在Spark集群中是一个相当昂贵的操作。 6. Spark中的Partition有什么作用? Partition是Spark中的数据划分单位。它可以将数据分成多个块并对每个块进行处理。Partition 可以提高 Spark 的并行度和运行效率,因为它可以将大规模数据分成多个小块,并在集群的多个计算节点上并行处理数据。 总而言之,Spark是大数据领域中使用最广泛的计算引擎之一,其技术理念和应用场景非常广泛。对于求职者而言,掌握 Spark 的基本概念和技术特点,提高对 Spark 的理解和应用能力,将有助于更好地处理和分析大规模数据集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值