zoukankan      html  css  js  c++  java
  • Java学习笔记--对象克隆

    转自:Edward_qing_Lee 的专栏  

    http://blog.csdn.net/edward_qing_lee/article/details/8249102

    一、java 方法参数 理解:

    方法参数 可理解为: 对于输入的实参 进行了一份拷贝,  (1) 若方法参数为基本类型,则在栈内存中开辟新的空间,所有的方法体内部的操作都是针对这个拷贝的操作,并不会影响原来输入实参的值 (2)若方法参数为引用类型,该拷贝与输入实参指向了同一个对象,方法体内部对于对象的操作,都是针对的同一个对象。

    另外,除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递"。

    下面举例说明:   

    package com.lqh.test;
    
    import java.util.*;
    
    public class HashtableAdd {
    	public static void main(String[] args) {
    		Hashtable<String, StringBuffer> ht = new Hashtable<String, StringBuffer>();
    		StringBuffer sb = new StringBuffer();
    		sb.append("abc,");
    		ht.put("1", sb);
    		sb.append("def,");
    		ht.put("2", sb);
    		sb.append("mno,");
    		ht.put("3", sb);
    		sb.append("xyz.");
    		ht.put("4", sb);
    
    		int numObj = 0;
    		Enumeration it = ht.elements();
    		while (it.hasMoreElements()) {
    			System.out.print("get StringBufffer " + (++numObj)
    					+ " from Hashtable: ");
    			System.out.println(it.nextElement());
    		}
    	}
    }
    上面的例子的实际输出的结果是:

     

    get StringBufffer 1 from Hashtable: abc,def,mno,xyz.  
    get StringBufffer 2 from Hashtable: abc,def,mno,xyz.  
    get StringBufffer 3 from Hashtable: abc,def,mno,xyz.  
    get StringBufffer 4 from Hashtable: abc,def,mno,xyz.  

    分析:向Hashtable传递 StringBuffer对象是只传递了这个StringBuffer对象的引用!每一次向Hashtable表中put一次 StringBuffer,并没有生成新的StringBuffer对象,只是在Hashtable表中又放入了一个指向同一StringBuffer对象的引用而已。对Hashtable表存储的任何一个StringBuffer对象(更确切的说应该是对象的引用)的改动,实际上都是对同一个 "StringBuffer"的改动。所以Hashtable并不能真正存储能对象,而只能存储对象的引用。也应该知道这条原则对与Hashtable相似的Vector, List, Map, Set等都是一样的。

    二、Java Clone( )介绍

    顾名思义,clone方法的含义就是克隆出一个一模一样的对象,这样是有两个对象的。

    实现clone方法的步骤()

    (1)实现Cloneable接口

    (2)重载Object类中的clone()方法,重载时需定义为public

    (3)在重载方法中,调用super.clone()

    例如:

    class CloneClass implements Cloneable {
        public int aInt;
    
        public Object clone() {
            CloneClass o = null;
            try {
                o = (CloneClass) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return o;
        }
    }
    
     
    解释: 

    (1)clone()方法是定义在java.lang.Object类中,该方法是一个protected的方法,所以重载时要把clone()方法的属性设置为public,这样其它类才能调用这个clone类的clone()方法

    (2)实现Cloneable接口:Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Objectclone()法(也就是调用了super.Clone()方法),那么Objectclone()方法就会抛出 CloneNotSupportedException异常。

     

    三、浅克隆与深克隆

    所谓浅克隆就是说被克隆的对象各个域都是基本类型,而不存在引用类型。如有存在引用类型的域,则需要进行深克隆。深克隆就是在重载clone()方法中,要对其引用类型的域也进行克隆。如下例:

    package com.lqh.clone;
    
    public class Address implements Cloneable{
    	
    	private String state;
    	private String province;
    	private String city;
    	
    	public String getState() {
    		return state;
    	}
    	public void setState(String state) {
    		this.state = state;
    	}
    	public String getProvince() {
    		return province;
    	}
    	public void setProvince(String province) {
    		this.province = province;
    	}
    	public String getCity() {
    		return city;
    	}
    	public void setCity(String city) {
    		this.city = city;
    	}
    	@Override
    	public String toString() {
    		StringBuilder sb = new StringBuilder();
    		sb.append("国家:" + state + ", ");
    		sb.append("省:" + province + ", ");
    		sb.append("市:" + city);
    		return sb.toString();
    	}
    	public Address(String state, String province, String city) {
    		super();
    		this.state = state;
    		this.province = province;
    		this.city = city;
    	}
    	
    	@Override
    	public Address clone() {
    		Address address = null;
    		try {
    			address = (Address) super.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return address;
    	}
    }
    package com.lqh.clone;
    
    public class Employee implements Cloneable{
    	private String name;
    	private int age;
    	private Address address;
    	
    	public Employee(String name, int age, Address address) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.address = address;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public Address getAddress() {
    		return address;
    	}
    	public void setAddress(Address address) {
    		this.address = address;
    	}
    	@Override
    	public Employee clone(){
    		Employee employee = null;
    		try {
    			employee = (Employee) super.clone();  
    			employee.address = address.clone();  //对引用类型的域进行克隆
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return employee;
    	}
    	@Override
    	public String toString() {
    		StringBuilder sb = new StringBuilder();
    		sb.append("姓名:" + name + ",");
    		sb.append("年龄:" + age+ ", ");
    		sb.append("地址:" + address);
    		return sb.toString();
    	}
    	
    	public static void main(String[] args) {
    		System.out.println("克隆之前:");
    		Address address = new Address("中国", "吉林", "长春");
    		Employee employee1 = new Employee("明日科技", 12, address);
    		System.out.println("员工1信息:" + employee1 );
    		
    		Employee employee2 = employee1.clone();
    		
    		employee2.getAddress().setState("中国");
    		employee2.getAddress().setProvince("四川");
    		employee2.getAddress().setCity("成都");
    		
    		System.out.println("克隆之后:");
    		System.out.println("员工2信息:" + employee2);
    		System.out.println("员工1信息:" + employee1);
    	}
    }
    

       Object类中clone()方法产生的过程是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,若不使用深克隆,即不对引用类型的域进行克隆,会导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。

      不是所有的类都能实现深度clone的,StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个 final类,这也是说我们也不能用继承的办法间接实现StringBuffer的clone。如果一个类中包含有StringBuffer类型对象或和 StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是 SringBuffer对象,而且变量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原来的是:o.unCA = (UnCloneA)unCA.clone();

    四、Clone中String和StringBuffer的区别

    下面的例子中包括两个类,CloneC类包含一个String类型变量和一个StringBuffer类型变量,并且实现了clone()方法。在 StrClone类中声明了CloneC类型变量c1,然后调用c1的clone()方法生成c1的拷贝c2,在对c2中的String和 StringBuffer类型变量用相应的方法改动之后打印结果:

    package clone;
    
    class CloneC implements Cloneable {
        public String str;
        public StringBuffer strBuff;
    
        public Object clone() {
            CloneC o = null;
            try {
                o = (CloneC) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return o;
        }
    }
    
    public class StrClone {
        public static void main(String[] a) {
            CloneC c1 = new CloneC();
            c1.str = new String("initializeStr");
            c1.strBuff = new StringBuffer("initializeStrBuff");
            System.out.println("before clone,c1.str = " + c1.str);
            System.out.println("before clone,c1.strBuff = " + c1.strBuff);
    
            CloneC c2 = (CloneC) c1.clone();
            c2.str = c2.str.substring(0, 5);
            c2.strBuff = c2.strBuff.append(" change strBuff clone");
            System.out.println("=================================");
            System.out.println("after clone,c1.str = " + c1.str);
            System.out.println("after clone,c1.strBuff = " + c1.strBuff);
            System.out.println("=================================");
            System.out.println("after clone,c2.str = " + c2.str);
            System.out.println("after clone,c2.strBuff = " + c2.strBuff);
        }
    }
    
    /* RUN RESULT
    before clone,c1.str = initializeStr
    before clone,c1.strBuff = initializeStrBuff
    =================================
    after clone,c1.str = initializeStr
    after clone,c1.strBuff = initializeStrBuff change strBuff clone
    =================================
    after clone,c2.str = initi
    after clone,c2.strBuff = initializeStrBuff change strBuff clone
    *
    */ 
    

      打印的结果可以看出,String类型的变量好象已经实现了深度clone,因为对c2.str的改动并没有影响到c1.str!难道Java把 Sring类看成了基本数据类型?其实不然,这里有一个小小的把戏,秘密就在于c2.str = c2.str.substring(0,5)这一语句!

      实质上,在clone的时候c1.str与c2.str仍然是引用,而且都指向了同一个 String对象。但在执行c2.str = c2.str.substring(0,5)的时候,它作用相当于生成了一个新的String类型,然后又赋回给c2.str。这是因为String被 Sun公司的工程师写成了一个不可更改的类(immutable class),在所有String类中的函数都不能更改自身的值。

     

    参考:http://www.blogjava.net/jerry-zhaoj/archive/2009/10/14/298141.html 

  • 相关阅读:
    ASP.NET Core 中间件 中间件(Middleware)和过滤器(Filter)的区别
    ASP.NET Core 中间件详解及项目实战
    开源项目1:某大学校友管理系统
    web安全浅析
    p2p网贷平台设计简析
    一些常见的并且比较难解决的设计问题
    CentOS 新增swap交换空间
    策略模式
    Centos6.4 本地yum源配置
    Linux(CentOs6.4)安装Git
  • 原文地址:https://www.cnblogs.com/gnivor/p/4324454.html
Copyright © 2011-2022 走看看