责编 | 屠敏
出品 | CSDN 博客
字符串在Java生产开发中的使用频率是非常高的,可见,字符串对于我们而言非常关键。那么从C语言过来的同学会发现,在C中是没有String类型的,那么C语言要想实现字符串就必须使用char数组,通过一个个的字符来组拼成字符串。 Java中是如何实现字符串的
那其实在Java中,关于字符串的实现,其实用的也是char数组,这可以从源码中得到体现。
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}这是String类的构造方法,而这个value实际上就是char数组。
/** The value is used for character storage. */
private final char value; 字符串在内存中的保存方式
我们都知道如何去创建一个字符串,那么, 字符串在内存中的保存方式是怎样的呢?
在内存中有一个区域叫做常量池,而当我们以这样的方式去创建字符串:
String s1 = "abc";
String s2 = "abc";这个字符串就一定会被保存到常量池中。而Java虚拟机如果发现常量池中已经存在需要创建的字符串中,它就不会重复创建,而是指向那个字符串即可。
通过这个图来理解一下,首先第一行代码会在常量池中创建hello和world两个字符串,接着在堆中开辟了一个空间存放组合后的字符串helloworld,然后变量s1指向它。我们说intern会返回常量池中的字符串,那么在常量池中没有helloworld的情况下intern方法会怎样处理呢?其实它会将对堆中helloworld的引用放入常量池中,此时s1.intern和s1都指向的是同一个对象,它们是相等的。但是s2在创建的过程中也会在堆中开辟一个空间存放helloworld,使变量s2指向它,而s2.intern方法在执行的时候发现,helloworld的引用已经存在,所以直接返回,但此时返回的其实是s1变量的引用,那么s2.intern与s2不相等相信大家能够理解了。
String s1 = new String("hello") + new String("world");
System.out.println(s1.intern == s1);
String s2 = new String("hello") + new String("world");
System.out.println(s2.intern == s1);那么这段程序的输出结果你若是能立马知晓,那么恭喜你,前面的知识点你已基本掌握。执行结果就是:
true
true我们还可以通过一个极端的方法来判断常量池的位置。
List list = new ArrayList;
String str = "boom";
for(int i = 0;i < Integer.MAX_VALUE;i++) {
String temp = str + i;
str = temp;
list.add(temp.intern);
}通过编写这一段程序能够让JVM去不停地将字符串变量存入常量池从而使其内存溢出,内存溢出后控制台信息如下:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:2694)
at java.lang.String.(String.java:203)
at java.lang.StringBuilder.toString(StringBuilder.java:405)
at com.itcast.test2.StringTest.main(StringTest.java:25)可以看到,控制台信息提示堆内存溢出,这也可以得出常量池的位置是在堆内。
这是Java7及其以后版本的输出信息,当我们将版本切换为Java7之前的版本,同样的代码,输出信息如下:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.util.Arrays.copyOfRange(Arrays.java:2694)
at java.lang.String.(String.java:203)
at java.lang.StringBuilder.toString(StringBuilder.java:405)
at com.itcast.test2.StringTest.main(StringTest.java:25)PermGen space其实就是方法区, 那么其实在JVM中的堆,一般分为三大部分:新生代、老年代、永久代:这个PermGen space就是永久代,也就是方法区,叫法不同而已。