java super.clone_Java高级特性:clone()方法

源码

public class Objcet{

protected native Object clone() throws CloneNotSupportedException();

}

由源码可知。

第一:Objcet类的clone()方法是一个native方法。native方法的执行效率一般远高于Java中的非native方法(一般不是java语言所写)。这也解释了为什么要用Object的clone()方法,而不是先new一个类,然后把原始对象复制到新对象中,虽然这样也能实现clone功能。(JNI是Java Native Interface的 缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。)

第二:Object类中的clone()方法被protected修饰符修饰。(关于protected修饰符见我的其它文章。)这意味着clone()方法只对java.util.lang包可见,和继承了Object类的子类可见。当然所有类都是继承了Object类。

为什么要用protected修饰呢?

为了安全,安全性从两方面考虑。首先我们要clone一个Employee对象,应该只有Employee类能够克隆Employee对象。使用protected修饰符保证了其它不相关的类无法克隆Employee对象。其次是Object对要复制的对象一无所知,它只会逐域进行复制,如果对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题。但是如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样原对象和克隆的对象还会共享一些信息。所以默认的拷贝都是浅拷贝。要想在任何地方都使用clone方法,我们必须将它改为public类型,并且实现Cloneable接口。如果一开始就是public,我们就分不清到底是浅拷贝还是深拷贝了。使用protected和Cloneable就约束了类设计者要了解自己的克隆过程。

Cloneable接口是标记接口,它不包含任何需要实现的方法。如果一个对象请求克隆,但没有实现这个接口,就会产生一个受检查异常,因为clone方法默认实现中有使用instanceof进行接口判断。

这里给出代码

//cloneTest.java

package com.testbase.clone;

public class cloneTest {

public static void main(String[] args){

// 虽然我们已经实现了Cloneable接口,不会产生异常

// 但是编译器并不知道,会报错,所以这里要捕获异常

try

{

Employee tobin = new Employee(30000);

int salary = tobin.getSalary();

System.out.println(salary);

Employee shengsheng = tobin.clone();

int shengSalary = shengsheng.getSalary();

System.out.println(shengSalary);

}

catch (CloneNotSupportedException e)

{

e.printStackTrace();

}

}

}

//Employee.java

package com.testbase.clone;

public class Employee implements Cloneable

{

private int salary;

public Employee()

{

}

public Employee(int asalary)

{

salary = asalary;

}

@Override

public Employee clone() throws CloneNotSupportedException

{

Employee cloned = (Employee) super.clone();

return cloned;

}

public int getSalary() {

return salary;

}

}

第三:Object.clone()方法返回一个Object对象。我们必须进行强制转换才能得到我们需要的类型。(强制转换一定能成功吗?如果重写了clone方法一定成功。但是如果没有重写,不能转换。clone的是父类Object,无法向下造型,子类重写了clone方法,拷贝就是当前类的对象,暂时转为Object,还可以通过强制类型转换回来)

给出一段错误代码。在Employee.java中拷贝是因为在其它类中拷贝一定不成功。因为Object类clone方法的protected特性。只有继承了它的子类和java.util.lang包可以调用clone()方法。所以选择在子类Employee中测试。

package com.testbase.clone;

public class Employee implements Cloneable

{

private int salary;

public Employee()

{

}

public Employee(int asalary)

{

salary = asalary;

}

// @Override

// public Employee clone() throws CloneNotSupportedException

// {

// Employee cloned = (Employee) super.clone();

// return cloned;

// }

public int getSalary() {

return salary;

}

public static void main(String[] args){

try

{

Employee tobin = new Employee(30000);

int salary = tobin.getSalary();

System.out.println(salary);

Object shengsheng = tobin.clone(); //这里不能强制类型转换

int shengSalary = shengsheng.getSalary();//这里报错,因为Object没有getSalary方法

System.out.println(shengSalary);

}

catch (CloneNotSupportedException e)

{

e.printStackTrace();

}

}

}

深拷贝和浅拷贝

