java123新地址_关于Java的拷贝你能说出个123么?

本文内容:讲解对象创建方式及Java中的深拷贝还是浅拷贝。

1、创建对象的5种方式通过 new 关键字❝

这是最常用的一种方式,通过 new 关键字调用类的有参或无参构造方法来创建对象。比如 Object obj = new Object();❞

通过 Class 类的 newInstance() 方法❝

这种默认是调用类的无参构造方法创建对象。比如 Person p2 = (Person) Class. forName("com.ys.test. Person"). newInstance();❞

通过 Constructor 类的 newInstance 方法❝

这和第二种方法类时,都是通过反射来实现。通过 java.lang.relect.Constructor 类的 newInstance() 方法指定某个构造器来创建对象。  Person p3 = (Person) Person.class.getConstructors()[0].newInstance();   实际上第二种方法利用 Class 的 newInstance() 方法创建对象,其内部调用还是 Constructor 的 newInstance() 方法。❞

利用 Clone 方法❝

Clone 是 Object 类中的一个方法,通过 对象A.clone() 方法会创建一个内容和对象 A 一模一样的对象 B,clone 克隆,顾名思义就是创建一个一模一样的对象出来。  Person p4 = (Person) p3.clone();❞

序列化❝

序列化是把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。而反序列化则是把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。序列化❞

Java 基本复制方法

java赋值是复制「对象引用」,如果我们想要得到一个对象的==副本==,使用赋值操作是无法达到目的的:修改新对象的值会同时修改旧对象的值。public class Client{

public static void main(String[] args) throws CloneNotSupportedException{

Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));

Person p1 = person;

p1.setAge(45);

System.out.println(p1.hashCode());

System.out.println(person.hashCode());

System.out.println("================");

System.out.println(p1.display());

System.out.println(person.display());

}

}

Clone 方法

如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以「改变各自的状态」,而互不影响,就需要用到java中对象的复制,如原生的clone()方法。本次讲解的是 Java 的深拷贝和浅拷贝,其实现方式正是通过调用 Object 类的 clone() 方法来完成。在 Object.class 类中,源码为:/**

* ...

* performs a "shallow copy" of this object, not a "deep copy" operation.

* 上面这里已经说明了,clone()方法是浅拷贝,而不是深拷贝

* @see java.lang.Cloneable

*/

protected native Object clone() throws CloneNotSupportedException;

这是一个用 native 关键字修饰的方法,关于native关键字有一篇博客专门有介绍,不理解也没关系,只需要知道用 native 修饰的方法就是告诉操作系统,这个方法我不实现了,让操作系统去实现(参考JNI)。具体怎么实现我们不需要了解,只需要知道 clone方法的作用就是「复制对象」,产生一个新的对象。那么这个新的对象和原对象是==什么关系呢==?

基本类型和引用类型

这里再给大家普及一个概念,在 Java 中「基本类型和引用类型」的区别。在 Java 中数据类型可以分为两大类:基本类型和引用类型。基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型 byte、short、int、long、float、double。引用类型则包括类、接口、数组、枚举等。  Java 将内存空间分为「堆和栈」。基本类型直接在栈 stack中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆 heap中,通过栈中的引用指向堆中存放的数据。

01a6e4adb4f043ae7a2f8c80447f5084.png

上图定义的 a 和 b 都是基本类型,其值是「直接存放在栈中」的;而 c 和 d 是 String 声明的,这是一个引用类型,「引用地址是存放在栈中,然后指向堆的内存空间」。  下面 d = c;这条语句表示将 c 的引用赋值给 d,那么 c 和 d 将指向同一块堆内存空间。

浅拷贝

接下来用代码看看浅拷贝的效果。package mytest;

@Data//lombok注解

class Person implements Cloneable{

private int age;

private String name;

private Address address;

public Person(int age, String name, Address address){

this.age = age;

this.name = name;

this.address = address;

}

@Override

protected Object clone() throws CloneNotSupportedException{

return super.clone();

}

public String display(){

return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";

}

}

@Data//lombok注解

class Address{

private String province;

private String street;

public Address(String province, String street){

this.province = province;

this.street = street;

}

@Override

public String toString(){

return "Address [province=" + province + ", street=" + street + "]";

}

}

public class Client{

public static void main(String[] args) throws CloneNotSupportedException{

Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));

Person clonePerson = (Person) person.clone();

System.out.println(person);

System.out.println(clonePerson);

// 信息完全一样

System.out.println(person.display());

System.out.println(clonePerson.display());

System.out.println("信息完全一致");

System.out.println("原始年龄:" + person.getAge());

System.out.println("克隆后原始年龄:" + clonePerson.getAge());

System.out.println("年龄完全一样");

System.out.println("原始名字哈希值:" + person.getName().hashCode());

System.out.println("克隆后名字哈希值:" + clonePerson.getName().hashCode());

System.out.println("字符串哈希值完全一样");

clonePerson.setName("xiaomai");

clonePerson.setAge(20);

