#String s="a"+"b"+"c",到底创建了几个对象?#

首先看一下这道常见的面试题,下面代码中,会创建几个字符串对象?

String s="a"+"b"+"c";
如果你比较一下Java源代码和反编译后的字节码文件,就可以直观的看到答案,只创建了一个String对象。

估计大家会有疑问了,为什么源代码中字符串拼接的操作,在编译完成后会消失,直接呈现为一个拼接后的完整字符串呢?

这是因为在编译期间,应用了编译器优化中一种被称为常量折叠(Constant Folding)的技术,会将编译期常量的加减乘除的运算过程在编译过程中折叠。编译器通过语法分析,会将常量表达式计算求值,并用求出的值来替换表达式,而不必等到运行期间再进行运算处理,从而在运行期间节省处理器资源。

而上边提到的编译期常量的特点就是它的值在编译期就可以确定,并且需要完整满足下面的要求,才可能是一个编译期常量:

被声明为final
基本类型或者字符串类型
声明时就已经初始化
使用常量表达式进行初始化
上面的前两条比较容易理解,需要注意的是第三和第四条,通过下面的例子进行说明:

final String s1="hello "+"Hydra";
final String s2=UUID.randomUUID().toString()+"Hydra";
编译器能够在编译期就得到s1的值是hello Hydra,不需要等到程序的运行期间,因此s1属于编译期常量。而对s2来说,虽然也被声明为final类型,并且在声明时就已经初始化,但使用的不是常量表达式,因此不属于编译期常量,这一类型的常量被称为运行时常量。再看一下编译后的字节码文件中的常量池区域:

可以看到常量池中只有一个String类型的常量hello Hydra,而s2对应的字符串常量则不在此区域。对编译器来说,运行时常量在编译期间无法进行折叠,编译器只会对尝试修改它的操作进行报错处理。

另外值得一提的是,编译期常量与运行时常量的另一个不同就是是否需要对类进行初始化,下面通过两个例子进行对比:

public class IntTest1 {

public static void main(String[] args) {

System.out.println(a1.a);

}
}
class a1{

static {

System.out.println("init class");

}

public static int a=1;
}
运行上面的代码,输出:

init class
1
如果对上面进行修改,对变量a添加final进行修饰:

public static final int a=1;
再次执行上面的代码,会输出:

1
可以看到在添加了final修饰后,两次运行的结果是不同的,这是因为在添加final后,变量a成为了编译期常量,不会导致类的初始化。另外,在声明编译器常量时,final关键字是必要的,而static关键字是非必要的,上面加static修饰只是为了验证类是否被初始化过。

我们再看几个例子来加深对final关键字的理解,运行下面的代码:

public static void main(String[] args) {

final String h1 = "hello";

String h2 = "hello";

String s1 = h1 + "Hydra";

String s2 = h2 + "Hydra";

System.out.println((s1 == "helloHydra"));

System.out.println((s2 == "helloHydra"));
}
执行结果:

true
false
代码中字符串h1和h2都使用常量赋值,区别在于是否使用了final进行修饰,对比编译后的代码,s1进行了折叠而s2没有,可以印证上面的理论,final修饰的字符串变量属于编译期常量。

再看一段代码,执行下面的程序,结果会返回什么呢?

public static void main(String[] args) {

String h ="hello";

final String h2 = h;

String s = h2 + "Hydra";

System.out.println(s=="helloHydra");
}
答案是false,因为虽然这里字符串h2被final修饰,但是初始化时没有使用编译期常量,因此它也不是编译期常量。

在上面的一些例子中,在执行常量折叠的过程中都遵循了使用常量表达式进行初始化这一原则,这里可能有的同学还会有疑问,到底什么样才能算得上是常量表达式呢?在Oracle官网的文档中,列举了很多种情况,下面对常见的情况进行列举(除了下面这些之外官方文档上还列举了不少情况,如果有兴趣的话,可以自己查看):

基本类型和String类型的字面量
基本类型和String类型的强制类型转换
使用+或-或!等一元运算符(不包括++和--)进行计算
使用加减运算符+、-,乘除运算符*、 / 、% 进行计算
使用移位运算符 >>、 <<、 >>>进行位移操作
……
字面量(literals)是用于表达源代码中一个固定值的表示法,在Java中创建一个对象时需要使用new关键字,但是给一个基本类型变量赋值时不需要使用new关键字,这种方式就可以被称为字面量。Java中字面量主要包括了以下类型的字面量:

//整数型字面量:
long l=1L;
int i=1;

//浮点类型字面量:
float f=11.1f;
double d=11.1;

//字符和字符串类型字面量:
char c='h';
String s="Hydra";

//布尔类型字面量:
boolean b=true;
当我们在代码中定义并初始化一个字符串对象后,程序会在常量池(constant pool)中缓存该字符串的字面量,如果后面的代码再次用到这个字符串的字面量,会直接使用常量池中的字符串字面量。

除此之外,还有一类比较特殊的null类型字面量,这个类型的字面量只有一个就是null,这个字面量可以赋值给任意引用类型的变量,表示这个引用类型变量中保存的地址为空,也就是还没有指向任何有效的对象。

