您的位置:首页 > 编程语言 > Java开发

菜鸟入坑》String深入了解

2018-03-09 23:16 447 查看

1.String在JVM中的内存分配

说到String首先要认识一下,String对象的创建,它在JVM(java虚拟机)中的内存分配。主要有两种情况:1在方法区的字符串常量池中,2在方法区的字符串常量池中和堆中。
在方法区中:
String  str1 = "abc"; String对象“abc”分配在方法区的字符串常量池中。
在方法区的字符串常量池中和堆中:

String str2 =new String( "ab"); String对象“ab",会先在堆中创建一个“ab”对象,之后字符串常量池也会创建“ab”对象。str2引用指向的是堆中的“ab”对象。

2.String的创建

我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,对应的引用就指向常量池已创建好的对象。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。
注意:这里是针对不以new方法创建的。new方式创建的都会在堆中创建对象。String str2 = new String("abc");
String str3 = "abc";
String str4 = new String("abc");
String str1 = "abc";
System.out.println(str1==str2);//false
System.out.println(str1==str3);//true
System.out.println(str2==str3);//false
System.out.println(str1==str4);//false
System.out.println(str2==str4);//fals

3.String的不变性认识

很多人认为String是final修饰的,所以创建String对象之后,它的值就不能改变了。实际上不正确。final修饰的类是无法被继承。我们打开源码看一下就会发现,String的内容是放到char[]数组中的同时被final修饰。所以这才是String对象创建之后,值就不变的原因。private final char value[];注意:初学者就有疑问了!String str1 = "123";
str1 = "234";这样不是改变了吗?问这个问题的说明,他对引用和对象还是不是很了解。首先str1是对象的引用,第一句str1这个引用对应的地址指向了对象“123”,第二句str2这个引用对应的地址指向了对象“234”。这两句是分别创建了两个对象,一个是“123”,一个是“234”。

4.String到底创建了多少个对象

这个问题很多面试都会遇到。
案例1:public void test1(){
String str1 = "123";//创建了一个对象,在字符串常量池中
String str2 = new String("123");//创建了两个对象,一个在堆中,一个在常量池中
System.out.println(str1==str2);//引用str1的地址指向字符串常量池的对象"123",引用str2的地址指向堆中的对象"123",所有引用地址不一样返回false
}案例2:public void test2(){
String str1 = "123";//创建了一个对象,在字符串常量池中
String str2 = "123";//先查字符串常量池看是否存在"123"对象,发现存在,不创建了,str2直接指向字符串常量池中"123"对象
System.out.println(str1==str2);//地址和内容一致返回true
}案例3:public void test3(){
String str1 = "123";
String str2 = "456";
String str3 = "123456";
String str4 = "123" + "456";//不会产生新的字符串对象
System.out.println(str3 == str4);//true
str4 = str1 + "456";//会产生新的字符串对象
System.out.println(str3 == str4);//false
str4 = str1 + str2;//会产生新的字符串对象
System.out.println(str3 == str4);//false
}分析后面两个为何为false。因为编译过程中,str4 = str1+"456";编译器认为str1是一个变量,不是常量。而str4 = "123"+"456"编译过程中就是一个常量。最后一句也同理。
对应的.class文件:



案例4:public class TestC {
final String str1 = "aaa";
final String str2 = "bbb";
@Test
public void test1(){
String str3 = str1 + str2;
String str4 ="aaabbb";
System.out.println(str3==str4); //输出为true
}
}分析:str1和str2都被final修饰了,是常量。在编译过程,str1+str2就对应了一个常量。
.class文件:



案例5:public class TestC {
final static String str1;
final static String str2;
static {
str1 ="aaa";
str2 ="bbb";
}
public void test1(){
String str3 = str1 + str2;
String str4 ="aaabbb";
System.out.println(str3==str4); //输出为false
//此时str1与str2相当于变量,而不是常量,因为静态语句块是在运行时才能确定,在编译时不能确定
}
}.class




5.String是线程安全的还是不安全的讨论

线程安全和不安全对String来说似乎没有讨论的必要。因为你创建了一个String对象,它的值就一定了。没有必要讨论是否线程安全或不安全了。如果非要定义,那就是线程安全的,不存在线程不安全的问题。

最后由于个人能力问题,有些地方还存在问题。请各位大牛指出!谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java基础 String