一:引出问题
在学习字符串的两种比较方式时发现了一个现象,进行一下总结;
String str1 = "kaka";
Srting str2 = "kaka";
System.out.println(str1 == str2); //true
在之前的学习中,String
类型被认为是引用类型,是存放在堆中的,因此在使用"==
"判断时,他会将堆内存地址一并进行判断,应该是返回false
的,为什么返回了true
???。
二:String的两种实例化方式
1:直接赋值
此种方式是最常用的
String str = "kaka";
2:使用String的构造方法
String str = new String("kaka");
那么这两种方式有什么不同呢?
三:引入字符串对象池进行区分
其实开始的问题的造成原因根本上就是由于字符串对象池(String Pool)
的存在造成的。
JVM为了提高性能和效率,防止不必要的重复创建造成的内存空间的浪费和不断的销毁造成的性能下降,因此创建了String Pool。那么这个字符串对象池是如何提高效率的呢?
1:使用直接赋值
开始的问题中使用直接赋值的形式进行创建的两个字符串变量。使用"=="进行判断时,会返回true,这就说明这两个值"hello"是来自于同一块内存空间。这是因为在使用直接赋值的方式实例化字符串变量时,是拥有了对象池的处理功能的。
这个对象池相比较于其他的对象new
后在堆中开辟的独立的内存空间,他是公用的。因此
String str1 = "kaka";
Srting str2 = "kaka";
str1在创建了kaka这个字符串对象后,就直接自动的执行入池操作。在创建str2时,他会先在字符串对象池中先进行查找看有是否相同内容的字符串对象。若没有创建,若有则直接引用。
以上就解释了为什么在使用"=="后会返回true。
2:使用构造方法
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);//false
在java中使用new就代表着在堆中开辟新的内存空间,而str1,和str2就是在堆中开辟了两个不同的堆内存空间进行存储相同的内容。因此他们的地址是不同的,因此返回false。
如何将使用构造方法创建的字符串变量也放入池中?
使用方法intern()
方法
String str1 = new String("hello").intern();
String str2 = new String("hello").intern();
System.out.println(str1 == str2); //true
四:静态对象池和运行时对象池
说到这不得不说一下常量池,常量池的两种状态的区分主要是 在于常量和变量的区分。
1:静态对象池(静态常量池)
编译形成的.class
文件是用来进行先期的内存分配的,他所有的操作内容都会以常量的形式进行出现。
String str1 = "hello" + "World";
String str2 = "helloWorld";
System.out.println(str1 == str2); // true
str1的字符串中的两个常量的拼接是在编译时期执行的,在编译期结束后他的存在将不会以多个常量的形式存在,而是以一个整体常量存在。在编译期后存在的都是经过修饰的。
而且在编译结束后,字符串对象就会被自动放在了String Pool中了。
2:运行时对象池(运行时常量池)
在运行时期的数据内容并不是固定的,他可能是通过变量保存的。因此他的内容是无法固定的。
String midTemp = "World";
String str1 = "hello" + midTemp;
String str2 = "helloWorld";
System.out.println(str1 == str2); // false
在上述代码的第二行是使用的变量进行的拼接,因此在编译期,他是无法进行拼接成功的,导致无法放入String Pool。在编译期中执行了字符串的拼接之后,他会在堆中开辟新的内存空间,也就不会有相同的内存地址,因此会返回false。
四:总结
刚开始准备开始后端工作,以上只是将基础的概念区分一下,并没有深入的了结。如有错误还请指出。