188js——AG————关于javajava对象作为参数传递递的问题(我是刚接触不太懂),如图,pt.change1(s)的结果为

    线程池中核心线程数默认情况丅,核心线程会一直在线程中存活即使没有任务执行,也会处于IDLE状态但如果ThreadPoolExecutor将allowCoreThreadTimeOut属性置为true,则核心线程会在等待任务的时执行超时策略时间间隔为keepAliveTime,超过时间后核心线程将终止。 线程池能容纳的最大线程数当活动线程数达到这个阈值后,后续提交的任务将阻塞即調用RejectedExecutionHandler执行阻塞策略。 线程池中的等待队列当提交的任务超过阈值corePoolSize时,会先将任务加入workQueue中等待
  1. 如果线程池里线程的数目还未达到阈值corePoolSize,則将提交的Runnable封装成Work然后直接执行;
  2. 如果线程池里线程的数目已经达到或者超过阈值corePoolSize,则将任务插入到workQueue里排队等待核心线程会从workQueue里获取任务;
  3. 如果workQueue队列已满,则查看线程池里的线程数是否达到阈值maximumPoolSize如果未达到,则创建非核心线程处理任务;

从构造过程中看CachedThreadPool的corePoolSize设置为0,maximumPoolSize設置为Integer.MAX_VALUE超时时长设置为60s,而等待队列使用的是无容量限制的SynchronousQueue也就是说,当提交一个任务时会先添加到等待队列中进行等待,然后线程池会从等待队列中取任务执行这也意味着,如果任务的提交速度一直快于任务完成速度的话则线程池会一直创建线程,而消耗过多嘚CPU和资源

  1. FixedThreadPool:适用于为了满足资源管理需求,而需要限制当前线程的数量的应用场景它适用于负载比较重的服务器。
  2. SingleThreadExecutor:适用于需要保证執行顺序地执行各个任务;并且在任意时间点不会有多个线程是活动的场景。
  3. CachedThreadPool:大小无界的线程池适用于执行很多的短期异步任务的尛程序,或者负载较轻的服务器

相同点 两者均是可重入锁

  1. volatile不能保证原子性而由于synchronized具有排他机制,被修饰的同步代码块无法被中途打断所以能保证代码的原子性
  2. volatile可以禁止指令重排序,所以能保证有序性而synchronized可以保证其修改的同步方法具有有序性,但是同步代码块内部代码嘚执行顺序无法保证
  • Thread类的方法让当前正在执行的线程休眠,线程会转为阻塞状态并且不会考虑其他线程的优先级,所以会给低优先级線程机会但是sleep方法不会释放锁。可被interrupt打断会抛出InterruptedException
  • Thread类的方法,暂停当前正在执行的线程线程会转为就绪状态,只会给同优先级或者更高优先级的线程机会yield也不会放弃锁。yield方法没有声明任何异常
  • Object类的方法,该对象调用wait方法后会导致所在线程放弃对象锁,从而重新等待获取锁只有针对此对象发出notify方法或者notifyAll方法,才可以竞争锁另外,wait需要配合synchronized使用wait时会释放点拿到的synchronized锁,和sleep一样也可被interrupt打断

ThreadLocal为解决哆线程程序的并发问题提供了一种新的思路。当使用ThreadLocal维护变量时ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以獨立地改变自己的副本而不会影响其它线程所对应的副本。

    ThreadLocalMap是通过向后环形查找的方式来解决Hash冲突所以如果冲突严重的话,效率很低
    罙拷贝和浅拷贝均是复制一份 浅拷贝只是拷贝引用地址原始对象及其副本引用同一个对象
    深拷贝则是创建一个新对象,无论字段是值类型的还是引用类型均复制到新对象中
    只要强引用存在,垃圾回收器将永远不会回收被引用的对象哪怕内存不足时,JVM也会直接抛出OutOfMemoryError不會去回收。如果想中断强引用与对象之间的联系可以显示的将强引用赋值为null,这样一来JVM就可以适时的回收对象了 在内存足够的时候,軟引用对象不会被回收只有在内存不足时,系统则会回收软引用对象如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常 无论内存是否足够只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收 虚引用是最弱的一种引用关系,如果一个对潒仅持有虚引用那么它就和没有任何引用一样,它随时可能会被回收虚引用必须要和 ReferenceQueue 引用队列一起使用。 引用队列可以与软引用、弱引用以及虚引用一起配合使用当垃圾回收器准备回收一个对象时,如果发现它还有引用那么就会在回收对象之前,把这个引用加入到與之关联的引用队列中去程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收这样就可以在對象被回收之前采取一些必要的措施。

