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

东方客主 等级 399 0 0

简介

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编程 经验技巧汇总
1.JSONArray数组如何循环遍历 java package xxx; import net.sf.json.JSONArray; import net.sf.json.JSONObject; public class Test { public static void main(String args) { /author:命运的信徒 da
一文弄懂Java中String的所有小秘密
简介 String是java中非常常用的一个对象类型。可以说java中使用最多的就是String了。那么String到底有哪些秘密呢?接下来本文将会一一讲解。 String是不可变的 String是不可变的,官方的说法叫做immutable或者constant。 String的底层其实是一个Char的数组。 priv
java实现九九乘法表的输出
这是小学二年级令人难忘的坷如今以java程序的行式展示,真不戳! public class score{ public static void main(String args) { int i,j; for(i1;i<9;i) { for (j 1; j < 9; j
java传值和传引用问题
这个问题还是很常见的,如果你平常敲代码比较多你可能经常会遇到这个问题。如果你知道java这个机制,你可能还会一直在找代码的问题。java中的值传递和引用传递。比如下面有这俩个方法java private void updataValue(String s){ s "123"; } private void upd
java截取url后缀以及判断是否带参数
java获取url后缀,以及判断是否带参数(?paramsxxx)java // 使用了String对象的方法截取字符串 String fileUrl "http://www.baidu.com?a1234"; int index fileUrl.lastIndexOf("?"); if (index 1) { file
java中List数组遍历删除
List数组遍历删除 环境 jdk8 junit 单元测试 正解java// 正解1, jdk自带的addAll方法 @Test public void test18() { String strs {"12","34","56","78","90"}; List<String list Ar
SQL 语句中 where 条件后 写上1=1 是什么意思
↑ 点击上方 “凹凸数据” 关注 + 星标 每天更新,干货&福利不断   这段代码应该是由程序(例如Java)中生成的,where条件中 1=1 之后的条件是通过 if 块动态变化的。例如: String sql"select  from tablename where 11"; if( conditon 1)   sqlsql+"  an
[C#]ArrayList、string、string[]之间的转换
1、ArrarList 转换为 string\[\] :  ArrayList list new ArrayList();  list.Add("aaa");  list.Add("bbb");  string\[\] arrString (string\[\])list.ToArray(typeof( string)) ;2、string\[\] 转换
使用Java 语言编写一个贪吃蛇游戏
使用 Java 语言编写一个贪吃蛇游戏,代码如下:GreedSnake .java package jzjsfx;public class GreedSnake public static void main(String[] args) SnakeModel model new SnakeModel(20, 30); SnakeControl c
阿里Java开发手册!非科班生金九银十求职经历
1 基础 为什么 Java 中只有值传递? int 范围?float 范围? hashCode 与 equals,什么关系? String StringBuffer 和 StringBuilder 的区别是什么?String 为什么是不可变的? Java 序列化中如果有些字段不想进行序列化 怎么办? 构造器 Constructor 是
阿里最新面试必备项之Java的String类,持续更新中!
最新腾讯面试必备项之Java的String类,持续更新中! 1.1 String的特性 String类:代表字符串。Java程序中的所有字符串字面值(如“abc”)都作为此类的实例实现。 String是一个final类,代表不可变的字符序列。 String字符串是常量,用双引号引起来表示。他们的值在创建之后不能更改。 String对象的找字符内容是存储在一个
java中去除字符串(String)中的换行字符(\r \n \t)
本文转载自 若有侵权,请联系本人删除例1:public class Test public static void main(String\[\] args) String s \ "'sds gdasda" + "\\n" + "edaeafd'"; System.out.println("转换前:"+s);
阿里程序员的Java之路!2021年最新Java面试点梳理
面试题模块介绍: 一、Java 基础 JDK 和 JRE 有什么区别? 和 equals 的区别是什么? 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? final 在 java 中有什么作用? java 中的 Math.round(1.5) 等于多少? String 属于基础的数据类型吗?
腾讯T3团队整理,持续更新中
Java基础1.JAVA 中的几种数据类型是什么,各自占用多少字节。2.String 类能被继承吗,为什么。3\. 两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?4\. String 属于基础的数据类型吗?5.Java 中操作字符串都有哪些类?它们之间有什么区别?6.Java 中 IO 流分为几种?7.BIO、NIO
踩坑了!熬夜整理小米Android面试题
一、Java初中级面试题1.容器(HashMap、HashSet、LinkedList,HashSet等)2.内存模型3.JVM、Davilk、ART 三者的原理和区别4.垃圾回收机制5.类加载方案6.说说你对Java 反射的理解7.说说你对动态代理的理解8.什么是线程池,如何使用?为什么要使用线程池?9.在多线程运行过程中,解决安全性问题?10.设计模式(