java对象复制与克隆

Posted zhangjin1120

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java对象复制与克隆相关的知识,希望对你有一定的参考价值。

  • 怎么实现克隆?


public class ObjectCopyTest {
    public static class Person implements Cloneable{
        int age;
        String name;

        static class Info {
           public String address;
        }
        Info info;

        @Override
        protected Person clone()  {
            Person p = null;
            try {
                p = (Person) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return p;
        }
    }

    public static void main(String[] args) {
        Person p = new Person();
        p.age=22;
        p.name = "zhangsan";
        p.info = new Person.Info();
        p.info.address = "xxx";


        Person p2 = p.clone();
        System.out.println(p2.info.address);
        System.out.println(p == p2);
    }
}
  • Cloneable接口内部没有抽象方法,Person为什么要实现Cloneable

    查看Cloneable接口注释如下

    翻译翻译:

    一个类实现指向Object#clone()方法的Cloneable接口,说明该类的对象创建一个字段对字段的副本是合法的。调用Object’s clone方法,在未实现 Cloneable 导致抛出异常CloneNotSupportedException。按照约定,实现此接口的类应该用public方法重写Object.clone()(这个方法是protected)。有关重写此方法的详细信息,请参见Object.clone()。请注意,此接口不包含克隆方法。因此,不可能仅仅因为实现了这个接口就克隆一个对象。即使反射调用clone方法,也不能保证它会成功。

总结一下就是只有实现Cloneable接口,并且重写clone方法,才能实现对象克隆。

  • 难道Cloneable接口只是一个标志,Object.clone()会识别这个标志?

    clone()方法源代码:
  protected native Object clone() throws CloneNotSupportedException;

是一个native方法,源码看不到。这能阻挡我们探索真理吗?不存在的。穷追不舍,Object.c中,有如下几行:

static JNINativeMethod methods[] = {
   {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
   {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
   {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
   {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
   {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

看来,这个clone方法,对应着一个叫JVM_Clone的C函数。继续找,jvm.cpp代码中,找到JVM_Clone如下:


JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
JVMWrapper("JVM_Clone");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
const KlassHandle klass (THREAD, obj->klass());
JvmtiVMObjectAllocEventCollector oam;

#ifdef ASSERT
// Just checking that the cloneable flag is set correct
if (obj->is_javaArray()) {
  guarantee(klass->is_cloneable(), "all arrays are cloneable");
} else {
  guarantee(obj->is_instance(), "should be instanceOop");
  bool cloneable = klass->is_subtype_of(SystemDictionary::Cloneable_klass());
  guarantee(cloneable == klass->is_cloneable(), "incorrect cloneable flag");
}
#endif

// Check if class of obj supports the Cloneable interface.
// All arrays are considered to be cloneable (See JLS 20.1.5)
if (!klass->is_cloneable()) {
  ResourceMark rm(THREAD);
  THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
}

// Make shallow object copy
const int size = obj->size();
oop new_obj = NULL;
if (obj->is_javaArray()) {
  const int length = ((arrayOop)obj())->length();
  new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
} else {
  new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
}
// 4839641 (4840070): We must do an oop-atomic copy, because if another thread
// is modifying a reference field in the clonee, a non-oop-atomic copy might
// be suspended in the middle of copying the pointer and end up with parts
// of two different pointers in the field.  Subsequent dereferences will crash.
// 4846409: an oop-copy of objects with long or double fields or arrays of same
// won't copy the longs/doubles atomically in 32-bit vm's, so we copy jlongs instead
// of oops.  We know objects are aligned on a minimum of an jlong boundary.
// The same is true of StubRoutines::object_copy and the various oop_copy
// variants, and of the code generated by the inline_native_clone intrinsic.
assert(MinObjAlignmentInBytes >= BytesPerLong, "objects misaligned");
Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,
                             (size_t)align_object_size(size) / HeapWordsPerLong);
// Clear the header
new_obj->init_mark();

// Store check (mark entire object and let gc sort it out)
BarrierSet* bs = Universe::heap()->barrier_set();
assert(bs->has_write_region_opt(), "Barrier set does not have write_region");
bs->write_region(MemRegion((HeapWord*)new_obj, size));

// Caution: this involves a java upcall, so the clone should be
// "gc-robust" by this stage.
if (klass->has_finalizer()) {
  assert(obj->is_instance(), "should be instanceOop");
  new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);
}

return JNIHandles::make_local(env, oop(new_obj));
JVM_END

有没有发现

if (!klass->is_cloneable()) {
...
}

大致可以推断,这个Cloneable接口确实是一个判断标志。

参考:Native code in Java

以上是关于java对象复制与克隆的主要内容,如果未能解决你的问题,请参考以下文章

反射实现java深度克隆

Java中对象的深浅克隆之序列化篇

JAVA浅复制与深复制

Java拷贝构造函数初尝试

java怎么样构造函数复制一个对象

java,,"浅克隆,只复制一个对象 ,深克隆 对象和引用一起复制"有java实例吗?不理解啊