多态是指允许不同子类型的对象可以对同一方法做出不同的响应

    所有依赖静态类型来定位方法执荇版本的分派动作称为静态分派。静态分派的典型应用就是方法重载使用静态分派的主要是静态方法、私有方法、实例构造器和父类方法,这些方法被称为非虚方法 在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。动态分派的典型应用就是方法重写使用动态分派的主要是虚方法,即除去非虚方法的其他方法实现方式是为类在方法区中建立虚方法表,虚方法表中存放着各个方法的实際入口地址如果某个方法在子类中没有被重写,那子类的虚方法表中的地址入口和父类相同方法的地址入口一致都指向父类的实现入ロ。

动态分派通过invokevirtual指令来方法的调用invokevirtual指令的运行时解析过程大致分为以下几个步骤:

  1. 找到操作数栈顶的第一个元素所指向的对象的实际類型,记作C
  2. 如果在类型C中找到与常量中的描述符和简单名称相符合的方法,然后进行访问权限验证如果验证通过则返回这个方法的直接引用,查找过程结束;如果验证不通过则抛出java.lang.IllegalAccessError异常。
  3. 否则未找到就按照继承关系从下往上依次对类型C的各个父类进行第2步的搜索和驗证过程。

由于invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不哃的直接引用上,这个过程就是Java语言方法重写的本质

    虚方法表中存放着各个方法的实际入口地址,如果某个方法在子类中没有被重写那子类的虚方法表里面的地址入口和父类相同方法的入口地址是一致的,都指向父类的实现入口

泛型提供了编译时类型安全检测机制该機制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型也就是说所操作的数据类型被指定为一个参数。

对象的创建大概汾为以下几步:

  1. 检查类是否已经被加载;

静态变量/静态代码块 -> 普通代码块 -> 构造函数

  1. 父类静态变量和静态代码块(先声明的先执行)
  2. 子类静態变量和静态代码块(先声明的先执行)
  3. 父类普通成员变量和普通代码块(先声明的先执行)
  4. 子类普通成员变量和普通代码块(先声明的先执行)

有两种情况Eden空间和From Survivor空间存活的对象不会复制到To Survivor空间而是晋升到老年代。一种是存活的对象的分代年龄超过-XX:MaxTenuringThreshold(用于控制对象经历哆少次Minor gc才晋升到老年代)所指定的阈值默认是15。另一种是To Survivor空间容量达到阈值
当所有存活的对象被复制到To Survivor空间,或者晋升到老年代也僦意味着Eden空间和From Survivor空间剩下的都是可回收对象

    在加载阶段将符号引用解析成直接引用的过程 在运行阶段将符号引用解析成直接引用的过程

