一文弄懂Java中String的所有小秘密

东方客主 等级 752 0 0
标签: string赋值Java

简介

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

String是不可变的

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

String的底层其实是一个Char的数组。

private final char value[]; 

所有的String字面量比如"abc"都是String的实现。

考虑下面的赋值操作:

String a="abc";
String b="abc"; 

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

有了“abc”,然后我们将“abc” 赋值给a和b。

一文弄懂Java中String的所有小秘密

image

可以看到这里a和b只是java heap中字符串的引用。

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

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将会拷贝一份新的数组,而不是使用之前的数组。从而不会发生上面的内存泄露的问题。

收藏
评论区

相关推荐

一文弄懂Java中String的所有小秘密
简介 String是java中非常常用的一个对象类型。可以说java中使用最多的就是String了。那么String到底有哪些秘密呢?接下来本文将会一一讲解。 String是不可变的 String是不可变的,官方的说法叫做immutable或者constant。 String的底层其实是一个Char的数组。 priv
阿里最新面试必备项之Java的String类,持续更新中!
最新腾讯面试必备项之Java的String类,持续更新中! 1.1 String的特性 String类:代表字符串。Java程序中的所有字符串字面值(如“abc”)都作为此类的实例实现。 String是一个final类,代表不可变的字符序列。 String字符串是常量,用双引号引起来表示。他们的值在创建之后不能更改。 String对象的找字符内容是存储在一个
JAVA 中为什么String 是immutable的
本文翻译自:[http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fwww.programcreek.com%2F2013%2F04%2Fwhy-string-
Java 中的 String 有没有长度限制?
![](https://oscimg.oschina.net/oscnet/34267162e55b43124ad6190129d01cc9085.jpg) 这是我的第 199 期分享 作者 | Hollis 来源 | Hollis(ID:hollischuang) 分享 | Java中文社群(ID:javacn666) 关于String有没有长
Java中有趣的String、StringBuffer与StringBuilder
String介绍 ---------   String类属于java.lang包中,String类是不可变类,任何对String的改变都会引发新的String对象的生成。   创建String的两种方式:   1.通过构造器创建:String str = new String("I am a string."); 2.通过变量赋值:String
Java中的String,StringBuilder,StringBuffer三者的区别
[Java中的String,StringBuilder,StringBuffer三者的区别](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.cnblogs.com%2Fsu-feng%2Fp%2F6659064.html) -----------------------------
Java数字序号转中文读写序号
1 public class NumToChineseUtil { 2 3 /** 4 * Java 好用的 5 * int 数字转中文 6 * @param src 7 * @return 8 */ 9
Java数组操作的10大方法
0、定义一个Java数组 ------------ String[] aArray = new String[5]; String[] bArray = {"a","b","c", "d", "e"}; String[] cArray = new String[]{"a","b","c","d","e"}; 第一种是定义了一个数组
java Clob类型 转String
1、我的数据库是oracle11g 遇到取出来的字段是clob类型,但是所需要的是string类型,写一个转换函数就可以解决问题了。 // Clob类型 转String public String ClobToString(Clob clob) throws SQLException, IOException {
java string 字符串替换
i、replace方法     该方法的作用是替换字符串中所有指定的字符,然后生成一个新的字符串。经过该方法调用以后,原来的字符串不发生改变。例如:      String s = “abcat”;      String s1 = s.replace(‘a’,‘1’);     该代码的作用是将字符串s中所有的字符a替换成字符1,生成
java中string和stringbuffer
 String s="Hello"; s+="World"; //以上代码等价与下述代码 StringBuffer sb=new StringBuffer(s); sb.append("world"); s=sb.toString(); StringBuffer会产生许多临时对象,从而导致程序的执行效率变低,
String 和 new String()的区别
String 和 new String()的区别 For Example String str1 = "ABC" String str2 = new String("ABC"); String str1 = "ABC"; 可能创建一个对象或者不创建对象。 如果"ABC" 这个字符串z在java String池中不存在,会在java String池中
String
public final class String extends Object implements Serializable, Comparable<String>, CharSequence String 类型继承 Serializable, Comparable<String>, CharSequence 接口。 The String class r
String
### **一,String,StringBuffer,StringBuilder三者之间的关系** 三个类的关系:StringBuffer和StringBuilder都继承自AbstractStringBuilder这个类, 而AbstractStringBuilder和String都继承自Object这个类(Object是所有java类的超类)
String字符串相等判断
\== :比较的是内存地址值 equals: 比较的是值,对象的equals()可以被重写 java中字符串的比较:== -------------- String a="abc"; String b="abc" 那么a==b将返回true。因为在java中字符串的值是不可改变的,相同的字符串在内存中只会存 一份,所以a和