`
txf2004
  • 浏览: 6866679 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

深入理解java的clone

 
阅读更多
预备知识
为什么要clone
Object的clone以及为什么如此实现
如何clone
对clone的态度
其他的选择
和Serializable的比较
性能

预备知识

为了理解java的clone,有必要先温习以下的知识。
java的类型,java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。
java引用类型的存储,java的引用类型都是存储在堆上的。
Java代码 收藏代码
  1. publicclassB{
  2. inta;
  3. Stringb;
  4. publicB(inta,Stringb){
  5. super();
  6. this.a=a;
  7. this.b=b;
  8. }
  9. }

对这样一个引用类型的实例,我们可以推测,在堆上它的内存存储形式(除去指向class的引用,锁的管理等等内务事务所占内存),应该有一个int值表示a,以及一个引用,该引用指向b在堆上的存储空间。


为什么要clone

恩,因为需要。废话。
有名的GoF设计模式里有一个模式为原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
简单的说就是clone一个对象实例。使得clone出来的copy和原有的对象一模一样。

插一个简单使用clone的例子,如果一个对象内部有可变对象实例的话,public API不应该直接返回该对象的引用,以防调用方的code改变该对象的内部状态。这个时候可以返回该对象的clone。

问题来了,什么叫一模一样。
一般来说,有
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
但是这些都不是强制的。
我们需要什么样的clone就搞出什么样的clone好了。
一般而言,我们要的clone应该是这样的。copy和原型的内容一样,但是又是彼此隔离的。即在clone之后,改变其中一个不影响另外一个。


Object的clone以及为什么如此实现

Object的clone的行为是最简单的。以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。
看例子。
Java代码 收藏代码
  1. classUser{
  2. Stringname;
  3. intage;
  4. }
  5. classAccountimplementsCloneable{
  6. Useruser;
  7. longbalance;
  8. @Override
  9. publicObjectclone()throwsCloneNotSupportedException{
  10. returnsuper.clone();
  11. }
  12. }


Java代码 收藏代码
  1. //user.
  2. Useruser=newUser();
  3. user.name="user";
  4. user.age=20;
  5. //account.
  6. Accountaccount=newAccount();
  7. account.user=user;
  8. account.balance=10000;
  9. //copy.
  10. Accountcopy=(Account)account.clone();
  11. //balance因为是primitive,所以copy和原型是相等且独立的。
  12. Assert.assertEquals(copy.balance,account.balance);
  13. copy.balance=20000;
  14. //改变copy不影响原型。
  15. Assert.assertTrue(copy.balance!=account.balance);
  16. //user因为是引用类型,所以copy和原型的引用是同一的。
  17. Assert.assertTrue(copy.user==account.user);
  18. copy.user.name="newName";
  19. //改变的是同一个东西。
  20. Assert.assertEquals("newName",account.user.name);



恩,默认实现是帮了我们一些忙,但是不是全部。
primitive的确做到了相等且隔离。
引用类型仅仅是复制了一下引用,copy和原型引用的东西是一样的。
这个就是所谓的浅copy了。
要实现深copy,即复制原型中对象的内存copy,而不仅仅是一个引用。只有自己动手了。
等等,是不是所有的引用类型都需要深copy呢?
不是!
我们之所以要深copy,是因为默认的实现提供的浅copy不是隔离的,换言之,改变copy的东西,会影响到原型的内部。比如例子中,改变copy的user的name,影响了原型。
如果我们要copy的类是不可变的呢,如String,没有方法可以改变它的内部状态呢。
Java代码 收藏代码
  1. classUserimplementsCloneable{
  2. Stringname;
  3. intage;
  4. @Override
  5. publicObjectclone()throwsCloneNotSupportedException{
  6. returnsuper.clone();
  7. }
  8. }

Java代码 收藏代码
  1. //user.
  2. Useruser=newUser();
  3. user.name="user";
  4. user.age=20;
  5. //copy
  6. Usercopy=(User)user.clone();
  7. //age因为是primitive,所以copy和原型是相等且独立的。
  8. Assert.assertEquals(copy.age,user.age);
  9. copy.age=30;
  10. //改变copy不影响原型。
  11. Assert.assertTrue(copy.age!=user.age);
  12. //name因为是引用类型,所以copy和原型的引用是同一的。
  13. Assert.assertTrue(copy.name==user.name);
  14. //String为不可变类。没有办法可以通过对copy.name的字符串的操作改变这个字符串。
  15. //改变引用新的对象不会影响原型。
  16. copy.name="newname";
  17. Assert.assertEquals("newname",copy.name);
  18. Assert.assertEquals("user",user.name);

可见,在考虑clone时,primitive和不可变对象类型是可以同等对待的。

java为什么如此实现clone呢?
也许有以下考虑。
1 效率和简单性,简单的copy一个对象在堆上的的内存比遍历一个对象网然后内存深copy明显效率高并且简单。
2 不给别的类强加意义。如果A实现了Cloneable,同时有一个引用指向B,如果直接复制内存进行深copy的话,意味着B在意义上也是支持Clone的,但是这个是在使用B的A中做的,B甚至都不知道。破坏了B原有的接口。
3 有可能破坏语义。如果A实现了Cloneable,同时有一个引用指向B,该B实现为单例模式,如果直接复制内存进行深copy的话,破坏了B的单例模式。
4 方便且更灵活,如果A引用一个不可变对象,则内存deep copy是一种浪费。Shadow copy给了程序员更好的灵活性。

如何clone
clone三部曲。
1 声明实现Cloneable接口。
2 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。
3 把浅copy的引用指向原型对象新的克隆体。
给个例子。
Java代码 收藏代码
  1. classUserimplementsCloneable{
  2. Stringname;
  3. intage;
  4. @Override
  5. publicUserclone()throwsCloneNotSupportedException{
  6. return(User)super.clone();
  7. }
  8. }
  9. classAccountimplementsCloneable{
  10. Useruser;
  11. longbalance;
  12. @Override
  13. publicAccountclone()throwsCloneNotSupportedException{
  14. Accountaccount=null;
  15. account=(Account)super.clone();
  16. if(user!=null){
  17. account.user=user.clone();
  18. }
  19. returnaccount;
  20. }
  21. }




对clone的态度

clone嘛,我觉得是个好东西,毕竟系统默认实现已经帮我们做了很多事情了。
但是它也是有缺点的。
1 手工维护clone的调用链。这个问题不大,程序员有责任做好。
2 如果class的field是个final的可变类,就不行了。三部曲的第三步没有办法做了。

考虑一个类对clone的态度,有如下几种。
1 公开支持:好吧,按照clone三部曲实现吧。前提是父类支持(公开或者默默)。
2 默默支持:不实现Cloneable接口,但是在类里面有正确的protected的clone实现,这样,该类不支持clone,但是它的子类如果想支持的话也不妨碍。
3 不支持:好吧,为了明确该目的,提供一个抛CloneNotSupportedException 异常的protected的clone实现。
4 看情况支持:该类内部可以保存其他类的实例,如果其他类支持则该类支持,如果其他类不支持,该类没有办法,只有不支持。


其他的选择

可以用原型构造函数,或者静态copy方法来手工制作一个对象的copy。
好处是即使class的field为final,也不会影响该方法的使用。不好的地方是所有的primitive赋值都得自己维护。


和Serializable的比较

使用Serializable同样可以做到对象的clone。但是:
Cloneable本身就是为clone设计的,虽然有一些缺点,但是如果它可以clone的话无疑用它来做clone比较合适。如果不行的话用原型构造函数,或者静态copy方法也可以。

Serializable制作clone的话,添加了太多其它的东西,增加了复杂性。
1 所有的相关的类都得支持Serializable。这个相比支持Cloneable只会工作量更大
2 Serializable添加了更多的意义,除了提供一个方法用Serializable制作Clone,该类等于也添加了其它的public API,如果一个类实现了Serializable,等于它的2进制形式就已经是其API的一部分了,不便于该类以后内部的改动。
3 当类用Serializable来实现clone时,用户如果保存了一个老版本的对象2进制,该类升级,用户用新版本的类反系列化该对象,再调用该对象用Serializable实现的clone。这里为了一个clone的方法又引入了类版本兼容性的问题。不划算。


性能

不可否认,JVM越来越快了。
但是系统默认的native实现还是挺快的。
clone一个有100个元素的int数组,用系统默认的clone比静态copy方法快2倍左右。
分享到:
评论
31 楼 agapple 2010-08-19 引用
to 楼主:

性能

不可否认,JVM越来越快了。
但是系统默认的native实现还是挺快的。
clone一个有100个元素的int数组,用系统默认的clone比静态copy方法快2倍左右。

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

针对这个我做了下测试。
Java代码 收藏代码
  1. iintfortimes=1000*100;
  2. intn=10000;
  3. int[]ints=newint[n];
  4. for(inti=0;i<n;i++){
  5. ints[i]=i;
  6. }
  7. longstart=System.currentTimeMillis();
  8. for(inti=0;i<fortimes;i++){
  9. ints.clone();
  10. }
  11. System.out.println("clonecost:"+(System.currentTimeMillis()-start));
  12. start=System.currentTimeMillis();
  13. int[]intsCopy=newint[n];
  14. for(inti=0;i<fortimes;i++){
  15. System.arraycopy(ints,0,intsCopy,0,ints.length);
  16. }
  17. System.out.println("copycost:"+(System.currentTimeMillis()-start));
  18. start=System.currentTimeMillis();
  19. int[]intsfor=newint[n];
  20. for(inti=0;i<fortimes;i++){
  21. intlength=ints.length;
  22. for(intj=0;j<length;j++){
  23. intsfor[j]=ints[j];
  24. }
  25. }
  26. System.out.println("forcopycost:"+(System.currentTimeMillis()-start));


发现性能没你说的那么好,发现还不如我循环copy来得快。
Java代码 收藏代码
  1. clonecost:2698ms
  2. copycost:480ms
  3. forcopycost:496ms


针对String对象的我也做了类似测试,发现效果也没系统的system.arraycopy来的好
Java代码 收藏代码
  1. clonecost:2636
  2. copycost:523
  3. forcopycost:2882


不知道LZ的2倍性能提升是咋来的,做了特殊优化?
30 楼 zhang_xzhi_xjtu 2010-08-19 引用
谢谢大家的评论
29 楼 lixia0417 2010-08-19 引用
真是不错,楼主把这个clone讲得真详细啊;
28 楼 kevin850115 2010-02-28 引用
zhang_xzhi_xjtu 写道
目录

预备知识
为什么要clone
Object的clone以及为什么如此实现
如何clone
对clone的态度
其他的选择
和Serializable的比较
性能

预备知识

为了理解java的clone,有必要先温习以下的知识。
java的类型,java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。
java引用类型的存储,java的引用类型都是存储在堆上的。
Java代码 收藏代码
  1. publicclassB{
  2. inta;
  3. Stringb;
  4. publicB(inta,Stringb){
  5. super();
  6. this.a=a;
  7. this.b=b;
  8. }
  9. }

对这样一个引用类型的实例,我们可以推测,在堆上它的内存存储形式(除去指向class的引用,锁的管理等等内务事务所占内存),应该有一个int值表示a,以及一个引用,该引用指向b在堆上的存储空间。


为什么要clone

恩,因为需要。废话。
有名的GoF设计模式里有一个模式为原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
简单的说就是clone一个对象实例。使得clone出来的copy和原有的对象一模一样。

插一个简单使用clone的例子,如果一个对象内部有可变对象实例的话,public API不应该直接返回该对象的引用,以防调用方的code改变该对象的内部状态。这个时候可以返回该对象的clone。

问题来了,什么叫一模一样。
一般来说,有
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
但是这些都不是强制的。
我们需要什么样的clone就搞出什么样的clone好了。
一般而言,我们要的clone应该是这样的。copy和原型的内容一样,但是又是彼此隔离的。即在clone之后,改变其中一个不影响另外一个。


Object的clone以及为什么如此实现

Object的clone的行为是最简单的。以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。
看例子。
Java代码 收藏代码
  1. classUser{
  2. Stringname;
  3. intage;
  4. }
  5. classAccountimplementsCloneable{
  6. Useruser;
  7. longbalance;
  8. @Override
  9. publicObjectclone()throwsCloneNotSupportedException{
  10. returnsuper.clone();
  11. }
  12. }


Java代码 收藏代码
  1. //user.
  2. Useruser=newUser();
  3. user.name="user";
  4. user.age=20;
  5. //account.
  6. Accountaccount=newAccount();
  7. account.user=user;
  8. account.balance=10000;
  9. //copy.
  10. Accountcopy=(Account)account.clone();
  11. //balance因为是primitive,所以copy和原型是相等且独立的。
  12. Assert.assertEquals(copy.balance,account.balance);
  13. copy.balance=20000;
  14. //改变copy不影响原型。
  15. Assert.assertTrue(copy.balance!=account.balance);
  16. //user因为是引用类型,所以copy和原型的引用是同一的。
  17. Assert.assertTrue(copy.user==account.user);
  18. copy.user.name="newName";
  19. //改变的是同一个东西。
  20. Assert.assertEquals("newName",account.user.name);



恩,默认实现是帮了我们一些忙,但是不是全部。
primitive的确做到了相等且隔离。
引用类型仅仅是复制了一下引用,copy和原型引用的东西是一样的。
这个就是所谓的浅copy了。
要实现深copy,即复制原型中对象的内存copy,而不仅仅是一个引用。只有自己动手了。
等等,是不是所有的引用类型都需要深copy呢?
不是!
我们之所以要深copy,是因为默认的实现提供的浅copy不是隔离的,换言之,改变copy的东西,会影响到原型的内部。比如例子中,改变copy的user的name,影响了原型。
如果我们要copy的类是不可变的呢,如String,没有方法可以改变它的内部状态呢。
Java代码 收藏代码
  1. classUserimplementsCloneable{
  2. Stringname;
  3. intage;
  4. @Override
  5. publicObjectclone()throwsCloneNotSupportedException{
  6. returnsuper.clone();
  7. }
  8. }

Java代码 收藏代码
  1. //user.
  2. Useruser=newUser();
  3. user.name="user";
  4. user.age=20;
  5. //copy
  6. Usercopy=(User)user.clone();
  7. //age因为是primitive,所以copy和原型是相等且独立的。
  8. Assert.assertEquals(copy.age,user.age);
  9. copy.age=30;
  10. //改变copy不影响原型。
  11. Assert.assertTrue(copy.age!=user.age);
  12. //name因为是引用类型,所以copy和原型的引用是同一的。
  13. Assert.assertTrue(copy.name==user.name);
  14. //String为不可变类。没有办法可以通过对copy.name的字符串的操作改变这个字符串。
  15. //改变引用新的对象不会影响原型。
  16. copy.name="newname";
  17. Assert.assertEquals("newname",copy.name);
  18. Assert.assertEquals("user",user.name);

可见,在考虑clone时,primitive和不可变对象类型是可以同等对待的。

java为什么如此实现clone呢?
也许有以下考虑。
1 效率和简单性,简单的copy一个对象在堆上的的内存比遍历一个对象网然后内存深copy明显效率高并且简单。
2 不给别的类强加意义。如果A实现了Cloneable,同时有一个引用指向B,如果直接复制内存进行深copy的话,意味着B在意义上也是支持Clone的,但是这个是在使用B的A中做的,B甚至都不知道。破坏了B原有的接口。
3 有可能破坏语义。如果A实现了Cloneable,同时有一个引用指向B,该B实现为单例模式,如果直接复制内存进行深copy的话,破坏了B的单例模式。
4 方便且更灵活,如果A引用一个不可变对象,则内存deep copy是一种浪费。Shadow copy给了程序员更好的灵活性。

如何clone
clone三部曲。
1 声明实现Cloneable接口。
2 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。
3 把浅copy的引用指向原型对象新的克隆体。
给个例子。
Java代码 收藏代码
  1. classUserimplementsCloneable{
  2. Stringname;
  3. intage;
  4. @Override
  5. publicUserclone()throwsCloneNotSupportedException{
  6. return(User)super.clone();
  7. }
  8. }
  9. classAccountimplementsCloneable{
  10. Useruser;
  11. longbalance;
  12. @Override
  13. publicAccountclone()throwsCloneNotSupportedException{
  14. Accountaccount=null;
  15. account=(Account)super.clone();
  16. if(user!=null){
  17. account.user=user.clone();
  18. }
  19. returnaccount;
  20. }
  21. }




对clone的态度

clone嘛,我觉得是个好东西,毕竟系统默认实现已经帮我们做了很多事情了。
但是它也是有缺点的。
1 手工维护clone的调用链。这个问题不大,程序员有责任做好。
2 如果class的field是个final的可变类,就不行了。三部曲的第三步没有办法做了。

考虑一个类对clone的态度,有如下几种。
1 公开支持:好吧,按照clone三部曲实现吧。前提是父类支持(公开或者默默)。
2 默默支持:不实现Cloneable接口,但是在类里面有正确的protected的clone实现,这样,该类不支持clone,但是它的子类如果想支持的话也不妨碍。
3 不支持:好吧,为了明确该目的,提供一个抛CloneNotSupportedException 异常的protected的clone实现。
4 看情况支持:该类内部可以保存其他类的实例,如果其他类支持则该类支持,如果其他类不支持,该类没有办法,只有不支持。


其他的选择

可以用原型构造函数,或者静态copy方法来手工制作一个对象的copy。
好处是即使class的field为final,也不会影响该方法的使用。不好的地方是所有的primitive赋值都得自己维护。


和Serializable的比较

使用Serializable同样可以做到对象的clone。但是:
Cloneable本身就是为clone设计的,虽然有一些缺点,但是如果它可以clone的话无疑用它来做clone比较合适。如果不行的话用原型构造函数,或者静态copy方法也可以。

Serializable制作clone的话,添加了太多其它的东西,增加了复杂性。
1 所有的相关的类都得支持Serializable。这个相比支持Cloneable只会工作量更大
2 Serializable添加了更多的意义,除了提供一个方法用Serializable制作Clone,该类等于也添加了其它的public API,如果一个类实现了Serializable,等于它的2进制形式就已经是其API的一部分了,不便于该类以后内部的改动。
3 当类用Serializable来实现clone时,用户如果保存了一个老版本的对象2进制,该类升级,用户用新版本的类反系列化该对象,再调用该对象用Serializable实现的clone。这里为了一个clone的方法又引入了类版本兼容性的问题。不划算。


性能

不可否认,JVM越来越快了。
但是系统默认的native实现还是挺快的。
clone一个有100个元素的int数组,用系统默认的clone比静态copy方法快2倍左右。

分享到:
评论

相关推荐

    深入理解Java中的克隆

    想必大家对克隆都有耳闻,世界上第一只克隆羊多莉就是利用细胞核移植技术将哺乳动物的成年体细胞培育出新个体,甚为神奇。...本文将尝试介绍一些关于Java中的克隆和一些深入的问题,希望可以帮助大家更好地了解克隆。

    深入理解java中的拷贝机制

    主要给大家深入介绍了java中的拷贝机制,网上关于java中拷贝的文章也很多,但觉得有必要再深的介绍下java的拷贝机制,有需要的朋友可以参考学习,下面来一起看看吧。

    java深入理解浅拷贝和深拷贝

    拷贝对象是java中经常会遇到的问题。java中存在两种类型,基础类型和引用类型。...Object对象中提供了一个clone方法,来供我们对java对象进行拷贝。 protected native Object clone() throws CloneNot

    Java的Object类讲解案例代码 equals()、hashCode()、finalize()、clone()、wait()

    经验丰富的Java开发者:即使你已经有一定的Java开发经验,仍然值得深入了解Object类的使用。该案例代码将提供一些实际应用场景,帮助你更加灵活地使用Object类的相关方法,从而优化你的项目。 通过这个源码资源,你...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    7.11 小结:多方位理解Java方法 191 7.12 习题 192 第8章 Java中的包(Package)命名习惯和注释 193 教学视频:43分钟 8.1 Java中的包(Package) 193 8.1.1 Java中的包 193 8.1.2 在Eclipse中使用包 194 ...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    7.11 小结:多方位理解Java方法 191 7.12 习题 192 第8章 Java中的包(Package)命名习惯和注释 193 教学视频:43分钟 8.1 Java中的包(Package) 193 8.1.1 Java中的包 193 8.1.2 在Eclipse中使用包 194 ...

    java 编程入门思考

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    Java初学者入门教学

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    java联想(中文)

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    JAVA_Thinking in Java

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    Java Object 类高难度进阶版面试题集锦解析Java Object类高难度面试题及答案解析

    提供了20道高难度的Java Object类面试题及详细答案解析,涵盖了equals()、hashCode()、toString()、clone()、finalize()等方法的重写和应用,以及对象...适合准备Java面试的开发者深入理解和掌握Object类的关键知识点。

    Thinking in Java简体中文(全)

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    Thinking in Java 中文第四版+习题答案

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    对象克隆(clone)详解.docx

    查看了很多文章对于clone()方法讲解都不慎透彻,特别是对于深层克隆的讲解语言晦涩难懂,很难理解,包括Oracle公司出版的JDK帮助文档也讲的不清楚,本人通过具体实践通过浅显易懂的语言详细讲解了clone()方法。...

    基于JAVA的搜索引擎 lucene-2.2.0

    必须深入地理解IndexWriter索引器初始化的过程,以及在构造一个IndexWriter索引器过程中涉及到的一些其他的类,应该深入研究这些相关的类的定义。这样才能有助于深化IndexWriter索引器构造的原理机制。 可见,...

    JAVA_Thinking in Java(中文版 由yyc,spirit整理).chm

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

    Think in Java(中文版)chm格式

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中...

    Thinking in Java(中文版 由yyc,spirit整理).chm

    7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6...

Global site tag (gtag.js) - Google Analytics