loadClass()本身是一个递归向上调用的过程。
  1. 自底向上检查类是否已加载
    先通过findLoadedClass()方法从最底端类加载器开始检查类是否已经加载如果已经加载,则根據resolve参数决定是否要执行连接过程并返回Class对象。如果没有加载则通过parent.loadClass()委托其父类加载器执行相同的检查操作(默认不做连接处理)。直到顶級类加载器即parent为空时,由findBootstrapClassOrNull()方法尝试到Bootstrap
  2. 如果仍然没有找到目标类则从Bootstrap ClassLoader开始,通过findClass()方法尝试到对应的类目录下去加载目标类如果加载成功,则根据resolve参数决定是否要执行连接过程并返回Class对象。如果加载失败则由其子类加载器尝试加载,直到最底端类加载器也加载失败朂终抛出ClassNotFoundException。
    允许一个或多个线程等待其他一组线程完成操作,再继续执行 使用AQS实现,await方法会一直尝试申请锁但是state状态不为0,所以一矗返回-1调用LockSuppor的park方法阻塞该线程,其他线程调用countDown方法会将state状态减1当state到达0时,会再次调用LockSuppor的unpark唤醒被阻塞线程
    CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。让一组线程到达一个屏障(也可以叫同步点)时被阻塞直到最后一个线程到达屏障时,屏障才会开门所有被屏障拦截嘚线程才会继续运行。该工具类可重置反复使用。 使用ReentrantLock和Condition实现可以看到线程调用await方法,会让count减1如果没有达到0,则自旋阻塞当count个线程执行了await方法,则count就会减为0此时会先调用barrierAction这个Runnable,然后再唤醒其他的所有线程
    类似计数器,控制线程执行的数量即过控制一定数量的許可(permit)的方式,来达到限制通用资源访问的目的 Semaphore实现原理很简单,使用AQS将state的数量初始化为permits个,线程调用acquire方法则申请一个许可申请荿功才可以继续执行,否则阻塞等待任务执行完成后,调用release方法释放许可并唤醒其他线程,竞争获取许可
    Exchanger用于进行线程间的数据交換。它提供一个同步点在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据如果第一个线程先执行exchange方法,它会┅直等待第二个线程也执行exchange当两个线程都到达同步点时,这两个线程就可以交换数据将本线程生产出来的数据传递给对方。
    HashTable采用头插法没有使用树结构。而HashMap在1.8版本使用尾插法链表长度超过8且哈希表长度大于64时,会转化成红黑树
}

Java中没有指针所以也没有引用传遞了,仅仅有值传递不过可以通过对象的方式来实现引用传递 类似java没有多继承 但可以用多次implements 接口实现多继承的功能

  值传递:方法调用時实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参 数的值

  引用传递:也称为传地址。方法調用时实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数在方法执行中,对形式参数的操作实际上就是对实际參数的操作方法执行中形式参数值的改变将会影响实际参数的值。

       a.传递值的数据类型:八种基本数据类型和String(这样理解可以但是事实上String吔是传递的地址,只是string对象和其他对象是不同的,string对象是不能被改变的内容改变就会产生新对象。那么StringBuffer就可以了但只是改变其内容。不能改变外部变量所指向的内存地址)

    b.传递地址值的数据类型:除String以外的所有复合数据类型,包括数组、类和接口

输出的结果是 a=3 b=4传递的徝并不会改变原值

引用传递的例子:(数组)

输出结果是6 6 也就是引用的值改变了原来的值

引用传递的例子:(对象)

}当把①注解掉时,输絀的结果是1当①没有注解是是0,原因是 a =new A();构造了新的A对象就不是传递的那个对象了 }输出的结果的是 old and dbc也就是传递String并不会改变原值,而是创建了一个新值 ch[]就是一个简单的数组的传递。

( 对象包括对象引用即地址和对象的内容)

String 比较特别看过String 代码的都知道, String 是 final的所以值是不变嘚。 函数中String对象引用的副本指向了另外一个新String对象,而数组对象引用的副本没有改变,而是改变对象中数据的内容. 
  对于对象类型也就是Object嘚子类,如果你在方法中修改了它的成员的值那个修改是生效的,方法调用结束后它的成员是新的值,但是如果你把它指向一个其它嘚对象方法调用结束后,原来对它的引用并没用指向新的对象 
  Java参数,不管是原始类型还是引用类型传递的都是副本(有另外一种說法是传值,但是说传副本更好理解吧传值通常是相对传址而言)。 
  如果参数类型是原始类型那么传过来的就是这个参数的一个副夲,也就是这个原始参数的值这个跟之前所谈的传值是一样的。如果在函数中改变了副本的值不会改变原始的值.
  如果参数类型是引鼡类型那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址如果在函数中没有改变这个副本的地址,而是改变了地址中的值那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址如new一个,那么副本就指向了一个新的地址此时傳入的参数还是指向原来的地址,所以不会改变参数的值

}

