ArrayList之Cloneable 标记性接口

前言

ArrayList集合介绍

动态数组:可调整大小的数组实现

数组结构介绍

增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素位置。

查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。

Cloneable 标记性接口

介绍

一个类实现cloneable接口来指示object.clone()方法,该方法对于该类的实例进行字段的复制是合法的。在不实现cloneable接口的实例上调用对象的克隆方法会导致CloneNotSupportedException被抛出。

简言之:克隆就是依据已经有的数据,创造一份新的完全一样的数据拷贝

Cloneable 源码分析

克隆的前提条件
被克隆对象所在的类必须实现 Cloneable
接口必须重写 clone 方法
clone的基本使用
public class ArrayList_Clone {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("人生就是旅途");
        list.add("也许终点和起点会重合");
        list.add("但是一开始就站在起点等待终点");
        list.add("那么其中就没有美丽的沿途风景和令人难忘的过往");
//调用方法进行克隆
        Object o = list.clone();
        System.out.println(o == list);
        System.out.println(o);
        System.out.println(list);
    }
}
源码分析
public class ArrayList {
    public Object clone() {
        try {
            ArrayList v = (ArrayList) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
}

案例:已知 A 对象的姓名为赵云,年龄30 。由于项目特殊要求需要将该对象的数据复制另外一个对象 B 中,并且此后 A 和 B 两个对象的数据不会相互影响 案例:已知 A 对象的姓名为关羽,年龄30,技能为大刀斩华雄(技能为一个引用数据类型 Skill ),由于项目 特殊要求需要将该对象的数据复制另外一个对象 B 中,并且此后 A 和 B 两个对象的数据不会相互影响

方式一:创建两个对象模拟
1. 准备学生类
public class Student implements Cloneable{
    //姓名
    private String name;
    //年龄
    private Integer age;
    public Student() {
    }
    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
2. 准备测试类
public class Test01 {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建学生对象
        Student stu1 = new Student("关羽", 29);
        //再次创建一个新的学生对象
        Student stu2 = new Student();
        //把stu1对象name的值取出来赋值给stu2对象的name
        stu2.setName(stu1.getName());
        //把stu1对象age的值取出来赋值给stu2对象的age
        stu2.setAge(stu1.getAge());
        System.out.println(stu1 == stu2);
        System.out.println(stu1);
        System.out.println(stu2);
        System.out.println("----此时不管修改哪个对象的内容,stu1和stu2都不会受到影响----");
        stu1.setName("扈三娘");
        System.out.println(stu1);
        System.out.println(stu2);
    }
}
3. 控制台效果
false Student{name=' 关羽', age=29} Student{name='关羽 '', age=29} ----此时不管修改哪
个对象的内容 ,stu1 stu2 都不会受到影响 ---- Student{name=' 赵云 ', age=29} Student{name='关羽 ', age=29}
方式二:使用克隆

浅克隆
1. 定义 Javabean
学生技能类
//学生的技能类
public class Skill implements Cloneable{
    private String skillName;
    public Skill() {
    }
    public Skill(String skillName) {
        this.skillName = skillName;
    }
    public String getSkillName() {
        return skillName;
    }
    public void setSkillName(String skillName) {
        this.skillName = skillName;
    }
    @Override
    public String toString() {
        return "Skill{" +
                "skillName='" + skillName + '\'' +
                '}';
    }
}
学生类
public class Student implements Cloneable{
    //姓名
    private String name;
    //年龄
    private Integer age;
    //技能
    private Skill skill;
    public Student() {
    }
    public Student(String name, Integer age, Skill skill) {
        this.name = name;
        this.age = age;
        this.skill = skill;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Skill getSkill() {
        return skill;
    }
    public void setSkill(Skill skill) {
        this.skill = skill;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skill=" + skill +
                '}';
    }
    //浅克隆
    @Override
    public Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }
}
2. 定义测试类
public class Test01 {
    public static void main(String[] args) throws CloneNotSupportedException {
        //用自定义对象演示 深浅拷贝
        Skill skill = new Skill("大刀斩华雄");
        Student s = new Student("关羽",31,skill);
        //调用clone方法进行克隆
        Student obj = s.clone();
        //比较地址
        System.out.println(s == obj);
        System.out.println("被克隆对象: "+s);
        System.out.println("克隆出来的对象: "+obj);
        System.out.println("----华丽的分割线----");
        //克隆之后,更改skill中的数据
        skill.setSkillName("浑身是胆");
        //更改克隆后对象的数据
        obj.setName("赵云");
        obj.setAge(19);
        System.out.println("被克隆对象: "+s);
        System.out.println("克隆出来的对象: "+obj);
    }
}
控制台效果
ArrayList之Cloneable 标记性接口_第1张图片
存在的问题 : 基本数据类型可以达到完全复制,引用数据类型则不可以
原因 : 在学生对象 s 被克隆的时候,其属性 skill( 引用数据类型 ) 仅仅是拷贝了一份引用,因此当 skill 的值发生改 变时,被克隆对象s 的属性 skill 也将跟随改变
深克隆
1. 定义 Javabean
学生技能类
/**
 * @author itheima
 * @since JDK 1.8
 */
public class Skill implements Cloneable{
    private String skillName;
    public Skill() {
    }
    public Skill(String skillName) {
        this.skillName = skillName;
    }
    public String getSkillName() {
        return skillName;
    }
    public void setSkillName(String skillName) {
        this.skillName = skillName;
    }
    @Override
    public String toString() {
        return "Skill{" +
                "skillName='" + skillName + '\'' +
                '}';
    }
    //重写克隆方法,将权限修饰符改成public
    @Override
    public Skill clone() throws CloneNotSupportedException {
        return (Skill) super.clone();
    }
}
学生类
/**
 * @author itheima
 * @since JDK 1.8
 */
public class Student implements Cloneable{
    //姓名
    private String name;
    //年龄
    private Integer age;
    //技能
    private Skill skill;
    public Student() {
    }
    public Student(String name, Integer age, Skill skill) {
        this.name = name;
        this.age = age;
        this.skill = skill;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Skill getSkill() {
        return skill;
    }
    public void setSkill(Skill skill) {
        this.skill = skill;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skill=" + skill +
                '}';
    }
    //深克隆
    @Override
    public Student clone() throws CloneNotSupportedException {
//调用超类Object中方法clone进行对象克隆,得到一个新的学生对象
        Student newStu = (Student) super.clone();
//调用学生类其属性skill的clone方法,对属性进行克隆
        Skill s = this.skill.clone();
//再将克隆的Skill设置给克隆出来的学生对象
        newStu.setSkill(s);
//返回克隆出来的学生对象
        return newStu;
    }
}
2. 定义测试类
测试类
public class Test01 {
    public static void main(String[] args) throws CloneNotSupportedException {
//用自定义对象演示 深浅拷贝
        Skill skill = new Skill("倒拔垂杨柳");
        Student s = new Student("鲁智深", 31, skill);
//调用clone方法进行克隆
        Student obj = s.clone();
//比较地址
        System.out.println(s == obj);
        System.out.println("被克隆对象: " + s);
        System.out.println("克隆出来的对象: " + obj);
        System.out.println("----华丽的分割线----");
//克隆之后,更改skill中的数据
        skill.setSkillName("荷花酒");
//更改克隆后对象的数据
        obj.setName("扈三娘");
        obj.setAge(19);
        System.out.println("被克隆对象: " + s);
        System.out.println("克隆出来的对象: " + obj);
    }
}

ArrayList之Cloneable 标记性接口_第2张图片

你可能感兴趣的:(Java集合,数据结构,java,开发语言)