一文弄懂String的全部小秘密

简介

String是java中很是经常使用的一个对象类型。能够说java中使用最多的就是String了。那么String到底有哪些秘密呢?接下来本文将会一一讲解。java

String是不可变的

String是不可变的,官方的说法叫作immutable或者constant。数组

String的底层实际上是一个Char的数组。安全

private final char value[];
复制代码

全部的String字面量好比"abc"都是String的实现。函数

考虑下面的赋值操做:this

String a="abc";
String b="abc";
复制代码

对于java虚拟机来讲,"abc"是字符串字面量,在JDK 7以后,这个字符串字面量是存储在java heap中的。而在JDK 7以前是有个专门的方法区来存储的。spa

有了“abc”,而后咱们将“abc” 赋值给a和b。线程

能够看到这里a和b只是java heap中字符串的引用。code

再看看下面的代码发生了什么:cdn

String c= new String("abc");
复制代码

首先在java heap中建立了“abc”,而后调用String的构造函数:对象

public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
复制代码

在构造函数中,String将底层的字符串数组赋值给value。

由于Array的赋值只是引用的赋值,因此上述new操做并不会产生新的字符串字面值。

可是new操做新建立了一个String对象,并将其赋值给了c。

String的不可变性还在于,String的全部操做都会产生新的字符串字面量。原来的字符串是永远不会变化的。

字符串不变的好处就在于,它是线程安全的。任何线程均可以很安全的读取字符串。

传值仍是传引用

一直以来,java开发者都有这样的问题,java究竟是传值仍是传引用呢?

我想,这个问题能够从两方面来考虑。

首先对于基础类型int,long,double来讲,对他们的赋值是值的拷贝。而对于对象来讲,赋值操做是引用。

另外一方面,在方法调用的参数中,所有都是传值操做。

public static void main(String[] args) {
	String x = new String("ab");
	change(x);
	System.out.println(x);
}
 
public static void change(String x) {
	x = "cd";
}
复制代码

咱们看上面的例子,上面的例子输出ab。由于x是对“ab”的引用,可是在change方法中,由于是传值调用,因此会建立一个新的x,其值是“ab”的引用地址。当x被从新赋值以后,改变的只是拷贝以后的x值。而自己的x值是不变的。

substring() 致使的内存泄露

第一次看到这个话题,你们可能会很惊讶,substring方法竟然会致使内存泄露?这个话题要从JDK 6开始讲起。

咱们先看下JDK 6的实现:

String(int offset, int count, char value[]) {
	this.value = value;
	this.offset = offset;
	this.count = count;
}
 
public String substring(int beginIndex, int endIndex) {
	//check boundary
	return  new String(offset + beginIndex, endIndex - beginIndex, value);
}
复制代码

能够看到,JDK 6的substring方法底层仍是引用的原始的字符串数组。惟一的区别就是offset和count不一样。

咱们考虑一下下面的应用:

String string = "abcdef";
String subString = string.substring(1, 3);
string = null;
复制代码

虽然最后咱们将String赋值为null,可是subString仍然引用了最初的string。将不会被垃圾回收。

在JDK 7以后,String的实现发送了变化:

public String(char value[], int offset, int count) {
	//check boundary
	this.value = Arrays.copyOfRange(value, offset, offset + count);
}
 
public String substring(int beginIndex, int endIndex) {
	//check boundary
	int subLen = endIndex - beginIndex;
	return new String(value, beginIndex, subLen);
}
复制代码

Arrays.copyOfRange将会拷贝一份新的数组,而不是使用以前的数组。从而不会发生上面的内存泄露的问题。

总结

虽然String是咱们常常使用的对象,可是里面的原理仍是值得咱们了解的。

本文做者:flydean程序那些事

本文连接:www.flydean.com/string-all-…

本文来源:flydean的博客

欢迎关注个人公众号:程序那些事,更多精彩等着您!

相关文章
相关标签/搜索