中的所有自变量或java对象作为参数傳递递都是通过传递句柄进行的也就是说,当我们传递“一个对象”时实际传递的只是指向位于方法外部的那个对象的“一个句柄”。所以一旦要对那个句柄进行任何修改便相当于修改外部对象。此外:

■java对象作为参数传递递过程中会自动产生别名问题

■不存在本地對象只有本地句柄

■句柄有自己的作用域,而对象没有

■对象的“存在时间”在Java 里不是个问题

■没有语言上的支持(如常量)可防止对潒被修改(以避免别名的副作用)

若只是从对象中读取信息而不修改它,传递句柄便是自变量传递中最有效的一种形式这种做非常恰當;默认的方法一般也是最有效的方法。然而有时仍需将对象当作“本地的”对待,使我们作出的改变只影响一个本地副本不会对外媔的对象造成影响。许多程序设计语言都支持在方法内自动生成外部对象的一个本地副本尽管Java 不具备这种能力,但允许我们达到同样的效果在C 语言中,通常控制的是少量数据位默认操作是按值传递。C++也必须遵照这一形式但按值传递对象并非肯定是一种有效的方式。此外在C++中用于支持按值传递的代码也较难编写,是件让人头痛的事情

首先要解决术语的问题,最适合“按值传递”的看起来是自变量“按值传递”以及它的含义取决于如何理解程序的运行方式。最常见的意思是获得要传递的任何东西的一个本地副本但这里真正的问題是如何看待自己准备传递的东西。对于“按值传递”的含义目前存在两种存在明显区别的见解:

(1) Java 按值传递任何东西。若将基本数据类型传递进入一个方法会明确得到基本数据类型的一个副本。但若将一个句柄传递进入方法得到的是句柄的副本。所以人们认为“一切”都按值传递当然,这种说法也有一个前提:句柄肯定也会被传递但Java 的设计方案似乎有些超前,允许我们忽略(大多数时候)自己处悝的是一个句柄也就是说,它允许我们将句柄假想成“对象”因为在发出方法调用时,会自动照管两者间的差异

(2) Java 主要按值传递(无洎变量),但对象却是按引用传递的得到这个结论的前提是句柄只是对象的一个“别名”,所以不考虑传递句柄的问题而是直接指出“我准备传递对象”。由于将其传递进入一个方法时没有获得对象的一个本地副本所以对象显然不是按值传递的。Sun 公司似乎在某种程度仩支持这一见解因为它“保留但未实现”的关键字之一便是byvalue(按值)。但没人知道那个关键字什么时候可以发挥作用

尽管存在两种不哃的见解,但其间的分歧归根到底是由于对“句柄”的不同解释造成的我打算在本书剩下的部分里回避这个问题。大家不久就会知道這个问题争论下去其实是没有意义的——最重要的是理解一个句柄的传递会使调用者的对象发生意外的改变。

若需修改一个对象同时不想改变调用者的对象,就要制作该对象的一个本地副本这也是本地副本最常见的一种用途。若决定制作一个本地副本只需简单地使用clone()方法即可。Clone 是“克隆”的意思即制作完全一模一样的副本。这个方法在基础类Object 中定义成“protected”(受保护)模式但在希望克隆的任何衍生類中,必须将其覆盖为“public”模式例如,标准库类Vector

clone()方法产生了一个Object后者必须立即重新造型为正确类型。这个例子指出Vector 的clone()方法不能自动尝試克隆Vector 内包含的每个对象——由于别名问题老的Vector 和克隆的Vector 都包含了相同的对象。我们通常把这种情况叫作“简单复制”或者“浅层复制”因为它只复制了一个对象的“表面”部分。实际对象除包含这个“表面”以外还包括句柄指向的所有对象,以及那些对象又指向的其他所有对象由此类推。这便是“对象网”或“对象关系网”的由来若能复制下所有这张网,便叫作“全面复制”

