专栏首页SpringCloud专栏5Java学习笔记之数据结构——字符串String

5Java学习笔记之数据结构——字符串String

字符串这个非常非常常用的数据结构,平时用的最多,但它到底是怎么工作的,可能没多少人去关心过。下面就来谈谈这个String到底有什么特殊的。

String s1 = "abc";

String s2 = "abc";

String s3 = "ab" + "c";

String s4 = new String("abc");

判断上面s1,s2,s3,s4是否相等,用==

答案是:s1==s2==s3!=s4。

在java里判断==的条件是引用地址相同,可以理解为指向这块内存的指针是相等的,当s1,s2,s3都相等时说明等号后面的"abc"只有一份,它们只是指向同一个内存块而已。

再看一个普通的class对象。有个Person类,有个int age属性。

Person p = new Person();

p.setAge(10);

给一个对象p的age赋值为10.此时如果把p作为参数传给别的方法,譬如有个

void change(Person p) {p.setAge(5);}

当把p传给change方法后,再查看原来的p的age,发现已经被修改成5了。说明通过new Person()得到的p在内存中只有1份,当在任何地方被修改后,p的属性就被修改了。

刚才说了s1,s2,s3它们都指向了同一块内存地址,"abc"只有唯一的一份,那是否可以猜测s2+"d"后,s1和s3也会随之改变呢。

答案是no。只有s2自己变成了"abcd",s1和s3不会有任何变化,因为系统又开辟了一块新的内容来装"abcd"。

那假如是下面的情况呢:

String s4 = new String("abc");

String s5 = s4;

s5 += "d";

求s4?

答案是s4依旧没有变化。看起来貌似和Person不太一样是吧。String就是比较特殊。

特殊在哪里呢?

1.String存放的地方和普通的类不一样

2.String不可改变,一旦一个String对象定义完毕,没有任何修改它的地方,它的所有属性都不可修改。修改后的那就是另外一个String了

我们主要关心的有栈,堆,和String常量存放的地方——方法区。(http://zangxt.iteye.com/blog/472236)

栈里主要存放的基本数据类型(int,double等),和对象的引用(如Person p = new Person()中的p),有时递归过深时发生栈溢出,说的就是这个,因为存放了调用关系层次过多导致栈溢出。

堆里是供线程共享的内存区域,存放的是对象实例,譬如Person被new后,里面的所有属性都在堆里开辟了内存来存放,这一块也是gc需要频繁关注的地方。

String有一块常量区(方法区),用来存放String。

当执行String s1 = "abc";时,系统便在这块常量区里,开辟了内存保存“abc”,并将指向这块内存的指针s1返回给调用者。以后只要常量区里还有"abc",再去定义String s2 = "abc",系统就不会再次开辟内存,而是直接将地址返回给s2.所以s1==s2。

所以当你定义String s2 = "abc"时,系统可能创建一个对象或者不创建对象,如果已经有了,那就直接返回地址,如果没有,那就创建。

但是String s4 = new String("abc")时,系统至少创建一个对象,用了new关键字,也就是至少会在栈里创建一个s4,然后再去常量区判断“abc”是否存在,不存在时new String这一步就会创建两个对象。可以看到,这样会额外耗费一个栈里的空间。所以平时,多用直接=的方式来创建String。

所以s4!=s2.

然后再看另外一个问题:

String a = "abc";

String b = "ab";

String c = b + "c";

问a==c吗?

答案是不等。因为a和b都是字符串常量,在编译期就被确定了,内存地址已被确定。

而c里面有个b是个变量,b是存放在栈里的一个引用而已,所以c不会在编译期确定,只会在运行时确定。那么a!=c。

而且,上面说过,String类是final的,里面也没有任何修改属性的方法,String是不可变的。当执行b + "c"时,底层是用StringBuilder类的append方法进行连接字符串的,连接完毕再toString返回引用地址的。

那为什么"ab" + "c" 却 == "abc"呢?

这是Jvm在编译期做的优化。

String s = "ab" + "c";这种只有常量的,会在编译期被合并,等同于String s = "abc"。然后将"abc"作为常量存放在常量区。或者这样的,也是==的

"a" + 1 + "b" == "a1b"

假如是这样:

int x = 1;

String s = "a" + x + "b"; 

这样就不等了。

在编译期内,只会优化合并所有变量之前的常量。"a" + "1" + x + "b",像这种就相当于先合并"a1",然后再new一个StringBuilder再去append后面的几个值。

参考:http://www.cnblogs.com/ITtangtang/p/3976820.html

总结一下,直接指定的String,如String s = "abc",对象"abc"是存放在常量池中的,s在栈里。

String s = new String("abc"),对于通过 new 产生一个字符串时,会先去常量池中查找是否已经有了 ”abc” 对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此 ”abc” 对象的拷贝对象。

不管存放在哪,String都是不可变的,用+号连接时,走的是StringBuiler创建了新对象.

参考:http://blog.csdn.net/u014082714/article/details/50087563

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • android充当server服务器

        在android上跑起来一个web服务器,可供电脑和手机通过http访问。这个需求并不常见,网上资料也不多,找了一会发现了一个不错的框架。github链...

    天涯泪小武
  • 4 手写实现SpringMVC,第四节:匹配用户请求、执行映射方法

    在上一篇我们已经完成了配置的url到方法的映射,并且完成了method的各参数的注解、参数名、类型等的映射配置。

    天涯泪小武
  • 50亿加密手机号md5快速存储及检索,rocksDB、redis等探索

    首先需求比较简单,将所有的号码段(如130、131、132)的全部手机号的md5和其对应的手机号存起来,将来传入一批手机号的md5,能迅速给出对应的明文手机号。...

    天涯泪小武
  • Java基础知识之Scanner类和String类学习,讲明白了,适合初学者

    1、Scanner 的概述和方法介绍 A:Scanner 的概述 B:Scanner 的构造方法原理 Scanner(InputStream source) S...

    用户1289394
  • 当我遵循了这 16 条规范写代码,同事只对我说了三个字: 666

    Many of the happiest people are those who own the least. But are we really so ha...

    良月柒
  • 老板看了我的代码,直呼“666”,说涨工资!

    如何更规范化编写Java 代码的重要性想必毋需多言,其中最重要的几点当属提高代码性能、使代码远离Bug、令代码更优雅。

    程序员小强
  • 这样规范写代码,同事直呼“666”

    Java团长
  • 这样规范写代码,同事直呼“666”

    zhisheng
  • Java-String.intern的深入研究

    When---什么时候需要了解String的intern方法: 面试的时候(蜜汁尴尬)!虽然不想承认,不过面试的时候经常碰到这种高逼格的问题来考察我们是否真正理...

    SecondWorld
  • 如何更规范化编写 Java 代码

    如何更规范化编写 Java 代码的重要性想必毋需多言,其中最重要的几点当属提高代码性能、使代码远离 Bug、令代码更优雅。

    淡定的蜗牛

扫码关注云+社区

领取腾讯云代金券