前言 有人说看了你的基本类型篇,那我期望有Java语言表达一句话还不成了是吧,我是不是得一字一顿呢?当然不是啦,Java中存在着String,只要使用双引号概括起来的那么就是一个String(字符串)。设计 字符串操作那可是计算机程序设计中最常见的行为了,使用频率高的因素决定了其定位与设计,设计者将其定位为不可变,不可变的表现如下:在设计上String类通过final修饰了类,同时将存放内容的char数组也使用final修饰。final关键字修饰的属性为不可修改的,修饰的类是不可继承的,如此就限制了使用者对其修改。publicfinalclassStringimplementsjava。io。Serializable,ComparableString,CharSequence,Constable,ConstantDesc{Stableprivatefinalbyte〔〕。。。}我们可以通过String类的源码先从表象看一看,每一个看起来会修改其值的方法最终都是创建了一个新的String对象。如下图字符串的截取方法,指定起止下标,但返回时如果截取长度与字符串长度一致返回当前对象,否则就创建一个新的对象返回。publicStringsubstring(intbeginIndex,intendIndex){intlengthlength();checkBoundsBeginEnd(beginIndex,endIndex,length);intsubLenendIndexbeginIif(beginIndex0endIndexlength){}returnisLatin1()?StringLatin1。newString(value,beginIndex,subLen):StringUTF16。newString(value,beginIndex,subLen);}位置使用常量方式创建的字符串存放在常量池里使用new方式创建的字符串存放在堆里 说到这里就顺便了一下String提供的方法intern(),这个方法就有点意思了,根据下图源码表示,这个方法是返回一个标准化的表示方式,通过字符串的常量池进行返回,也就是说针对字符串调用此方法就会统一表示,主要是针对通过new创建在堆上的字符串,调用此方法,它会先去常量池里发现,如果存在返回它,如果不存在就会放入常量池并返回,简单理解就是将存在于堆的字符串放入常量池。Returnsacanonicalrepresentationforthestringobject。pApoolofstrings,initiallyempty,ismaintainedprivatelybytheclass{codeString}。pWhentheinternmethodisinvoked,ifthepoolalreadycontainsastringequaltothis{codeString}objectasdeterminedbythe{linkequals(Object)}method,thenthestringfromthepoolisreturned。Otherwise,this{codeString}objectisaddedtothepoolandareferencetothis{codeString}objectisreturned。pItfollowsthatforanytwostrings{codes}and{codet},{codes。intern()t。intern()}is{codetrue}ifandonlyif{codes。equals(t)}is{codetrue}。pAllliteralstringsandstringvaluedconstantexpressionsareinterned。Stringliteralsaredefinedinsection3。10。5oftheciteTheJavaLanguageSpecificationcite。returnastringthathasthesamecontentsasthisstring,butisguaranteedtobefromapoolofuniquestrings。jls3。10。5StringLiteralspublicnativeStringintern();重载 重载就是操作符在应用于对应类时具有特殊意义,这里对于String而言即为连接的作用,以下通过javap命令对于源码进行反编译,进行更直观的确认:java反编译流程(参数很多,请根据需要进行添加)1、javacXXX。java2、javapvXXXpublicclassTest{publicstaticvoidmain(String〔〕args){SSSystem。out。println(s);}}0:ldc2Stringa2:astore13:new3classjavalangStringBuilder6:dup7:invokespecial4MethodjavalangStringBuilder。init:()V10:ldc5Stringbcd12:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB15:aload116:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB19:invokevirtual7MethodjavalangStringBuilder。toString:()LjavalangS22:astore223:getstatic8FieldjavalangSystem。out:LjavaioPrintS26:aload227:invokevirtual9MethodjavaioPrintStream。println:(LjavalangS)V30:return 通过将源代码javap反译后可以看到指令码中,Java在处理拼接的字符串时借助了StringBuilder类,append方法即追加内容,调用toString方法new出来最终结果。 有人会问拼接时有四个元素进行拼接,为什么只调用了2次append方法呢,因为前3个元素均在常量池优化合并。 为了直观地看出,下面的例子,请参照如下,共计调用5次append方法:publicclassTest{publicstaticvoidmain(String〔〕args){SSSystem。out。println(s);}}0:ldc2Stringa2:astore13:new3classjavalangStringBuilder6:dup7:invokespecial4MethodjavalangStringBuilder。init:()V10:ldc5Stringb12:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB15:aload116:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB19:ldc7Stringc21:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB24:aload125:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB28:ldc8Stringd30:invokevirtual6MethodjavalangStringBuilder。append:(LjavalangS)LjavalangStringB33:invokevirtual9MethodjavalangStringBuilder。toString:()LjavalangS36:astore237:getstatic10FieldjavalangSystem。out:LjavaioPrintS40:aload241:invokevirtual11MethodjavaioPrintStream。println:(LjavalangS)V44:return弯弯绕绕面试 一、请写出以下程序的输出结果publicclassTest{publicstaticvoidmain(String〔〕args){SSSSSSSystem。out。println(absum);trueSystem。out。println(absuma);falseSystem。out。println(absumab);falseSystem。out。println(sumsuma);falseSystem。out。println(sumsumab);falseSystem。out。println(sumasumab);false}}根据前面我们通过反编译得出来的结论:1、常量拼接仍在常量池;2、有变量参与拼接,JDK就会估化操作:SSStringBuildertempnewStringBuilder();temp。append(a);temp。append(b);temp。toString();StringBuilder的toString()方法为newString() 二、问以下语句共创建了几个对象StringsnewString(abc);以上语句等价于:Stringtemp123;01常量池存在,则将堆内对象引用赋值,否则在堆内创建对象,并加入常量池StringsnewString(temp);1所以上述语句共创建12个对象 三、请问以下代码是否存在问题StringlockThread。currentThread()。getName();synchronized(lock){DOSOMETHING}Lock取值为当前线程的名称,但线程名称非常量,存在于堆内,因此在这种情况下会导致锁失效。需使用intern()方法将lock加入常量池。