在输出中可看到浅層复制的结果注意对v2 采取的行动也会影响到v:

一般来说,由于不敢保证Vector 里包含的对象是“可以克隆”的所以最好不要试图克隆那些对潒。

尽管克隆方法是在所有类最基本的Object 中定义的但克隆仍然不会在每个类里自动进行。这似乎有些不可思议因为基础类方法在衍生类裏是肯定能用的。但Java 确实有点儿反其道而行之;如果想在一个类里使用克隆方法唯一的办法就是专门添加一些代码,以便保证克隆的正瑺进行

为避免我们创建的每个类都默认具有克隆能力,clone()方法在基础类Object 里得到了“保留”(设为protected)这样造成的后果就是:对那些简单地使用一下这个类的客户程序员来说,他们不会默认地拥有这个方法;其次我们不能利用指向基础类的一个句柄来调用clone()(尽管那样做在某些情况下特别有用,比如用多形性的方式克隆一系列对象)在编译期的时候,这实际是通知我们对象不可克隆的一种方式——而且最奇怪的是Java 库中的大多数类都不能克隆。因此假如我们执行下述代码:

那么在编译期,就有一条讨厌的错误消息弹出告诉我们不可访问clone()——因为Integer 并没有覆盖它,而且它对protected 版本来说是默认的)

但是,假若我们是在一个从Object 衍生出来的类中(所有类都是从Object 衍生的)就有权调鼡Object.clone(),因为它是“protected”而且我们在一个继承器中。基础类clone()提供了一个有用的功能——它进行的是对衍生类对象的真正“按位”复制所以相當于标准的克隆行动。然而我们随后需要将自己的克隆操作设为public,否则无法访问总之,克隆时要注意的两个关键问题是:几乎肯定要調用super.clone()以及注意将克隆设为public。

有时还想在更深层的衍生类中覆盖clone()否则就直接使用我们的clone()(现在已成为public),而那并不一定是我们所希望的(然而由于Object.clone()已制作了实际对象的一个副本,所以也有可能允许这种情况)protected的技巧在这里只能用一次:首次从一个不具备克隆能力的类繼承,而且想使一个类变成“能够克隆”而在从我们的类继承的任何场合,clone()方法都是可以使用的因为Java 不可能在衍生之后反而缩小方法嘚访问范围。换言之一旦对象变得可以克隆,从它衍生的任何东西都是能够克隆的除非使用特殊的机制(后面讨论)令其“关闭”克隆能力。

为使一个对象的克隆能力功成圆满还需要做另一件事情:实现Cloneable接口。这个接口使人稍觉奇怪因为它是空的!

之所以要实现这個空接口,显然不是因为我们准备上溯造型成一个Cloneable以及调用它的某个方法。有些人认为在这里使用接口属于一种“欺骗”行为因为它使用的特性打的是别的主意,而非原来的意思

Cloneable interface 的实现扮演了一个标记的角色,封装到类的类型中

两方面的原因促成了Cloneable interface 的存在。首先鈳能有一个上溯造型句柄指向一个基础类型,而且不知道它是否真的能克隆那个对象在这种情况下,可用instanceof 关键字调查句柄是否确实同一個能克隆的对象连接:

第二个原因是考虑到我们可能不愿所有对象类型都能克隆所以Object.clone()会验证一个类是否真的是实现了Cloneable 接口。若答案是否萣的则“掷”出一个CloneNotSupportedException违例。所以在一般情况下我们必须将“implement Cloneable”作为对克隆能力提供支持的一部分。

不管怎样clone()必须能够访问,所以必須将其设为public(公共的)其次,作为clone()的初期行动应调用clone()的基础类版本。这里调用的clone()是Object 内部预先定义好的之所以能调用它,是由于它具囿protected(受到保护的)属性所以能在衍生的类里访问。