那么,如果不是使用的常量表达式进行初始化,在变量的初始化过程中引入了其他变量(且没有被final修饰)的话,编译器会怎样进行处理呢?我们下面再看一个例子:

public static void main(String[] args) {

String s1="a";

String s2=s1+"b";

String s3="a"+"b";

System.out.println(s2=="ab");

System.out.println(s3=="ab");
}
结果打印:

false
true
为什么会出现不同的结果?在Java中,String类型在使用==进行比较时,是判断的引用是否指向堆内存中的同一块地址,出现上面的结果那么说明指向的不是内存中的同一块地址。

通过之前的分析,我们知道s3会进行常量折叠,引用的是常量池中的ab,所以相等。而字符串s2在进行拼接时,表达式中引用了其他对象,不属于编译期常量,因此不能进行折叠。

那么,在没有常量折叠的情况下,为什么最后返回的是false呢?我们看一下这种情况下,编译器是如何实现,先执行下面的代码:

public static void main(String[] args) {

String s1="my ";

String s2="name ";

String s3="is ";

String s4="Hydra";

String s=s1+s2+s3+s4;
}
然后使用javap对字节码文件进行反编译,可以看到在这一过程中,编译器同样会进行优化:

可以看到,虽然我们在代码中没有显示的调用StringBuilder,但是在字符串拼接的场景下,Java编译器会自动进行优化,新建一个StringBuilder对象,然后调用append方法进行字符串的拼接。而在最后,调用了StringBuilder的toString方法,生成了一个新的字符串对象,而不是引用的常量池中的常量。这样,也就能解释为什么在上面的例子中,s2=="ab"会返回false了。

本文代码基于Java 1.8.0_261-b12 版本测试

#四九三[超话]#周二的主题是什么呢?是遗忘,是错过,是嗓子突然变得越来越哑。

​中午的时候光光突然组局,她说买了两个小东西,想要送给我们,可以吃完饭去她家拿。竹哥也可以顺便撸猫。而当时我因为早起,在迎合了两句之后便陷入睡眠。

再次醒来是光光的消息把我震醒,她问所以你晚上有课吗?我一下子弹了起来,并且决绝地把晚上本来可能有的课鸽掉了,吱吱说赚钱重要,我说不,见你们更重要。如果没有了快乐,赚再多的钱又能怎样呢?当然我也没有赚很多钱,不然我的苦恼会减少一半,或者70%,或者80%。

我一直试图把吱吱先约出来,但她一直在闪烁其词。我难过的说吱吱没有回应我,直到吱吱说不,我早已拒绝你。我没有读懂她的拒绝,是我的错。

我们再次来到了川当家,在路上的时候点了光光最爱的一直想喝的送鸭子的家的茶。我和光的选择正好能促成一个套餐,小吱发现的,这个家没她也真的不行。但光能喝,要喝1L的,而吱吱更能喝,她要喝两个600ml的。其实我也算是能喝水的,甚至还因为爱喝水而被别人表扬过。但在她们两个的面前,我还是觉得自己有很长的路要走,有很多的水要喝。

点菜的部分该怎么形容呢?又是一场漫长的联盟,坚持,辜负,背叛,妥协,取舍。

好在结果都不错,我们也吃的很开心,吱喝的也很开心,也可能没那么开心,因为我的水没喝到一半的时候,她第一桶已经喝完了。

我们本来说要玩sports,甚至我还带了自己的switch,坐下之后吱吱说忘带卡了,我说没关系。怪我自己,怪我自己,除了塞尔达还没有任何游戏的卡。是我没能给这个家更好的生活和娱乐。

我真的没给这个家更好的娱乐吗?因为后遗症,我如果咳的厉害的话,嗓子会变哑,越来越哑,由浑浊到彻底失声。一开始跟她们预警的时候,还没有人当回事儿,直到最后我完全失声。嘶哑着嗓子喊猫,和她们说话,给她们唱歌,他们的表情有心疼,痛苦的时候你别说了,但这种心疼没过两秒就会变成狂笑,就会变成“你唱首《特别的人》吧”

而光光的小礼物在光进家之后就开始翻箱倒柜的找,最后她终于找到了,是因为我看了很久一直很喜欢,但一直没买的小徽章,我开心的把徽章别了过去,并且调到了最开心的程度。随后我们开始在毫无交流中刷抖音,抓娃娃,玩儿塞尔达,房间里只能听到各自的笑声,我嘶哑的声音。或许婚姻生活就是这样的吧。

再次起身的时候,我发现my social battery已经到了最低值。而吱吱头上的social battery也到了最低,我说这也许是冥冥之中的暗示吧,毕竟哪个真正开心的人会需要这样的徽章呢?出门的路上吱吱说她一直在尽力把徽章往左边调。

“仿佛这样真的就能左右自己的心情。”



