解析Java中神秘的String

很多Java的初学者对于String的使用细节不是太关心,但是我在这里想通过对Inside Java 2 Virtual Machine的学习和部分手动试验向大家展示String在所有Java Object中最特别的地方。

关键词: ==, equals, hashCode, intern, interned string reference list

首先最重要的是区分String中“==”, “equals()”以及“hashCode()”。

在Java中,很多时候两个相同Class的实例对象就算内容一样,通过equals对比后还是不一样,比如,

Car car1 = new Car();
Car car2 = new Car();
car1.equals(car2); // false

其根本原因还是两个对象在内存中是不同的对象,放在Java虚拟机的堆中不同的位置上。这样,你在对比两个对象的时候,当然就是不同的东西了。而equals有个必要条件,就是只要两个对象equals不一样的话,hashCode就必须不一样;反之,hashCode必须是一样的。因此我们可以知道当两个内容相同的实例对象equals不同的时候,他们的hashCode也必然是不同的。

String很特别,他的equals以及hashCode实现的方法是充分的利用了已有的对象,能够非常有效的节约空间。其实现方法就是两个不同的String对象,假如内容一样,那么equals是一样的,即他们的hashCode也是相同的。从已有的代码来看,其hashCode计算方法即是对每一个放在String内部数据结构所带的char数组中的字符进行计算和相加。比如,

String s1 = "hello";
其可能的hashCode计算方法是将h, e, l, l, o的每个ASCII码取出来然后乘以一个数字,最后相加。这样做的结果就是,只要你两个String字符串是一样的,最后hashCode就是一样的。而String的equals实现方法还是比较两个String对象中的字符串是否一样,一样的则返回为真。这也同样说明了两个String对象的字符串相同则就是同一个对象。

而“==”比较的是调用它的String引用所指向的String对象是否相同,这是什么意思呢?就是说,我用了==符号就是去比较位于==两边String引用所指向的,放在Java虚拟机的堆中的String对象是否是一样的。比如,

String s1 = new String("123");
String s2 = new String("234");

大家一看,必然知道这两者不仅equals下来是不一样的,==后肯定也是不一样的,毕竟这两个字符串内容都不一样,所以对应的String对象也肯定是不一样的。那么我们看这样呢?

String s1 = new String("123");
String s2 = new String("123");

首先可以确定的是s1.equals(s2);结果肯定是true,两者分别查看hashCode()后结果也是一样的,都是48690。但是如果你用s1 == s2进行比较的话,结果肯定是false!

为什么呢?

原因很简单,因为你使用了动态分配对象new,所以Java虚拟机在自己的堆上分别为两个String对象都分配了空间,因此你用==进行比较的时候,它比较的是两个String对象堆中实际的位置,结果当然就是不一样的了。而你用equals因为比较的是String实际的字符串是否相等,所以结果是相同的。

同样的,我们有,

String s1 = "123";
String s2 = new String("123");

这个==下来结果也是不同的,equals和hashCode是相同的。

神秘的“=”符号和new的区别。

可能大部分同学在使用String生成一个新的String对象的时候,都没有去想过使用动态分配对象new和直接在“=”符号后赋上一个字符串生成String对象的区别。实际上两者区别非常大。

使用=号后直接赋上字符串是告诉Java虚拟机,你在Java编译器将Java源代码编译成为bytecode的时候就让bytecode所在的class file知道String内容是什么,并且“潜规则”预订好了将String生成为一个对象的时间后续动作

上面两个黑体字符串表示什么特别的意思呢?
就是说,在这个String s1 = "123";所在的方法被调用的时候,Java虚拟机会对编译时候已经知道的“123”进行一些动作以便生成String对象。第一个动作就是resolution,第二个动作是检查interned string reference list中是否有改对象的引用了(即reference),第三个可能的动作是在interned string reference list中没有该String对象的引用的时候对该对象intern()一下,将其加入到list中。

问题是上面几句话是什么意思?

因为我们知道在编译完的时候Java虚拟机就已经知道了我们以后会用到123这个String对象,所以将其暂时放到class file中的constant pool表中。其实说白了,就是说因为我知道以后要用你,现在就暂时以文字的方式将你存储起来,以后以便将你实例化。这就像XML一样,你先可以将一些对象的内容存储在XML文件中,到时候遇到它要用它的时候再将其deserialize读出来,并放到构造器中实例化。Java虚拟机也是这样做的。好这样我们知道通过第一步resolution后,我们会将其实例化。

但实际上,Java虚拟机中为了节省空间,暂时并没有将其实例化,而是检查放在其method area中所维护的一个叫做interned string reference list中是否有该对象存在了。该list中保持的是指向Java虚拟机堆中的String对象的引用。所以你在进行检查的时候,实际上是调用了这个即将创建的String对象的equals和该list中所有引用比较。这样做的好处就是,因为equals会在发现两者字符串相同的时候返回true,所以你能保持同样的String在Java虚拟机堆中永远只有这一个对象存在

继续刚才的1,2,3。假如Java虚拟机发现了相同的String内容已经存在了,就返回该对象的引用给s1。所以这样s1指向了这个已存在的对象。而假如Java虚拟机发现没有同样的内容存在,那么就调用new String();创建该对象,并放在堆中。

第三步是在list中没有该对象的时候,将刚创建的String对象intern(),即将其加入到这个维护的list中。

new会做什么呢?new使得Java虚拟机在调用一个class file的时候无从得知该对象的存在,即在method具体走到该步的时候,才会在堆中分配该对象。仅此而已。这样做的一个缺点是,假如该堆中已经有了同样内容的String对象,即“123”已经存在,虚拟机还是会为你再次分配个一样的对象。这叫做浪费革命粮食。因此我们可以比较一下,

