• Effective Java 之-----谨慎的覆盖clone方法


    1、概述

      如果clone方法返回一个由构造器创建的对象,它就得到有错误的类。因此,如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone得到的对象。
    如果类的所有超类都遵循这条规则,那么调用super.clone最终会调用Object的clone方法,从而创建出正确的实例。这种机制类似于自动的构造器调用链,只不过它不是强制要求的。

    2、示例

    需求: 为HashTable编写一个clone方法,它内部数据包含一个HashTable数组,每个HashTable都指向键值对列表的第一个项,如果table是空的,返回null。

    代码实现如下:

    public class HashTable implements Cloneable {
        private Entry[] buckets;
        
        private static class Entry{
            Object key;
            Object value;
            Entry next;
    
            Entry(Object key, Object value, Entry next) {
                this.key = key;
                this.value = value;
                this.next = next;
            }
        }
        //......
    }
    
    //递归clone这个HashTable数组,如下:
    public HashTable clone(){
        HashTable result = new HashTable();
        try {
            result = (HashTable) super.clone();
            result.buckets = buckets.clone();
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return result;
    }
    

      虽然被clone对象有它自己的HashTable数组,但这个数组引用的链表与原始对象是一样的,从而很容易引起clone对象和原始对象中的不确定的行为,为了修正这个问题,必须单独的拷贝并组成每个桶的链表,实现如下:

    public class HashTable1 implements Cloneable {
        private Entry[] buckets;
    
        private static class Entry{
            Object key;
            Object value;
            Entry next;
    
            Entry(Object key, Object value, Entry next) {
                this.key = key;
                this.value = value;
                this.next = next;
            }
            
           Entry deepCopy(){
        		Entry result = new Entry(key,value,next);
        		for(Entry p=result;p.next !=null; p=p.next){
            		p.next=new Entry(p.next.key,p.next.value,p.next.next);
       		}
        		return result;
    	}
        }
        //......
    
        public HashTable1 clone(){
            HashTable1 result = new HashTable1();
            try {
                result = (HashTable1) super.clone();
                result.buckets = new Entry[buckets.length];
                for(int i=0;i<buckets.length;i++){
                    result.buckets[i] = buckets[i].deepCopy();
                }
                return result;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return result;
        }
    }
    

      步骤:先调用super.clone方法,然后把结果对象中的所有域都设置称他们的空白状态,然后调用高层的方法来重新产生对象的状态。

    3、总结

      简而言之,所有实现了Cloneable接口的类都应该用一个公有的方法覆盖clone,此方法首先调用super.clone,然后修正任何需要修正的域。一般情况下,这意味着要拷贝任何包含内部“深层结构”的可变对象,并用指向新对象的引用代替原来指向这些对象的引用。

  • 相关阅读:
    (九)SpringBoot之错误处理
    (九)SpringBoot之使用jsp
    (八)SpringBoot之freeMarker基本使用
    (七)freemarker的基本语法及入门基础
    (六)Spring Boot之日志配置-logback和log4j2
    (五)Spring Boot之@RestController注解和ConfigurationProperties配置多个属性
    (四)Spring Boot之配置文件-多环境配置
    HashPayloadPcapReader
    Wireshark理解TCP乱序重组和HTTP解析渲染
    Centos定时启动和清除任务
  • 原文地址:https://www.cnblogs.com/hunterCecil/p/5716073.html
Copyright © 2020-2023  润新知