I' m going to the sun on my holiday. I well go there by a spaceship. I will take a big blue spaceship.
Then I' ll pilot the spaceship to the sun. The sun is very hot. So I put on the super-shirt. I n the morning, I well have some sun burger for my breakfast.
At eight o' clock, I well play with my friends there. They are super dog and super girl. Super dog is white and black. Super girl is very clever.Super girl and super dog like to play with me. So I play with them for forty minutes. Then I do my homework in my little red room on a small blue table. After my hone work, I will have my lunch. I will eat sun salad. I well make some red toy bear to the sun babies. I will have red juice, red fish and red rice. Al the things are red. Then I need a tot of water on the sun because the sun is too hot. Sol will walk to the spaceship. I' 1l pilot the spaceship to the earth.


发布     👍 0 举报 写留言 🖊   
✋热门推荐
  • 新款索尼Xperia 1 II的型号是XQ-AT42,是一款支持eSIM卡的手机,其与国际版相比,新款索尼Xperia 1 II的运存升级为12GB,配色方面还
  • 2)其实欢拼好货的佣金都是从商家成交的订单里抽出来的,也是商家为了提供商品销量愿意让出来的一部分利润,也就是说这个钱绝对是正大光明的。数据显示3-10L的中小容
  • 顶礼上师三宝!我们所得到的苦乐果来自我们的身口意的起惑造业,所以平时我们的修行应从三门下手调整,而感受苦乐是我们的心,故修心显得更为重要。而一个人爱自己的最大奥
  • 真nm搞笑这司机 你家缺那两个b钱吗[微笑][微笑][微笑]我跟支佳丽下车六分钟发现我手机掉了以后打电话是通的 后面就关机了 我当时就用小支手机登我id把手机锁
  • 生活有苦有甜,才叫完整。并不是每件事情都要有意义,轻松一点过日子,生活就是自己哄自己,把自己劝明白了也就什么都解决了。
  • #王源[超话]##王源从2000年到20岁# 宝贝 真开心陪你一起度过了二十岁的第一天啊 看到了不一样的小源 不知道怎么去表达这一天的感受了 又开心又难过 又欣
  • 大通智胜在十小劫的禅坐期间,忉利天和其他诸天的天神天主们,被他修道的精神所感动,就为他在菩提树下敷设了狮子座,高“一由旬”。其佛本坐道场,破魔军已,垂得阿耨多罗
  • #请君# 我的评分:[星星][星星][星星][星星][星星]天知道我反复横拉了多少次进度条才能完美截图(我觉得完美就行)轻轻唤醒元能,一对一私手把手私教施法,这
  • @任嘉伦Allen 亮点推荐:任嘉伦|请君@任嘉伦Allen陈哲远说给个眼神能体会吗 救命啊,谁还没有被萧无敌的小伎俩蛊到了啊,真的要被陈哲远的眼神戏迷死了,怎
  • 懂得宽恕,懂得原谅,开心的是自己,懂得淡泊名利,懂得享受生活,放松的是自己,懂得珍惜,懂得关心,自己不会有遗憾,人生需要懂得很多,开心了就会放松,放松了活得就不
  • 还有三十一号楼那个女人(下次再用这个理由来闹就把她的照片发上来)每次都说自己收不到信息,不是管家没告诉她就是网格员没通知她,不知道她怎么就那么出奇,每次必定专把
  • 一是深圳拥有丰富的上下游资源优势,上游设计能力突出,下游应用场景广泛;二是深圳创新要素市场化配置程度高、选人用人机制灵活,便于汇聚高端人才,有利于加速技术创新及
  • #西谷夕1010生日快乐#疫情封校没法拍到谷 快递也封了什么都进不来TT 只能凑合给你过一个简单的生日…因为你爱上排球中自由人的位置,大家都在飞行时永远有一个人
  • Make Up 1/43 VM223B TOYOTA TS010 Le Mans 1992 "ZENT" No.8 VM275B Honda NSX GT2
  • 其疼痛特点为胃脘绞痛,痛势较剧,苔白脉紧呈寒实之象;后者多由素体气虚或久病脾胃虚弱,中阳不振,寒从内生,胃失温养,其疼痛特点为胃痛隐隐,得按则减,舌脉呈虚寒之象
  • 【tao宝自助找券→】 【券后仅¥49|领40元券↓↓↓】[二哈]优选精梳棉面料,手感细腻,柔软舒适,翻领设计,以舒适为主,卡通图案,给孩子温暖的爱。~[猪头]
  • 。其实我们来这里只是为了新开的任天堂世界区域,明明是一大早就入园了可还是只能预约到晚上六点半的整理券真是要哭死[泪]。
  • 高校生の皆さまに、ほんの少しでも、何か感じていただけるものがお届け出来ていたら…嬉しいです☺️マスク越しでも、表情や感情は伝わってきて、心が豊かになれる時間でし
  • #英国生活[超话]##留学英国[超话]##英国[超话]##英国工作[超话]#为了课题连着听了好几天各种✝️sermon,其中有一个居然是Springfield
  • 印旺为忌,取木火克制旺金为用神,你的命局丙火透官克制庚金,丙火坐戌土火库又有甲木生之,格局小成,但是木火的力量还是不大,需要大运流年辅助,少年乙巳大运,乙巳木火