为了避免大家出现此类情况,不在基本问题上丢失工作机会,本篇文章我们一起来深入了解一下这三个对象。
String
在Java中字符串不是基本数据类型,而是对象。
Java提供了String类来创建和操作字符串。需要注意的是,String的值是不可变的,因为String对象被final修饰,是不可变的类对象。
从String的源码中可以看出,String类是一个final类,因此每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
下面我们看一段代码(代码区域可以左右滑动):
public class TestDemo { public static void main(String[] args) { String s1 = "hello"; String s2 = "java" + "python"; String s3 = s1 + "world"; String s4 = new String("hello"); }}
我们看一下这段代码的反编译代码(代码区域可以左右滑动):
public static void main(java.lang.String[]); Code: 0: ldc #2 // String hello 2: astore_1 3: ldc #3 // String javapython 5: astore_2 6: aload_1 7: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String; 12: astore_3 13: new #5 // class java/lang/String 16: dup 17: ldc #2 // String hello 19: invokespecial #6 // Method java/lang/String."":(Ljava/lang/String;)V 22: astore 4 24: return}
你会发现,每次在操作字符串的时候,jvm都会创建新的对象,摒弃之前的对象,这样就造成了内存大量的浪费。
那么对于下面的代码:
String name="王者荣耀";System.out.println(name);name="吃鸡";System.out.println(name);
很多人会认为,不是说字符串不可变吗?这个name的值怎么变化了呢?其实我们看似变化了,其实jvm内部是这样操作的。
由此可见,name最终抛弃了“王者荣耀”这个值,最后指向了“吃鸡”,但是“王者荣耀”这个内容还在内存中,会随着下一次垃圾回收给回收掉。可是垃圾回收机制的时机是不可控的,这就造成了在此期间内存的浪费!
再看以下代码:
String str1 = "hello world";String str2 = new String("hello world");String str3 = "hello world";String str4 = new String("hello world"); System.out.println(str1==str2); System.out.println(str1==str3); System.out.println(str2==str4);
输出结果:
falsetruefalse
在上述代码中,String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了 字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。
众所周知,通过new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。
StringBuffer
简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。
StringBuffer是线程安全的,所有的方法都加锁,进行资源同步串行化处理,我们可以通过它的源码可以看出:
StringBuilder
StringBuilder是线程不安全的, StringBuilder 类表示一个可变的字符序列,我们知道,StringBuilder 是非线程安全的容器,一般适用于 单线程 场景中的字符串拼接操作。 StringBuilder与StringBuffer继承体系应用场景
String
在字符串内容不经常发生变化的业务场景优先使用String类例如:常量声明、少量的字符串拼接操作等
StringBuffer
在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程环境下
例如XML解析、HTTP参数解析与封装。
StringBuilder
在频繁进行字符串的运算(如拼接、替换、删除等) ,并且运行在单线程环境下
例如SQL语句拼装、JSON封装等。
三者在执行速度方面的比较:StringBuilder > StringBuffer > String
好了,今天的内容就到这里,希望各位面试的时候不仅仅是只回答一些片面的东西,你的回答代表着你开发功底的深浅。
往期精彩回顾
编程学不会?这样学,你会成为大牛!
Windows很卡该怎么办?我只说这一次!
我要吹爆它!Windows办公神器软件!!
Java必备软件合集
希望这篇文章可以帮助到你~欢迎大家点个 在看,分享至朋友圈 IT的路上 我 一直陪着你写留言99+