clonePerson.getAddress().setStreet("中山路");

System.out.println(clonePerson.display());

System.out.println(person.display());

System.out.println("年龄跟姓名 是完全的深拷贝 副本跟原值无关的!");

System.out.println("地址信息的修改是浅拷贝  ");

}

}

结果如下:❝

mytest.Person@15f550a

mytest.Person@6b2d4a

Person [age=15, name=sowhat, address=Address [province=河北, street=建华南大街]]

Person [age=15, name=sowhat, address=Address [province=河北, street=建华南大街]]

信息完全一致

原始年龄:15

克隆后原始年龄:15

年龄完全一样

原始名字哈希值:-1432601412

克隆后名字哈希值:-1432601412

字符串哈希值完全一样

Person [age=20, name=xiaomai, address=Address [province=河北, street=中山路]]

Person [age=15, name=sowhat, address=Address [province=河北, street=中山路]]❞

结论:原对象与新对象是两个不同的对象。

拷贝出来的新对象与原对象内容一致

接着将新对象里面的信息进行了修改,然后输出发现原对象里面的部分信息也跟着变了。其中 基本类型跟 String类型的改变不会影响到 原始对象的改变。而其他的Ojbect 类型改变的时候会影响到原始数据。上面的结论称为浅拷贝「浅拷贝」:创建一个新对象,然后将当前对象的非静态字段复制到该对象,如果字段类型是值类型(基本类型跟String)的,那么对该字段进行「复制」;如果字段是引用类型的,「则只复制该字段的引用而不复制引用指向的对象(也就是只复制对象的地址)」。此时新对象里面的引用类型字段相当于是原始对象里面引用类型字段的一个副本,原始对象与新对象里面的引用字段指向的是同一个对象。因此,修改clonePerson里面的address内容时,原person里面的address内容会跟着改变。

深拷贝

了解了浅拷贝,那么深拷贝是什么也就很清楚了。那么该如何实现深拷贝呢?Object 类提供的 clone 是只能实现 浅拷贝的。,即将「引用类型的属性内容也拷贝一份新的」。那么,实现深拷贝我这里收集到两种方式:「第一种」是给需要拷贝的引用类型也实现Cloneable接口并覆写clone方法;「第二种」则是利用序列化。接下来分别对两种方式进行演示。

深拷贝-clone方式

对于以上演示代码,利用clone方式进行深拷贝无非就是将Address类也实现Cloneable,然后对Person的clone方法进行调整。让每个引用类型属性内部都重写clone() 方法,既然引用类型不能实现深拷贝,那么我们将每个引用类型都拆分为基本类型,分别进行浅拷贝。比如上面的例子,Person 类有一个引用类型 Address(其实String 也是引用类型,但是String类型有点特殊,后面会详细讲解),我们在 Address 类内部也重写 clone 方法。如下:package mytest;

@Data//lombok注解

class Person implements Cloneable{

private int age;

private String name;

private Address address;

protected int abc = 12;

public Person(int age, String name, Address address){

this.age = age;

this.name = name;

this.address = address;

}

@Override  // clone 重载

protected Object clone() throws CloneNotSupportedException{

Person person = (Person) super.clone();

//手动对address属性进行clone,并赋值给新的person对象

person.address = (Address) address.clone();

return person;

}

public String display(){

return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";

}

}

@Data//lombok注解

class Address implements  Cloneable{

private String province;

private String street;

public Address(String province, String street){

this.province = province;

this.street = street;

}

// 深拷贝时添加

@Override

protected Object clone() throws CloneNotSupportedException{

return super.clone();

}

@Override

public String toString(){

return "Address [province=" + province + ", street=" + street + "]";

}

}

public class Client{

public static void main(String[] args) throws CloneNotSupportedException{

Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));

Person p1 = person;

p1.setAge(45);

System.out.println(p1.hashCode());

System.out.println(person.hashCode());

System.out.println(p1.display());

System.out.println(person.display());

System.out.println("-----------");

Person clonePerson = (Person) person.clone();

System.out.println(person);

System.out.println(clonePerson);

// 信息完全一样

System.out.println(person.display());

System.out.println(clonePerson.display());

System.out.println("信息完全一致");

System.out.println("原始年龄:" + person.getAge());

System.out.println("克隆后原始年龄:" + clonePerson.getAge());

System.out.println("年龄完全一样");

System.out.println("原始名字哈希值:" + person.getName().hashCode());

System.out.println("克隆后名字哈希值:" + clonePerson.getName().hashCode());

System.out.println("字符串哈希值完全一样");

clonePerson.setName("sowhat1412");

clonePerson.setAge(20);

clonePerson.getAddress().setStreet("中山路");

System.out.println(clonePerson.display());

System.out.println(person.display());

System.out.println("年龄跟姓名 是完全的深拷贝 副本跟原值无关的!");

System.out.println("地址信息的修改是浅拷贝  ");

}

}

但是这种做法有个弊端,这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。