浅拷贝:拷贝引用,但是不拷贝引用指向的对象,对拷贝引用的对象进行修改,两份拷贝都会被修改。如果源对象和浅拷贝对象所共享的子对象都是不可变的,那么这种共享就是安全的。比如String类。或者在对象的生命周期内,子对象一直包含着不变的常量,没有更改器方法会改变它,也没有方法会生成它的引用,这种情况同样是安全的。

深拷贝:可能会更改的子对象也进行了拷贝。要进行深拷贝,需要一层层地重写clone方法。

数组是深拷贝。所有数组类型都有一个public的clone()方法,可以用这个方法建立一个新数组。

int[] a = {1,2,3,4};

int[] cloned = a.clone();

cloned[0]=11;

对象串行化实现拷贝

常见面试题

1.为什么进行拷贝

因为我们某个对象实例现在需要保存一些有效值,我们希望生成一个和原来一样的对象,对这个对象的修改不改变原对象的属性。

2.深拷贝和浅拷贝的区别

浅拷贝对基本数据类型生成一份新的拷贝,对引用类型,只是复制了引用,引用所指向的那块内存空间并没有拷贝

深拷贝对引用指向的那块内存空间也拷贝了一份(新内存空间)。换言之深拷贝要把复制的对象所引用的对象也都拷贝了一遍。

3.String克隆的特殊性在那里?StringBuffer和StringBuilder呢?

对基本数据类型都能自动实现深拷贝。而对引用类型是浅拷贝。String是引用类型的特例。因为String是不允许修改的。所以相当于进行了深拷贝,是安全的。由于String是不可变类,对String类中的很多修改操作都是通过new对象复制处理的。所以当我们修改clone前后对象里面String属性的值时,实际上就指向了新的内存空间。自然对clone前的源对象没有影响,类似于深拷贝。虽然它是引用类型,但是不影响我们深拷贝的使用。

而对于StringBuffer和StringBuilder,需要主动进行clone重写。否则就是浅拷贝。

4.实现对象克隆的常见方式有哪些,具体怎么做?

常用的方式有三种。

通过自己写一个clone方法,new一个同样的对象,赋值实现深度克隆,繁琐容易出错。

通过实现Cloneable接口并重写Object类的clone方法,分为深浅两种方式。

通过Serializable接口并用对象的序列化和反序列化来实现真正的深拷贝。

代码,主要是第2和第3个方法。

通过实现 Cloneable 接口并重写 Object 类的 clone() 方法实现浅克隆做法:Object 类中 clone 方法的默认实现最终是一个 native 方法(如果 clone 类没有实现 Cloneable 接口并调用了 Object 的 clone 方法就会抛出 CloneNotSupportedException 异常,因为 clone 方法默认实现中有使用 instanceof 进行接口判断),相对来说效率高些,默认实现是先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容,对基本数据类型就是值复制,而对非基本类型变量保存的仅仅是对象的引用,所以会导致 clone 后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。

浅拷贝。

class Employee implements Clonealbe{

public Employee clone() throws CloneNotSupportedException

{

return (Employee) super.clone();

}

}

深拷贝

class Employee implements Clonealbe{

public Employee clone() throws CloneNotSupportedException

{

Employee cloned = (Employee) super.clone();

cloned.hireDay = (Date) hireDay.clone();

return cloned();

}

}

通过Serializable接口并用对象的序列化和反序列化来实现真正的深拷贝。(还未学到)

class CloneUtil {

public static T clone(T obj) {

T cloneObj = null;

try {

ByteArrayOutputStream byteOut = new ByteArrayOutputStream();

ObjectOutputStream objOut = new ObjectOutputStream(byteOut);

objOut.writeObject(obj);

objOut.close();

ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());

ObjectInputStream objIn = new ObjectInputStream(byteIn);

cloneObj = (T) objIn.readObject();

objIn.close();

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

return cloneObj;

}

}

class InfoBean implements Serializable {

public String name;

public int age;

}

class PeopleBean implements Serializable {

public String vipId;

public InfoBean infoBean;

public Object clone() {

return CloneUtil.clone(this);

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值