java String类详解

本文详细解析了Java中String类的内部结构、字符串常量池的工作原理及其在不同JDK版本中的变化,探讨了字符串创建、存储位置、加法操作、String.intern()方法的使用场景,以及String、StringBuffer与StringBuilder的区别。

1.String类的数据结构与特点

String类是通过char数组来保存数据的。

String类由final修饰,无法被继承,内部方法同理也无法重写。

String类由final修饰的原因:①避免线程不安全的问题;②为了避免安全隐患,如果每一次使用String都在前面加上final使用的不好会导致性能问题,干脆就做成final类,放在常量池实现数据共享,节省资源,提高效率,可以在JVM里做很多优化。

 

2.字符串常量池

作用:为了节省资源空间,避免相同的字符串的重复创建,同时也省去了重复创建的时间提升了性能。

创建字符串:创建字符串时JVM会检查常量池中是否存在该字符串,已存在就直接返回字符串的引用,不存在的就在常量池中创建该字符串。

*常量池:常量池分为静态常量池与运行时常量池。

静态常量池顾名思义就是在*.class文件中的字面量,也包括类和方法的信息。

运行时常量池又称字符串常量池,就是将类装载之后,class文件中的常量池会载入到内存中,具体位置在不同的java版本会有不同。

jdk1.6中,运行时常量池是在方法区中,属于“永久代”。

jdk1.7中,运行时常量池被移除方法区,放在java堆中。

jdk1.8中,取消了“永久代”,将运行时常量池放在元空间,独立于堆。

取消永久代的原因:

(1)字符串存在永久代中,容易出现性能问题和内存溢出。

(2)类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

(3)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小

3.字符串的创建,以及字符串存储的位置。

String s1 = "sssss"; //直接保存在字符串常量池中;

String s2 = new String("sssss"); //堆中创建对象,常量池中有对应的常量。

用这种方式创建字符串时,首先也是检查字符串常量池中是否存在相应的字符串,没有则在堆中直接创建,返回堆的地址。如果常量池中存在对于的字符串,则通过复制的方式,将字符串拷贝一份在堆中,返回在堆中的地址。

String s3 = s1 + s2; //程序运行时创建,只存放在堆区。

String s4 = "sss" + "sss"; //编译时创建,存放在字符串常量池中。

toString()方法只会在堆中创建字符串对象。

intern()方法在堆上创建对象,在常量池创建引用。(1.7版本,1.7之前的只在常量池创建)

4.String.intern()方法详解

intern到底做了些什么?

intern用来返回运行时常量池中的某字符串,如果运行时常量池中已经存在该字符串,则直接返回运行时常量池中该对象的引用。否则,在常量池中加入该对象,然后 返回引用。

java1.7前后版本intern的区别?

① jdk1.6中字符串常量池存放在方法区”永久带“中,当使用intern()方法时,intern先判断常量池中是否存在该常量,存在直接返回该引用;不存在则先将字符串复制到常量池中,再返回常量池中的该字符串引用。

② jdk1.7之后,intern会先判断常量池中是否存在当前字符串,不存在时不会将当前字符串复制到常量池,只是将当前字符串的引用放在常量池。如果存在,也不会改变存在的引用,以及只会保存首次存储的字符串的引用。

什么时候会用到intern()?

String.intern()方法可以用来处理在Java中字符串的重复问题。通过使用intern()方法,可以节省大量由重复字符串对象消耗的堆内存。

5.String的加法操作

String c = "xx" + "yy " + a + "zz" + "mm" + b; 实质上的实现过程是:

String c = new StringBuilder("xxyy").append(a).append("zz").append("mm").append(b).toString();

之所以这么做的原因是因为:

非常量字符串相加时,由于相加的变量中存放的是字符串的地址引用,在编译时无法确切地知道其具体的值,也就没有办法对其进行优化处理,这时为了达到连接的效果,其内部采用了 StringBuilder 的机制进行处理,实际上是产生了一个StringBuilder对象通过append累加后用toString()方法产生String对象,上面提到,toString方法只会在堆中创建字符串对象。

而常量之间的相加,比如String d = "sss" + "ssa";不会有类似的问题,编译器会对这些进行优化。

6.String,StringBuffer与StringBuilder的区别

String一旦创建对象,该对象就不可变,即每次修改都是重新创建一个新的对象,再将引用指向新的对象。

StringBuffer和StringBuilder都是可变的,可变就意味着可能出现线程不安全的问题,而StringBuffer中的方法大多是采用synchronized关键字修饰,是线程安全的,单页同样意味着在非多线程情况下的性能问题。StringBuilder是非线程安全的。

一般情况下,String适用于少量的字符串操作,StringBuilder适合单线程下的大量字符串操作,StringBuffer适合多线程下的大量字符串操作。

7.String的==和equal

==作用于基础数据类型,是比较值是否相同;作用于引用类型是比较引用指向的地址是否相同。(即是否指向同一个对象)

equal是Object类的方法,String重写了equal方法,比较所指向的字符串的值是否相等。

其他如Double,Date,Integer都是如此。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值