利用序列化

序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。注意每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。package mytest;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

/**

* 利用序列化和反序列化进行对象的深拷贝

* @author  ljj

*/

class DeepClone implements Serializable{

private static final long serialVersionUID = 1412L;

public Object deepClone() throws Exception{

//序列化

ByteArrayOutputStream bos = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream(bos);

oos.writeObject(this);

//反序列化

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

ObjectInputStream ois = new ObjectInputStream(bis);

return ois.readObject();

}

}

@Data

class Person extends DeepClone{

private static final long serialVersionUID = 1L;

private int age;

private String name;

private Address address;

public Person(int age, String name, Address address){

this.age = age;

this.name = name;

this.address = address;

}

public String display(){

return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";

}

}

@Data

class Address extends DeepClone{

private static final long serialVersionUID = 1412L;

private String province;

private String street;

public Address(String province, String street){

this.province = province;

this.street = street;

}

@Override

public String toString(){

return "Address [province=" + province + ", street=" + street + "]";

}

public void setStreet(String street){

this.street = street;

}

}

public class Client{

public static void main(String[] args) throws Exception{

Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));

Person clonePerson = (Person) person.deepClone();

System.out.println(person);

System.out.println(clonePerson);

System.out.println(person.display());

System.out.println(clonePerson.display());

clonePerson.setName("sowhat1412");

clonePerson.setAge(20);

Address address = clonePerson.getAddress();

address.setStreet("中山路");

System.out.println(clonePerson.display());

System.out.println(person.display());

}

}

注意:本源码已经修复首页天气预报显示非法盗用等问题 精仿hao123网址导航系统简介(切记上传后先到后台全站生成下html静态页面): 1.本程序为asp+access数据库,所有代码开源,无绑定域名等限制,直接把源码放在win系统服务器即可。 2.常规管理--网站基本参数(可视模式) 进去可以进行修改编辑,可视化操作更方便更简单。 3.静态页面管理-静态页面生成-一键生成所有静态页面,后台地址/admin/目录,用户名admin,密码123456 源码特色: 1、流量来路交换链功能! 更加准确的加密算法记录来路,有站点点入即可在有效位置推荐站长站点,非常实用的流量交换功能。 2、网址加色、推荐功能! 自己可以随心所欲的设置某个站点的颜色,或设置站与站之间的排序,适合手傻瓜操作,这样可以使收费网址靠前,更好的进行双赢。 3、宣传得积分、积分兑换奖品功能! 结合WEB2.0的元素,注册用户可以通过宣传来获取积分,积分可以换取相应的奖品,大大增加网站的粘合度,从而更加有利于网站的推广服务! 4、历史记录、在线网络收藏夹功能! 对已访问过的网址会本地自动记录,可以在线收藏自己爱好的网址,分类收藏,排序收藏等一系列完整的网络收藏夹功能。 5、模板标签化 程序与模板分离! 前台模版与后台程序完全分离,简单实用的标签调用,可以自己独立制作模板,非常简单实用的标签,子类可以单独设置模板,制作出属于自己的个性网址导航。 6、后台集成各大搜索引擎联盟账号! 百度、淘宝、迅雷、搜狗、谷歌、狗狗、当当、卓越等联盟帐号的集成,增加隐性的收入,你只要去申请一个他们联盟的帐号,然后在后台添加帐号即可,马上用户在使用您的网站搜索时即可产生效益! 详细说明: 1、首页风格图:,支持换肤 2、图片广告后台可自行设置10个,用户打开前台时会自动轮播 3、后台可自行设置默认颜色风格,换肤功能中的皮肤可后台一键升级,以满足不同节假日换肤之用 4、后台可自行添加闻、军事、体育、八卦闻,默认会自动更 5、前台支持多搜索引擎切换,后台可设置默认搜索引擎 6、后台可设置所有网址的小图标,前台会自动兼容小图标 7、前台已经内置多个内页的子频道,比如: 闻频道,影视频道, 电视剧频道, 购物频道, 团购频道, 笑话频道, 小游戏频道, 彩票频道, 快递频道,手机频道, 旅游频道, 军事频道, 股票频道, 音乐频道, 小说频道, 天气频道, 违章查询等频道,并在不断增加中,上线后,还将不断完善! 8、前台所有网址,所有网址类别,所有工具箱,菜单都可以后台自行修改 9、支持QQ登陆,支持ucenter账号互通 10、支持积分系统,包括下线推广,绑定首页积分,注册,登陆积分 11、支持收藏夹,网址观看历史记录功能 12、支持流量交互功。后台自己设置流量按几比几返还,集成流量交互系统,让你的导航导航程序与众不同,点入大于点出,网站自动套红,防作弊,三次验证,精确统计能 13、支持网址批量入库功能。 +++++++++++++++++++++ P S +++++++++++++++++++++++++ 1. logo替换位置:template/模板名/images/下 2. 前台网址、统计代码等全部在位置:19模板功能管理--模块分布图
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值