第一,简介
String是java语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的immutable类,被声明成为final class,所有属性也都是final的。也由于它的不可变性,类似拼接,裁剪字符串等动作,都会产生新的String对象。String特性主要有两点。1,不可变:不可变的主要作用在于当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁等待的时间,从而大幅度提高系统性能。不可变模式是一个可以提高多线程程序的性能,降低多线程程序复杂度的设计模式。2,常量池的优化:当2个String对象拥有相同的值时,他们只引用常量池中的同一个拷贝。当同一个字符串反复出现时,这个技术可以大幅度节省内存空间。
StringBuffer是为了解决String拼接产生太多中间对象的问题而提供的一个类。StringBuffer本质是一个线程安全的可修改字符串序列,它保证了线程安全,也随之带来了额外的性能开销。
StringBuild是jdk1.5中新增的,在能力上和StringBuffer没有本质区别,但是它去掉了线程安全的部分,有效减小了开销。
为了实现修改字符串序列的目的,StringBuffer和StringBuild底层都是利用了可修改的(char,JDK9以后是byte)数组,二者都继承了AbstractStringBuilder,里面包含了基本操作,区别仅在于最终方法是否加了synchronized。它们内部构建初始化数组长度是16,长度大于16时会自动扩容,需要抛弃原有数据创建新(可以简单认为是倍数)的数组,还要进行arraycopy。所以我们可以根据字符长度传入初始化的合适长度,避免多次扩容的开销。除非有线程安全考虑建议使用StringBuild。
第二,String的创建机理和应用场景
创建机理:由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。举例:
String str1 = "hello,world"; //通过直接量赋值方式,放入字符串常量池。
String str2 = new String(“hello,world”);//通过new方式赋值方式,不放入字符串常量池。
注意:String提供了inter()方法。调用该方法时,如果常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并且返回此池中对象的引用。
应用场景:在字符串内容不经常发生变化的业务场景优先使用String类。例如:常量声明、少量的字符串拼接操作等。如果有大量的字符串内容拼接,避免使用String与String之间的“+”操作,因为这样会产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。
第三,轻松理解String.intern()
该方法的总结参考《深入理解Java虚拟机》一书。
1,new String都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。 JDK1.8后字符串常量池放到了堆中,不再是在方法区中了。
2,通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。
3,常量字符串的“+”操作,编译阶段直接会合成为一个字符串。如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用。
4,对于final字段,编译期直接进行了常量替换(而对于非final字段则是在运行期进行赋值处理的)。
5,常量字符串和变量拼接时(如:String str3=baseStr + “01”;)会调用stringBuilder.append()在堆上创建新的对象。
6,JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。
举例说明:
第四,String使用equals和==比较的区别?
“==”操作符的作用:1,用于基本数据类型的比较。2,判断引用是否指向堆内存的同一块地址。
equals的作用:用于判断两个变量是否是对同一个对象的引用,即堆中的内容是否相同,返回值为布尔类型
结论:1,对象不同,内容相同,"=="返回false,equals返回true。2,同一对象,"=="和equals结果相同
我是温驭臣,一个Android开发者,以上是我的简单总结,如果有缺陷,希望在评论区看到您的补充。