精华内容
下载资源
问答
  • 1.为什么有拷贝? 因为new一个对象太占用资源,当要复制大量对象的时候用拷贝实现的方式很有优势,很快。 2.浅拷贝与深拷贝 浅拷贝是指对一个类进行拷贝是,会对基本数据类型进行值...深拷贝基本数据类型拷贝同...

    1.为什么有拷贝?

         因为new一个对象太占用资源,当要复制大量对象的时候用拷贝实现的方式很有优势,很快。

    2.浅拷贝与深拷贝

        浅拷贝是指对一个类进行拷贝是,会对基本数据类型进行值传递(string也是基本类型),而对于类属性中对象类型变量(包括数组)会让他们直接指向同一个内存地址,所以修改其中一个的值会影响到拷贝对象中的值,所以具有局限性

        深拷贝基本数据类型拷贝同浅拷贝一样,引用类型变量会直接拷贝,而不是让他们指向同一地址。

    具体实现方式:1.通过Object中的clone方法; 2.自己通过构造函数方式实现;3.序列化方法实现

    直接赋值

    直接赋值是我们最常用的方式,在我们代码中的体现是Persona = new Person();Person b = a,是一种简单明了的方式,但是它只是拷贝了对象引用地址而已,并没有在内存中生成新的对象,我们可以通过下面这个例子来证明这一点

    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    /**
    * @desc: demo
    * @name: SimplePerson.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:21:28
    * @history:
    * @version: v1.0
    */
    
    public class SimplePerson {
    
    	// 姓名
        private String name;
        // 年龄
        private int age;
        // 邮件
        private String email;
        // 房屋描述
        private String houseDesc;
        
    	public SimplePerson(String name, int age, String email, String houseDesc) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.email = email;
    		this.houseDesc = houseDesc;
    	}
    	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 String getEmail() {
    	
    		return email;
    	}
    	public void setEmail(String email) {
    	
    		this.email = email;
    	}
    	public String getHouseDesc() {
    	
    		return houseDesc;
    	}
    	public void setHouseDesc(String houseDesc) {
    	
    		this.houseDesc = houseDesc;
    	}
    	@Override
    	public String toString() {
    		return "{\"name\":\"" + name + "\", \"age\":\"" + age + "\", \"email\":\"" + email + "\", \"houseDesc\":\""
    				+ houseDesc + "\"}";
    	}
    }
    
    

    测试:

    // 初始化一个对象
    		SimplePerson tompai = new SimplePerson("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 直接赋值
    		SimplePerson tompai1 = tompai;
    		// 改变 person1 的属性值
    		tompai1.setName("not tompai");
    		System.out.println("person对象:" + tompai);
    		System.out.println("person直接赋值对象:" + tompai1);

    我们将 person 对象复制给了 person1 对象,我们对 person1 对象的 name 属性进行了修改,并未修改 person 对象的name 属性值,但是我们最后发现 person 对象的 name 属性也发生了变化,其实不止这一个值,对于其他值也是一样的,所以这结果证明了我们上面的结论:直接赋值的方式没有生产新的对象,只是生新增了一个对象引用,直接赋值在 Java 内存中的模型大概是这样的

    浅拷贝
    浅拷贝也可以实现对象克隆,从这名字你或许可以知道,这种拷贝一定存在某种缺陷,是的,它就是存在一定的缺陷,先来看看浅拷贝的定义:如果原型对象的成员变量是值类型,将复制一份给克隆对象,也就是说在堆中拥有独立的空间;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。换句话说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。 可能你没太理解这段话,那么我们在来看看浅拷贝的通用模型:

     

    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    /**
    * @desc: demo
    * @name: PersonHouse.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:33:33
    * @history:
    * @version: v1.0
    */
    
    public class PersonHouse {
    
    	private String desc;
    
    	public String getDesc() {
    	
    		return desc;
    	}
    
    	public void setDesc(String desc) {
    	
    		this.desc = desc;
    	}
    
    	@Override
    	public String toString() {
    		return "{\"desc\":\"" + desc + "\"}";
    	}
    	
    }
    
    
    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    /**
    * @desc: demo
    * @name: FullPerson.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:34:52
    * @history:
    * @version: v1.0
    */
    
    public class FullPerson implements Cloneable{
    
    	// 姓名
        private String name;
        // 年龄
        private int age;
        // 邮件
        private String email;
        // 房屋描述
        
    	// 将原来的 string desc 变成了 PersonDesc 对象,这样 personDesc 就是引用类型
        private PersonHouse personHouse;
    
    	public FullPerson(String name, int age, String email, String desc) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.email = email;
    		this.personHouse = new PersonHouse();
            this.personHouse.setDesc(desc);
    	}
    	
    	public void setDesc(String desc) {
    		 this.personHouse.setDesc(desc);
    	}
    	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 String getEmail() {
    	
    		return email;
    	}
    
    	public void setEmail(String email) {
    	
    		this.email = email;
    	}
    
    	public PersonHouse getPersonHouse() {
    	
    		return personHouse;
    	}
    
    	public void setPersonHouse(PersonHouse personHouse) {
    	
    		this.personHouse = personHouse;
    	}
    
    	@Override
    	public String toString() {
    		return "{\"name\":\"" + name + "\", \"age\":\"" + age + "\", \"email\":\"" + email + "\", \"personHouse\":\""
    				+ personHouse + "\"}";
    	}
    	
    	@Override
        public Object clone() {
    		Object object=null;
            try {
    			 object=super.clone();
    		} catch (CloneNotSupportedException e) {
    			//TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            return object;
        }	
    }
    
    

    测试:

    // 浅拷贝2 Clonenable实现
    		// 初始化一个对象
    		FullPerson person2 = new FullPerson("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 复制对象
    		FullPerson person3 = (FullPerson) person2.clone();
    		// 改变 person1 的属性值
    		person3.setName("clone tompai");
    		// 修改 person age 的值
    		person3.setAge(22);
    		person3.setDesc("i am Not tompai");
    		System.out.println("person对象:" + person2);
    		System.out.println("person浅拷贝2对象:" + person3);

    深拷贝

    深拷贝也是对象克隆的一种方式,相对于浅拷贝,深拷贝是一种完全拷贝,无论是值类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象,简单点说就是拷贝对象和被拷贝对象没有任何关系,互不影响。深拷贝的通用模型如下:

    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    /**
    * @desc: demo
    * @name: DeepPersonHouse.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:49:48
    * @history:
    * @version: v1.0
    */
    
    public class DeepPersonHouse implements Cloneable{
    
    	private String desc;
    
    	public String getDesc() {
    	
    		return desc;
    	}
    
    	public void setDesc(String desc) {
    	
    		this.desc = desc;
    	}
    
    	@Override
    	public String toString() {
    		return "{\"desc\":\"" + desc + "\"}";
    	}
    	
    	@Override
        public Object clone() {
    		Object object=null;
            try {
    			 object=super.clone();
    		} catch (CloneNotSupportedException e) {
    			//TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            return object;
        }	
    }
    
    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    /**
    * @desc: demo
    * @name: DeepFullPerson.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:49:25
    * @history:
    * @version: v1.0
    */
    
    public class DeepFullPerson implements Cloneable{
    
    	// 姓名
        private String name;
        // 年龄
        private int age;
        // 邮件
        private String email;
        // 房屋描述
        
    	// 将原来的 string desc 变成了 PersonDesc 对象,这样 personDesc 就是引用类型
        private DeepPersonHouse personHouse;
    
    	public DeepFullPerson(String name, int age, String email, String desc) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.email = email;
    		this.personHouse = new DeepPersonHouse();
            this.personHouse.setDesc(desc);
    	}
    	
    	public void setDesc(String desc) {
    		 this.personHouse.setDesc(desc);
    	}
    	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 String getEmail() {
    	
    		return email;
    	}
    
    	public void setEmail(String email) {
    	
    		this.email = email;
    	}
    
    	public DeepPersonHouse getPersonHouse() {
    	
    		return personHouse;
    	}
    
    	public void setPersonHouse(DeepPersonHouse personHouse) {
    	
    		this.personHouse = personHouse;
    	}
    
    	@Override
    	public String toString() {
    		return "{\"name\":\"" + name + "\", \"age\":\"" + age + "\", \"email\":\"" + email + "\", \"personHouse\":\""
    				+ personHouse + "\"}";
    	}
    	
    	@Override
        public Object clone() {
    		DeepFullPerson object=null;
            try {
    			 object=(DeepFullPerson)super.clone();
    			// 需要将引用对象也克隆一次
    			 object.personHouse = (DeepPersonHouse) personHouse.clone();
    		} catch (CloneNotSupportedException e) {
    			//TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            return object;
        }
    }
    
    

    测试:

    // 深拷贝1
    		// 初始化一个对象
    		DeepFullPerson person4 = new DeepFullPerson("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 复制对象
    		DeepFullPerson person5 = (DeepFullPerson) person4.clone();
    		// 改变 person1 的属性值
    		person5.setName("clone tompai");
    		// 修改 person age 的值
    		person5.setAge(22);
    		person5.setDesc("i am Not tompai");
    		System.out.println("person对象:" + person4);
    		System.out.println("person深拷贝1对象:" + person5);

    实现 Serializable 接口方式也可以实现深拷贝,而且这种方式还可以解决多层克隆的问题,多层克隆就是引用类型里面又有引用类型,层层嵌套下去,用 Cloneable 方式实现还是比较麻烦的,一不小心写错了就不能实现深拷贝了,使用 Serializable 序列化的方式就需要所有的对象对实现 Serializable 接口,我们对代码进行改造,改造成序列化的方式

    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    import java.io.Serializable;
    
    /**
    * @desc: demo
    * @name: PersonHouseSer.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:57:45
    * @history:
    * @version: v1.0
    */
    
    public class PersonHouseSer implements Serializable{
    
    	/**
    	* TODO
    	*/
    	private static final long serialVersionUID = -3161946439571740925L;
    	private String desc;
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
    	@Override
    	public String toString() {
    		return "{\"desc\":\"" + desc + "\"}";
    	}
        
    }
    
    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    /**
    * @desc: demo
    * @name: FullPersonSer.java
    * @author: tompai
    * @email:liinux@qq.com
    * @createTime: 2020年1月3日 下午1:59:40
    * @history:
    * @version: v1.0
    */
    
    public class FullPersonSer implements Serializable{
    
    	private static final long serialVersionUID = -2796028617106160841L;
    	// 姓名
        private String name;
        // 年龄
        private int age;
        // 邮件
        private String email;
        // 房屋描述
        
    	// 将原来的 string desc 变成了 PersonDesc 对象,这样 personDesc 就是引用类型
        private PersonHouseSer personHouse;
        public FullPersonSer(String name, int age, String email, String desc) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.email = email;
    		this.personHouse = new PersonHouseSer();
            this.personHouse.setDesc(desc);
    	}
        
        public void setDesc(String desc) {
    		 this.personHouse.setDesc(desc);
    	}
    	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 String getEmail() {
    	
    		return email;
    	}
    
    	public void setEmail(String email) {
    	
    		this.email = email;
    	}
    
    	public PersonHouseSer getPersonHouse() {
    	
    		return personHouse;
    	}
    
    	public void setPersonHouse(PersonHouseSer personHouse) {
    	
    		this.personHouse = personHouse;
    	}
    
    	public static long getSerialversionuid() {
    	
    		return serialVersionUID;
    	}
    
    	public FullPersonSer clone() {
        	FullPersonSer person = null;
            try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                // 将流序列化成对象
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                person= (FullPersonSer) ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return person;
        }
    
    	@Override
    	public String toString() {
    		return "{\"name\":\"" + name + "\", \"age\":\"" + age + "\", \"email\":\"" + email + "\", \"personHouse\":\""
    				+ personHouse + "\"}";
    	}
    	
    	
    }
    
    

    完整测试代码:

    
    /** 
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package com.tompai.clone;
    
    /**
     * @desc: demo
     * @name: PersonApp.java
     * @author: tompai
     * @email:liinux@qq.com
     * @createTime: 2020年1月3日 下午1:22:18
     * @history:
     * @version: v1.0
     */
    
    public class PersonApp {
    
    	/**
    	 * @author: tompai
    	 * @createTime: 2020年1月3日 下午1:22:19
    	 * @history:
    	 * @param args void
    	 */
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		// 初始化一个对象
    		SimplePerson tompai = new SimplePerson("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 直接赋值
    		SimplePerson tompai1 = tompai;
    		// 改变 person1 的属性值
    		tompai1.setName("not tompai");
    		System.out.println("person对象:" + tompai);
    		System.out.println("person直接赋值对象:" + tompai1);
    		// =========================
    		// 浅拷贝2 Clonenable实现
    		// 初始化一个对象
    		FullPerson person2 = new FullPerson("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 复制对象
    		FullPerson person3 = (FullPerson) person2.clone();
    		// 改变 person1 的属性值
    		person3.setName("clone tompai");
    		// 修改 person age 的值
    		person3.setAge(22);
    		person3.setDesc("i am Not tompai");
    		System.out.println("person对象:" + person2);
    		System.out.println("person浅拷贝2对象:" + person3);
    		// 深拷贝1
    		// 初始化一个对象
    		DeepFullPerson person4 = new DeepFullPerson("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 复制对象
    		DeepFullPerson person5 = (DeepFullPerson) person4.clone();
    		// 改变 person1 的属性值
    		person5.setName("clone tompai");
    		// 修改 person age 的值
    		person5.setAge(22);
    		person5.setDesc("i am Not tompai");
    		System.out.println("person对象:" + person4);
    		System.out.println("person深拷贝1对象:" + person5);
    
    		// 深拷贝2序列化方法实现
    		// 初始化一个对象
    		FullPersonSer person6 = new FullPersonSer("tompai", 20, "liinux@qq.com", "i am tompai.");
    		// 复制对象
    		FullPersonSer person7 = (FullPersonSer) person6.clone();
    		// 改变 person1 的属性值
    		person7.setName("clone tompai");
    		// 修改 person age 的值
    		person7.setAge(22);
    		person7.setDesc("i am Not tompai");
    		System.out.println("person对象:" + person6);
    		System.out.println("person深拷贝2对象:" + person7);
    	}
    
    }
    

     

    展开全文
  • 应用场景 需要同一个类的多个不同对象完成业务操作,群发email,需要多个Mail类的对象 好处:提高性能 public static void main(String[] args) throws CloneNotSupportedException { Mail mail = new...

    学习路径:https://coding.imooc.com/class/270.html

    • 应用场景
      需要同一个类的多个不同对象完成业务操作,群发email,需要多个Mail类的对象
      好处:提高性能
     public static void main(String[] args) throws CloneNotSupportedException {
            Mail mail = new Mail();
            mail.setContent("初始化模板");
            for(int i=0; i<10; i++) {
                 //创建10个Mail的对象进行发邮件的操作,使用原型模式加快性能
                Mail mailTemp = (Mail) mail.clone();
                mailTemp.setName("第" + i + "个克隆出来的对象");
                // 使用克隆采用的是二进制的拷贝,效率比直接new高很多
            }
            // 不改变原来的Mail
            MailUtil.saveOriginMailRecord(mail);
        }
    
    • 实现
    // 省略get set方法
    public class Mail implements Cloneable{
        private String name;
        private String address;
        private String content;
        public Mail() {
            System.out.println("Mail Class Constructor");
        }
    
        /**
         * 记得重写方法
         * @return
         * @throws CloneNotSupportedException
         */
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public String toString() {
            return "Mail{" +
                    "name='" + name + '\'' +
                    ", address='" + address + '\'' +
                    ", content='" + content + '\'' +
                    '}';
        }
    }
    
    public class MailUtil {
        public static void sendMail(Mail mail) {
            String outputContent = "向{0}同学,邮件地址{1},邮件内容:{2},发送成功";
            System.out.println(MessageFormat.format(outputContent, mail.getName(),mail.getAddress(),mail.getContent()));
        }
    
        public static void saveOriginMailRecord(Mail mail) {
          System.out.println("存储originMail记录,originMail:" + mail.getContent());
        }
    }
    
    • 原理

    原型模式的本质是类继承Clonable接口,重写clone方法。clone的底层是使用二进制拷贝,需要多个对象的时候可以用该方法取代多次new,提高性能。


    • 修改原对象的成员变量,可能会同时修改克隆出来的对象。避免这种坑即使用深拷贝。

    不重写clone方法,默认使用浅拷贝

    public class Pig implements Cloneable{
        private String name;
        // 引用对象
        private Date birthday;
        
        // 默认的clone方法,不会修改引用对象的指向,即拷贝出来的对象中birthday引用指向的是同一块内存区域
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Pig pig = (Pig)super.clone();
    
            // 深拷贝,有引用对象的时候需要重写clone()方法
            pig.birthday = (Date)pig.birthday.clone();
            return pig;
        }
    }
    
    • 拓展
      原型模式和单例模式结合的时候,注意修改clone方法

    1.极端场景

     // step 2克隆破坏单例
            HungrySingleton hungrySingleton = HungrySingleton.getInstance();
            Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
            method.setAccessible(true);
            HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
            System.out.println(hungrySingleton);
            System.out.println(cloneHungrySingleton);
    

    2.关键代码

     /**
         * 单例模式下使用的clone方法, 防止克隆破坏就要修改方法实现
         * @return
         * @throws CloneNotSupportedException
         */
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return getInstance();
        }
    
    展开全文
  • 浅拷贝|深拷贝

    2017-12-13 13:09:53
    但是,该方法应用在特定场景下可能是个坑。 在拷贝过程中其对象内部的基本类型与String类型的值会被直接拷贝,但是对于数组、对象引用只是拷贝地址,即还是指向原生对象的数组或对象引用,原生对象与拷贝对象这两...

    一、浅拷贝


    什么是浅拷贝?

    Object类是Java语言所有类的祖先类,所有类皆继承于他。Object类提供了clone方法,为此所有的Java类都可以支持该方法。但是,该方法应用在特定场景下可能是个坑。


    在拷贝过程中其对象内部的基本类型与String类型的值会被直接拷贝,但是对于数组、对象引用只是拷贝地址,即还是指向原生对象的数组或对象引用,原生对象与拷贝对象这两个对象共享了这些数组与对象引用,一个改变了另外一个也随之而改变。


    上述clone的过程与特性,我们称之为“浅拷贝”。这是需要在实际编程过程中特别注意的。虽然很多时候“浅拷贝”可以解决业务问题,但是更多时候会留下隐患,需要留意提防。

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    二、深拷贝


    什么是深拷贝?

    深拷贝解决了原生对象与拷贝对象共用数组和对象引用的问题。


    怎样实现深拷贝?

    在调用了clone()方法之后,在存在有共用关系的数组和对象的地方,创建新的内容相同的数组和对象来进行覆盖,来切断与原对象的关联。

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    展开全文
  • 原型模式有哪些应用场景?它的两种实现方式,深拷贝和浅拷贝。 目录一、什么叫原型模式?1.1 什么叫对象的创建成本比较大?1.2 一个例子二、深拷贝和浅拷贝2.1 什么是深什么是浅?2.2 如何实现深拷贝2.3 交替使用...

    知深浅,创造生命才能游刃有余。

    什么叫原型模式?原型模式有哪些应用场景?深拷贝和浅拷贝了解一下吧。

    一、什么叫原型模式?

    如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。
    java这种基于类的面向对象的编程语言,其实还是比较少用到原型模式。而JavaScript这种基于原型的面向对象编程语言,原型模式很常用。

    1.1 什么叫对象的创建成本比较大?

    实际上,创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。

    但是,如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。

    1.2 一个例子

    假设数据库中存储了大约 10 万条“搜索关键词”信息,每条信息包含关键词、关键词被搜索的次数、信息最近被更新的时间等。系统 A 在启动的时候会加载这份数据到内存中,用于处理某些其他的业务需求。为了方便快速地查找某个关键词对应的信息,我们给关键词建立一个散列表索引。

    如果你熟悉的是 Java 语言,可以直接使用语言中提供的 HashMap 容器来实现。其中,HashMap 的 key 为搜索关键词,value 为关键词详细信息(比如搜索次数)。我们只需要将数据从数据库中读取出来,放入 HashMap 就可以了。

    不过,我们还有另外一个系统 B,专门用来分析搜索日志,定期(比如间隔 10 分钟)批量地更新数据库中的数据,并且标记为新的数据版本。比如,在下面的示例图中,我们对 v2 版本的数据进行更新,得到 v3 版本的数据。这里我们假设只有更新和新添关键词,没有删除关键词的行为。

    在这里插入图片描述
    为了保证系统 A 中数据的实时性(不一定非常实时,但数据也不能太旧),系统 A 需要定期根据数据库中的数据,更新内存中的索引和数据。

    实际上,也不难。我们只需要在系统 A 中,记录当前数据的版本 Va 对应的更新时间 Ta,从数据库中捞出更新时间大于 Ta 的所有搜索关键词,也就是找出 Va 版本与最新版本数据的“差集”,然后针对差集中的每个关键词进行处理。如果它已经在散列表中存在了,我们就更新相应的搜索次数、更新时间等信息;如果它在散列表中不存在,我们就将它插入到散列表中。

    
    public class Demo {
      private ConcurrentHashMap<String, SearchWord> currentKeywords = new ConcurrentHashMap<>();
      private long lastUpdateTime = -1;
    
      public void refresh() {
        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到currentKeywords中
        List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
        long maxNewUpdatedTime = lastUpdateTime;
        for (SearchWord searchWord : toBeUpdatedSearchWords) {
          if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
            maxNewUpdatedTime = searchWord.getLastUpdateTime();
          }
          if (currentKeywords.containsKey(searchWord.getKeyword())) {
            currentKeywords.replace(searchWord.getKeyword(), searchWord);
          } else {
            currentKeywords.put(searchWord.getKeyword(), searchWord);
          }
        }
    
        lastUpdateTime = maxNewUpdatedTime;
      }
    
      private List<SearchWord> getSearchWords(long lastUpdateTime) {
        // TODO: 从数据库中取出更新时间>lastUpdateTime的数据
        return null;
      }
    }
    

    不过,现在,我们有一个特殊的要求:任何时刻,系统 A 中的所有数据都必须是同一个版本的,要么都是版本 a,要么都是版本 b,不能有的是版本 a,有的是版本 b。那刚刚的更新方式就不能满足这个要求了。除此之外,我们还要求:在更新内存数据的时候,系统 A 不能处于不可用状态,也就是不能停机更新数据。

    我们把正在使用的数据的版本定义为“服务版本”,当我们要更新内存中的数据的时候,我们并不是直接在服务版本(假设是版本 a 数据)上更新,而是重新创建另一个版本数据(假设是版本 b 数据),等新的版本数据建好之后,再一次性地将服务版本从版本 a 切换到版本 b。这样既保证了数据一直可用,又避免了中间状态的存在。

    设计思路如下

    
    public class Demo {
      private HashMap<String, SearchWord> currentKeywords=new HashMap<>();
    
      public void refresh() {
        HashMap<String, SearchWord> newKeywords = new LinkedHashMap<>();
    
        // 从数据库中取出所有的数据,放入到newKeywords中
        List<SearchWord> toBeUpdatedSearchWords = getSearchWords();
        for (SearchWord searchWord : toBeUpdatedSearchWords) {
          newKeywords.put(searchWord.getKeyword(), searchWord);
        }
    
        currentKeywords = newKeywords;
      }
    
      private List<SearchWord> getSearchWords() {
        // TODO: 从数据库中取出所有的数据
        return null;
      }
    }
    

    不过,在上面的代码实现中,newKeywords 构建的成本比较高。我们需要将这 10 万条数据从数据库中读出,然后计算哈希值,构建 newKeywords。这个过程显然是比较耗时。为了提高效率,原型模式就派上用场了。

    我们拷贝 currentKeywords 数据到 newKeywords 中,然后从数据库中只捞出新增或者有更新的关键词,更新到 newKeywords 中。而相对于 10 万条数据来说,每次新增或者更新的关键词个数是比较少的,所以,这种策略大大提高了数据更新的效率。

    按照这个设计思路,我给出的示例代码如下所示:

    
    public class Demo {
      private HashMap<String, SearchWord> currentKeywords=new HashMap<>();
      private long lastUpdateTime = -1;
    
      public void refresh() {
        // 原型模式就这么简单,拷贝已有对象的数据,更新少量差值
        HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywords.clone();
    
        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到newKeywords中
        List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
        long maxNewUpdatedTime = lastUpdateTime;
        for (SearchWord searchWord : toBeUpdatedSearchWords) {
          if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
            maxNewUpdatedTime = searchWord.getLastUpdateTime();
          }
          if (newKeywords.containsKey(searchWord.getKeyword())) {
            SearchWord oldSearchWord = newKeywords.get(searchWord.getKeyword());
            oldSearchWord.setCount(searchWord.getCount());
            oldSearchWord.setLastUpdateTime(searchWord.getLastUpdateTime());
          } else {
            newKeywords.put(searchWord.getKeyword(), searchWord);
          }
        }
    
        lastUpdateTime = maxNewUpdatedTime;
        currentKeywords = newKeywords;
      }
    
      private List<SearchWord> getSearchWords(long lastUpdateTime) {
        // TODO: 从数据库中取出更新时间>lastUpdateTime的数据
        return null;
      }
    }
    

    这里我们利用了 Java 中的 clone() 语法来复制一个对象。如果你熟悉的语言没有这个语法,那把数据从 currentKeywords 中一个个取出来,然后再重新计算哈希值,放入到 newKeywords 中也是可以接受的。毕竟,最耗时的还是从数据库中取数据的操作。相对于数据库的 IO 操作来说,内存操作和 CPU 计算的耗时都是可以忽略的。

    不过,不知道你有没有发现,实际上,刚刚的代码实现是有问题的。要弄明白到底有什么问题,我们需要先了解另外两个概念:深拷贝(Deep Copy)和浅拷贝(Shallow Copy)。

    二、深拷贝和浅拷贝

    2.1 什么是深什么是浅?

    我们来看,在内存中,用散列表组织的搜索关键词信息是如何存储的。我画了一张示意图,大致结构如下所示。从图中我们可以发现,散列表索引中,每个结点存储的 key 是搜索关键词,value 是 SearchWord 对象的内存地址。SearchWord 对象本身存储在散列表之外的内存空间中。

    在这里插入图片描述
    浅拷贝和深拷贝的区别在于,浅拷贝只会复制图中的索引(散列表),不会复制数据(SearchWord 对象)本身。相反,深拷贝不仅仅会复制索引,还会复制数据本身。浅拷贝得到的对象(newKeywords)跟原始对象(currentKeywords)共享数据(SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。具体的对比如下图所示:
    在这里插入图片描述
    在这里插入图片描述
    在 Java 语言中,Object 类的 clone() 方法执行的就是我们刚刚说的浅拷贝。它只会拷贝对象中的基本数据类型的数据(比如,int、long),以及引用对象(SearchWord)的内存地址,不会递归地拷贝引用对象本身。

    在上面的代码中,我们通过调用 HashMap 上的 clone() 浅拷贝方法来实现原型模式。

    我们可以将浅拷贝替换为深拷贝。newKeywords 不仅仅复制 currentKeywords 的索引,还把 SearchWord 对象也复制一份出来,这样 newKeywords 和 currentKeywords 就指向不同的 SearchWord 对象,也就不存在更新 newKeywords 的数据会导致 currentKeywords 的数据也被更新的问题了。

    2.2 如何实现深拷贝

    • 递归拷贝对象
      归拷贝对象、对象的引用对象以及引用对象的引用对象……直到要拷贝的对象只包含基本数据类型数据,没有引用对象为止。根据这个思路对之前的代码进行重构。重构之后的代码如下所示:
    
    public class Demo {
      private HashMap<String, SearchWord> currentKeywords=new HashMap<>();
      private long lastUpdateTime = -1;
    
      public void refresh() {
        // Deep copy
        HashMap<String, SearchWord> newKeywords = new HashMap<>();
        for (HashMap.Entry<String, SearchWord> e : currentKeywords.entrySet()) {
          SearchWord searchWord = e.getValue();
          SearchWord newSearchWord = new SearchWord(
                  searchWord.getKeyword(), searchWord.getCount(), searchWord.getLastUpdateTime());
          newKeywords.put(e.getKey(), newSearchWord);
        }
    
        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到newKeywords中
        List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
        long maxNewUpdatedTime = lastUpdateTime;
        for (SearchWord searchWord : toBeUpdatedSearchWords) {
          if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
            maxNewUpdatedTime = searchWord.getLastUpdateTime();
          }
          if (newKeywords.containsKey(searchWord.getKeyword())) {
            SearchWord oldSearchWord = newKeywords.get(searchWord.getKeyword());
            oldSearchWord.setCount(searchWord.getCount());
            oldSearchWord.setLastUpdateTime(searchWord.getLastUpdateTime());
          } else {
            newKeywords.put(searchWord.getKeyword(), searchWord);
          }
        }
    
        lastUpdateTime = maxNewUpdatedTime;
        currentKeywords = newKeywords;
      }
    
      private List<SearchWord> getSearchWords(long lastUpdateTime) {
        // TODO: 从数据库中取出更新时间>lastUpdateTime的数据
        return null;
      }
    
    }
    
    • 序列化实现
      先将对象序列化,然后再反序列化成新的对象。具体的示例代码如下所示:
    
    public Object deepCopy(Object object) {
      ByteArrayOutputStream bo = new ByteArrayOutputStream();
      ObjectOutputStream oo = new ObjectOutputStream(bo);
      oo.writeObject(object);
      
      ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
      ObjectInputStream oi = new ObjectInputStream(bi);
      
      return oi.readObject();
    }
    

    2.3 交替使用深浅拷贝

    我们可以先采用浅拷贝的方式创建 newKeywords。对于需要更新的 SearchWord 对象,我们再使用深度拷贝的方式创建一份新的对象,替换 newKeywords 中的老对象。毕竟需要更新的数据是很少的。这种方式即利用了浅拷贝节省时间、空间的优点,又能保证 currentKeywords 中的中数据都是老版本的数据。具体的代码实现如下所示。这也是标题中讲到的,在我们这个应用场景下,最快速 clone 散列表的方式。

    public class Demo {
      private HashMap<String, SearchWord> currentKeywords=new HashMap<>();
      private long lastUpdateTime = -1;
    
      public void refresh() {
        // Shallow copy
        HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywords.clone();
    
        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到newKeywords中
        List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
        long maxNewUpdatedTime = lastUpdateTime;
        for (SearchWord searchWord : toBeUpdatedSearchWords) {
          if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
            maxNewUpdatedTime = searchWord.getLastUpdateTime();
          }
          if (newKeywords.containsKey(searchWord.getKeyword())) {
            newKeywords.remove(searchWord.getKeyword());
          }
          newKeywords.put(searchWord.getKeyword(), searchWord);
        }
    
        lastUpdateTime = maxNewUpdatedTime;
        currentKeywords = newKeywords;
      }
    
      private List<SearchWord> getSearchWords(long lastUpdateTime) {
        // TODO: 从数据库中取出更新时间>lastUpdateTime的数据
        return null;
      }
    }
    

    三、参考链接

    展开全文
  • Java数组深度分析

    2021-01-15 19:30:07
    一、什么是数组? 二、数组的基本定义 ...十三、浅拷贝与深拷贝 十四、多维数组 十五、数组的应用场景 十六、ArrayIndexOutOfBoundsException:数组索引越界异常(解决方法) 十七、NullPointerExcept
  • 及时送达前言本文的主要内容如下:介绍原型模式示例Java语言的clone浅克隆与克隆实现克隆原型模式的典型应用原型模式原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新...
  • JAVA设计模式--原型模式

    万次阅读 2016-05-23 19:48:46
    三、Java中的深拷贝与浅拷贝(或深度克隆与浅度克隆) 四、原型模式应用场景 五、原型模式的特点 参考文章 一、什么是原型模式 原型(Prototype)模式是一种对象创建型模式,它通过原型实例指定创建对象的种类,...
  • Java基础拾遗1

    2018-09-10 16:35:23
    主题:对象浅拷贝和深拷贝 重要程度:不重要(相对于面试来说),了解应用场景,基本原理,用的时候调用api即可 应用场景:代码分层后,各层交互的对象有一些是属性相同的,当需要复制时,需要用到拷贝方法。比如前段...
  • Java常见问题汇总

    2019-10-08 10:17:24
    1.String,StringBuffer,StringBulider的区别及应用场景 2.Servlet生命周期 3.向上转型与向下转型 4.Java的多态性 5.重写和重载的区别 6.深拷贝和浅拷贝的区别 7.wait和sleep的区别 8.内存泄漏和内存溢出 9....
  • java面试准备

    2020-08-12 11:33:22
    基础 Arrays.sort实现原理和Collection实现原理 foreach和while的区别(编译之后) ...cloneable接口实现原理,浅拷贝or深拷贝 Java NIO使用 hashtable和hashmap的区别及实现原理,hashmap会问到数组
  • Java学习基础和总结三

    2020-03-18 09:35:46
    反射机制的应用场景?4.Java中的异常处理?5.Java序列化中,如果有的字段不想被序列化,应该怎么办?6.深拷贝和浅拷贝? 1.为什么java中只有值传递? 按值调用:表示方法接收的是调用者提供的值。 按引用调用:表示...
  • 深拷贝与浅拷贝中,提到可以采用「序列化与反序列化」的方式来实现深拷贝,今天主要来填一下序列化的坑。其中,序列化是一种对象持久化的手段,普遍应用于网络传输和远程方法调用(RMI)等场景中,建议关注。什么是...
  • 一、Java基础面试题

    2020-05-16 14:18:22
    目录 1. == 和 equals 的区别? 2. 拆箱和装箱分别是什么?分别应用在什么场景? 3.Java基础类型与封装类型的区别 ...9.深拷贝和浅拷贝区别是什么? 10.值传递和引用传递的区别是什么? 11.什么是 java 序列化?
  • 创建对象的方式深拷贝vs 浅拷贝Java中的两种异常类型是什么?他们有什么区别?Java中Exception和Error有什么区别?异常处理完成以后,Exception对象会发生什么变化?throw 和 throws 的区别?final、finally、...
  • 深拷贝与浅拷贝中,提到可以采用「序列化与反序列化」的方式来实现深拷贝,今天主要来填一下序列化的坑。 其中,序列化是一种对象持久化的手段,普遍应用于网络传输和远程方法调用(RMI)等场景中,建议关注。 ...
  • java基础 Arrays.sort实现原理和Collection实现原理foreach和while的区别(编译之后)线程池的...cloneable接口实现原理,浅拷贝or深拷贝Java NIO使用hashtable和hashmap的区别及实现原理,hashmap会问到数组索引,hash
  • 一、场景 创建对象比较耗时的时候,可以拷贝原型,再进行修改;是基于内存的操作 ...三、开发中的应用场景 四、Java中原型模式的使用 注:此处重写的是Object类中的clone()方法;Object类中的clone()...
  • 应用场景 注意事项 说明 1.五大创建型模式之一,其他还有抽象工厂模式、单例模式、建造者模式、工厂模式。 2.原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。 3. 允许一个对象...
  • java基础Arrays.sort实现原理和Collection实现原理foreach和while的区别(编译之后)线程池的种类,区别和使用场景分析线程池的实现原理和线程的调度过程线程池如何调优线程池的最大线程数目根据什么确定动态代理的几...
  • java基础Arrays.sort实现原理和Collection实现原理 foreach和while的区别(编译之后) 线程池的种类,区别和使用场景分析线程池的实现原理和线程的调度过程线程池如何调优线程池的最大线程数目根据什么确定动态代理的...
  • 图解Java设计模式(一)

    万人学习 2019-02-27 21:06:35
    单例模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等 3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧 课程内容和目标 本课程是使用Java来讲解设计模式...
  • 单例模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等 3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧 课程内容和目标 本课程是使用Java来讲解设计模式...
  • 63.深拷贝和浅拷贝区别是什么? 六、Java Web 64.jsp 和 servlet 有什么区别? 65.jsp 有哪些内置对象?作用分别是什么? 66.说一下 jsp 的 4 种作用域? 67.session 和 cookie 有什么区别? 68.说一下 session 的...
  • 传引用既是传内存地址,如果传递的对象发生了改变,那么被传递的对象也将改变(所有某些场景需要深拷贝) 1、对象是按引用传递的,原始数据类型是按值传递的 2、Java 应用程序有且仅有的一种参数传递机制,即按值...
  • 63. 深拷贝和浅拷贝区别是什么? 28 六、Java Web 28 64. jsp 和 servlet 有什么区别? 28 65. jsp 有哪些内置对象?作用分别是什么? 29 66. 说一下 jsp 的 4 种作用域? 29 67. session 和 cookie 有什么区别? 30...
  • 原型模式

    2021-01-19 16:43:03
    目录原型模式简介原型模式应用场景原型模式浅拷贝和深拷贝java中内存分配浅拷贝和深拷贝的区别源代码如下所示 原型模式简介 原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,...
  • 原型模式从根本上来说就是Java中的对象克隆,针对于对象的浅拷贝和深拷贝Java中提供了两种实现方式: 实现Cloneable接口并重写clone() 实现Serializable 接口,通过序列化和反序列化实现深拷贝 相较于前面的单例...
  • 文章目录23种设计模式之原型模式参考资料一、简介定义原理特点优点缺点通用类图应用场景二、实现分类浅克隆(浅拷贝)深克隆(深拷贝)浅克隆原型模式深克隆原型模式1. 重写clone()方法2. 使用对象的序列化三、总结 参考...
  • 80_Netty复合缓冲区详解与3种缓冲区适用场景分析 81_Netty引用计数的实现机制与自旋锁的使用技巧 82_Netty引用计数原子更新揭秘与AtomicIntegerFieldUpdater深度剖析 83_AtomicIntegerFieldUpdater实例演练与...
  • IO体系架构系统回顾与装饰模式的具体应用 33_Java NIO深入详解与体系分析 34_Buffer中各重要状态属性的含义与关系图解 35_Java NIO核心类源码解读与分析 36_文件通道用法详解 37_Buffer深入详解 38_NIO堆外内存与零...

空空如也

空空如也

1 2 3
收藏数 42
精华内容 16
关键字:

java深拷贝应用场景

java 订阅