String s1 = "123";
String s2 = new String("123");
String s3 = new String("123");

s1在method调用到这句话的时候在堆中创建了一个String对象,内容是123,并将这个对象的引用添加到了interned string reference list中。
s2在堆中创建了一个同样为123的对象。s3也是在堆中创建了一个s3的对象。

s1 == s2; // false
s2 == s3; // false
s1.equals(s2); //true
s2.equals(s3); //true

intern()以及优化String。

我们在有了下面的语句后,

String s1 = "123";
String s2 = new String("123");
String s3 = new String("123");

分别用,

s2 = s2.intern();
s3 = s3.intern();

这样就会让Java虚拟机去寻interned string reference list中寻找是否有相同内容的String对象存在。结果是它会发现s1所指向的对象的内容和s2以及s3所指向的对象的内容是一样的。于是乎,s2以及s3会被赋予s1。这样s2和s3原来所指向的堆中的对象就是没有reference的对象了。迟早,他们会被垃圾回收器所回收掉空间。

上面两步完成后你再测试,

s1 == s2; // true
s2 == s3; // true

希望以上的文字对于大家进行Java或者Java虚拟机的学习有帮助。

Java Virtual Machine进展很快!

本来计划要到下个月初才能看完的Inside Java 2 Virtual Machine,估计这个星期内(最快周四)就能看完!然后还剩大概9-10天复习下重点章节,理清下整体的思路。

除此之外,就是应该加强看Dalvik代码的力度了,千万不要偷懒,这个月底确实要达到理清个大概的目标才行。....唯一遗憾的是,代码确实有点儿麻烦,大概要到2月或者3月底才能看完。努力吧。

下个月开始算法那本书的学习!争取两个月内把重要的知识都学一遍!同时应该辅之以C++数据结构那本书。

一定要亲手改变自己!

我的政坛分析

刚才偶然看见多维新闻一篇文章“中共最高层的理想主义者”,其中说到,

胡温的个性虽则温和良善,却对时事领悟机敏,其从父辈处传承的中国传统文化教养,其个性中暗藏的“酷”和永不屈服的意志, 是胡温能够在中共官场洁身自好、最终成为政坛领袖的关键之道。胡温对中国社会的理想主义寄托,主要来自于中国传统文化的大同社会理想和悲悯之心,并与其曾 长期在落后地区工作、深谙民间疾苦直接有关联。胡总的“和谐社会”,温总的“亲民”,其实都是与中国传统知识分子对社会的关怀和政治理想一脉相承的,也就是“修身治国平天下”,为民生创造“太平盛世”。

这点确实能感觉出来,像胡总对台湾放下身段,地震和元旦期间赴四川看望灾民等等行为;温总要求广东企业不要大规模裁员,要求政府帮助下岗群众都是他们对社会关怀和悲悯之心的体现。

这篇文章中后面紧接着说到,

而习近平、李可强、李源潮他们的理想主义色彩,却植根于77级/78级大学生集体。习和二李均是77级/78级这个特殊的 大学生团体的成员。在那个特殊的年代,各大名校的校园里激情荡扬、思想开放犹如魏晋、人文理想犹如春风,那时由于许多新文化运动的老教授还健在,又有外教 传授现代西方学说,所以77级/78级学生集体比较完整地接受了中国古典文化、五四新文化和西方文明方面的教育,学贯中西,西学中用,可谓是现代中国历史 上罕见的一代中国精英集体。这个集体的“集体特征”就是强烈的理想主义、人文中国的情怀及对祖国的深深眷恋。比如一些77级/78级大学生来美国20多年 了就是不肯加入美国籍,而宁可以游子的身份寄托对祖国强盛的梦想。可以这样说,对于留在中国或漂流海外的77级/78级大学生来说,他们中的许多人一辈子 注定是理想主义者,理想主义是他们此生难以抹去的烙印。

习和二李我就确实不了解了,不是不愿意了解,而是他们确实太低调了,真是越是要成为首长的人才越低调。而那些暂时没机会,却想着当首长的人才会努力创造机会去出风头,比如薄熙来和汪洋。

这两个人都是从重庆或者以前在重庆待过的。行为看似没什么相似之处,其实是很像的。比如汪洋在这次经济危机中,明显的和温总对着干,说要“腾笼换鸟”,就是要让mass-production的企业尽早的退出以让位给新型企业。先不说这种说法合不合理了,这种公然和“中央”唱反调的行为就很反常。当然,这也不是什么新鲜事,中国历史上诸侯强中央弱的情况就屡次发生。现在在广东,山东和江苏等省份经济成长后可能又会出现。薄熙来这人我不喜欢,个人没什么大的能力,比如在商务部时就没看出来有什么出人之处。而他在大连时能够把大连带出来是有一定的时代背景的,这并不能给他加多少分。而他在重庆的表现可真是让人大跌眼镜。什么修酷似邓小平的主席雕像,什么重庆大学生学唱20首红色歌曲等等....... 不知道他是怎么想的,又是一个和中央唱反调。胡总才在17届三中全会上强调“不会再走回头路,会坚持把改革开放道路走下去”就是明显信号,薄熙飞来偏要迎合偏左的那部份人的心理,真是其心可畏啊。

但是了,不知道这批领导人今后会怎么样,还是很难说,反正薄熙来我不看好,汪洋说不定还会上,如果他能成功解决广东这次面临的经济危机。