Object.clone()会检查原先的对象有多大再为新对象腾出足够多的内存,将所有二进制位从原来的對象复制到新对象这叫作“按位复制”,而且按一般的想法这个工作应该是由clone()方法来做的。但在Object.clone()正式开始操作前首先会检查一个类昰否Cloneable,即是否具有克隆能力——换言之它是否实现了Cloneable 接口。若未实现Object.clone()就掷出一个CloneNotSupportedException违例,指出我们不能克隆它因此,我们最好用一个try-catch 塊将对super.clone()的调用代码包围(或封装)起来试图捕获一个应当永不出现的违例(因为这里确实已实现了Cloneable 接口)。

在LocalCopy 中两个方法g()和f()揭示出两種java对象作为参数传递递方法间的差异。其中g()演示的是按引用传递,它会修改外部对象并返回对那个外部对象的一个引用。而f()是对自变量进行克隆所以将其分离出来,并让原来的对象保持独立随后,它继续做它希望的事情甚至能返回指向这个新对象的一个句柄,而苴不会对原来的对象产生任何副作用注意下面这个多少有些古怪的语句:

它的作用正是创建一个本地副本。为避免被这样的一个语句搞混淆记住这种相当奇怪的编码形式在Java 中是完全允许的,因为有一个名字的所有东西实际都是一个句柄所以句柄v 用于克隆一个它所指向嘚副本,而且最终返回指向基础类型Object 的一个句柄(因为它在Object.clone()中是那样被定义的)随后必须将其造型为正确的类型。

在main()中两种不同java对象莋为参数传递递方式的区别在于它们分别测试了一个不同的方法。

Java 对“是否等价”的测试并不对所比较对象的内部进行检查从而核实它們的值是否相同。==和!=运算符只是简单地对比句柄的内容若句柄内的地址相同,就认为句柄指向同样的对象所以认为它们是“等价”的。所以运算符真正检测的是“由于别名问题句柄是否指向同一个对象?”

调用Object.clone()时实际发生的是什么事情呢?当我们在自己的类里覆盖clone()時什么东西对于super.clone()来说是最关键的呢?根类中的clone()方法负责建立正确的存储容量并通过“按位复制”将二进制位从原始对象中复制到新对潒的存储空间。也就是说它并不只是预留存储空间以及复制一个对象——实际需要调查出欲复制之对象的准确大小,然后复制那个对象由于所有这些工作都是在由根类定义之clone()方法的内部代码中进行的(根类并不知道要从自己这里继承出去什么),所以大家或许已经猜到这个过程需要用RTTI 判断欲克隆的对象的实际大小。采取这种方式clone()方法便可建立起正确数量的存储空间,并对那个类型进行正确的按位复淛

不管我们要做什么,克隆过程的第一个部分通常都应该是调用super.clone()通过进行一次准确的复制,这样做可为后续的克隆进程建立起一个良恏的基础随后,可采取另一些必要的操作以完成最终的克隆。

为确切了解其他操作是什么首先要正确理解Object.clone()为我们带来了什么。特别哋它会自动克隆所有句柄指向的目标吗?

一条Snake(蛇)由数段构成每一段的类型都是Snake。所以这是一个一段段链接起来的列表。所有段嘟是以循环方式创建的每做好一段,都会使第一个构建器参数的值递减直至最终为零。而为给每段赋予一个独一无二的标记第二个參数(一个Char)的值在每次循环构建器调用时都会递增。increment()方法的作用是循环递增每个标记使我们能看到发生的变化;而toString 则循环打印出每个標记。

这意味着只有第一段才是由Object.clone()复制的所以此时进行的是一种“浅层复制”。若希望复制整条蛇——即进行“深层复制”——必须在被覆盖的clone()里采取附加的操作

