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

被折叠的 条评论
为什么被折叠?