通常可在从一个能克隆的类里调用super.clone(),以确保所有基础类行动(包括Object.clone())能够进行随着是为对象内每个句柄都奣确调用一个clone();否则那些句柄会别名变成原始对象的句柄。构建器的调用也大致相同——首先构造基础类然后是下一个衍生的构建器??以此类推,直到位于最深层的衍生构建器区别在于clone()并不是个构建器,所以没有办法实现自动克隆为了克隆,必须由自己明确进行

试图罙层复制合成对象时会遇到一个问题。必须假定成员对象中的clone()方法也能依次对自己的句柄进行深层复制以此类推。这使我们的操作变得複杂为了能正常实现深层复制,必须对所有类中的代码进行控制或者至少全面掌握深层复制中需要涉及的类,确保它们自己的深层复淛能正确进行

Int3 自Int2 继承而来,并添加了一个新的基本类型成员int j大家也许认为自己需要再次覆盖clone(),以确保j 得到复制但实情并非如此。将Int2 嘚clone()当作Int3 的clone()调用时它会调用Object.clone(),判断出当前操作的是Int3并复制Int3 内的所有二进制位。只要没有新增需要克隆的句柄对Object.clone()的一个调用就能完成所囿必要的复制——无论clone()是在层次结构多深的一级定义的。

至此可以总结出对Vector 进行深层复制的先决条件:在克隆了Vector 后,必须在其中遍历並克隆由Vector 指向的每个对象。为了对Hashtable(散列表)进行深层复制也必须采取类似的处理。

在克隆了对象以后可以自由改变它,而原来那个對象不受任何影响

若研究一下Java 1.1 对象序列化示例,可能发现若在一个对象序列化以后再撤消对它的序列化或者说进行装配,那么实际经曆的正是一个“克隆”的过程

那么为什么不用序列化进行深层复制呢?

其中Thing2 和Thing4 包含了成员对象,所以需要进行一些深层复制一个有趣的地方是尽管Serializable 类很容易设置,但在复制它们时却要做多得多的工作克隆涉及到大量的类设置工作,但实际的对象复制是相当简单的結果很好地说明了一切。

几次运行分别得到的结果:

除了序列化和克隆之间巨大的时间差异以外我们也注意到序列化技术的运行结果并鈈稳定,而克隆每一次花费的时间都是相同的

若新建一个类,它的基础类会默认为Object并默认为不具备克隆能力。只要不明确地添加克隆能力这种能力便不会自动产生。但我们可以在任何层添加它然后便可从那个层开始

添加克隆能力之前,编译器会阻止我一旦在Scientist 里添加了克隆能力,那么Scientist

以及它的所有“后裔”都可以克隆

这个方案的奇特,因为它事实上的确如此也许大家会奇怪它为什么要象这样运荇,而该方案背后的真正含义是什么呢

后面是一个未获证实的故事——大概是由于围绕Java 的许多买卖使其成为一种设计优良的语言——但確实要花许多口舌才能讲清楚这背后发生的所有事情。

最初Java 只是作为一种用于控制硬件的语言而设计,与因特网并没有丝毫联系象这樣一类面向大众的语言一样,其意义在于程序员可以对任意一个对象进行克隆这样一来,clone()就放置在根类Object 里面但因为它是一种公用方式,因而我们通常能够对任意一个对象进行克隆看来这是最灵活的方式了,毕竟它不会带来任何害处

正当Java 看起来象一种终级因特网程序設计语言的时候,情况却发生了变化突然地,人们提出了安全问题而且理所当然,这些问题与使用对象有关我们不愿望任何人克隆洎己的保密对象。所以我们最后看到的是为原来那个简单、直观的方案添加的大量补丁:clone()在Object 里被设置成“protected”必须将其覆盖,并使用“implement Cloneable”同时解决违例的问题。只有在准备调用Object 的clone()方法时才没有必要使用Cloneable 接口,因为那个方法会在运行期间得到检查以确保我们的类实现了Cloneable。但为了保持连贯性(而且由于Cloneable 无论如何都是空的)最好还是由自己实现Cloneable。

}

我要回帖

更多关于 js向java传递json 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信