在Java中关于字符串的处理可以说是五花八门,也是在使用Java过程中一个非常重要的基础知识。
字符串一共有此几类:String、StringBuilder、StringBuffer,还有极少用的StringJoiner。接下来介绍一下String。
String算是最基础的引用数据类型的字符串格式,初始化常用的方式有:
1.
String s = “abc”;
2.
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArray); // "Hello"
3.
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArray, 0, 2); // "He"
4.
byte[] byteArray = {72, 101, 108, 108, 111}; // 对应 ASCII 码的 "Hello"
String str = new String(byteArray); // "Hello"
5.
byte[] byteArray = {72, 101, 108, 108, 111}; // 对应 ASCII 码的 "Hello"
String str = new String(byteArray, 0, 3); // "Hel"
6.
StringBuilder stringBuilder = new StringBuilder("Hello");
String str = new String(stringBuilder); // "Hello"
分点开讲吧:
字符串的创建
String不是基本数据类型,他是java.lang包下的一个类,属于引用数据类型,并且String中的数据是不可变的。
String的创建:
要明白在Java中,String的创建会储存在堆内存的“串池”中,在创建String时,首先会检测在“串池”中有没有已存在的相同的字符串,如果存在,则会直接指向这个地址值,代码演示是这样的:
String a = “abc”;
String b = “abc”;
System.out.println(a == b); // 此时是true
有没有想过为什么?这不是理所应当?但是看下面的:
String a = “abc”;
String b = new String(“abc”);
System.out.println(a == b); // 此时是false
心理活动:为什么呢?这两种内容不都是abc嘛?
答:我们首先要明白一点, == 在java中如果对于引用数据类型使用,比较的是地址值,String正是引用数据类型,此时第二个案例的false充分说明了这一点,对比的是地址而不是内容。
其中的重点就是第二行的字符串b是new出来的,(知识点:new出来的对象会独立在堆内存中开辟一份内存空间)此时并不是在“串池”中,所以此时两个地址自然不一样。所以我们在比较String时一定要使用equals()进行比较。当然,如果你真的想比较地址值那就用 == 。
再看这里:
String a = new String("abc");
问:会创建几个对象?
答案是:1个或2个。
答:因为刚刚说过new是在堆内存中开辟一份新的内存空间,所以自然会有一个对象,但是在new的过程中,是要从“串池”拿数据的。
问:所以为什么从“串池”拿?
答:仔细看,你最后的小括号里是不是有一个字符串”abc”,这个字符串给你做有参构造可不是白做的,你既然写出来了“abc”他就是一个引用数据类型了,会直接在“串池“创建一份存储,再进行有参构造的,所以就是1个或者两个了。当”串池”存在 “abc”直接用,不存在新创一个对象。
字符串的拼接
接下来这里:
String a = “abc”;
String b = “a” + “bc”;
System.out.println(a == b); // 此时是true
在字符串的拼接过程中的创建原理:
a的初始化就是直接在”串池”里创建一个abc字符串,
当b进行拼接创建时,系统在编译为.class文件时,其实会直接讲第二行变换成:
String b = “abc”;
所以接下来不就明了了,一样的字符串进行创建不会直接创建,而是回去”串池”找有没有相同的,找到了直接指向,所以在这里便是true。
继续:
String a = “abc”;
String b = “a”;
String c = b + “bc”;
System.out.println(a == c); // 此时是false
内心:刚刚还说拼接就是直接变成”abc”指向“串池”现在咋是false了?
答:注意看,这里是有变量参与了,在拼接过程中如果有变量参与,在Java中String的变量拼接的底层原理中,其实是基于StringBuilder实现的,(这里如果不清除Stringbuilder的可以先去了解一下),此时在源码中会直接在堆内存new一块独立的存储空间进行拼接,所以就解释了为什么这里是false。因为在拼接过程中基于StringBuilder,但是拼接过程中是new出来的,所以又回到上一点了,便是false。
字符串的拼接原理
刚刚说到,String的变量拼接是基于StringBuilder的,所以是将字符串先拆解成一个char[]数组,这样成了基本数据类型,再进行拼接。
在JDK8之前,在拼接时设这样的,看代码:
String a = “a”;
String b = “b”;
String c = “c”;
String d = a + b + c; // 这里最少创建4个对象
System.out.println(d);
为什么会创建4个对象呢?
首先a + b , a和b各是一个对象, 在第一次拼接时:基于StringBuilder创建出第三个对象S1,这样就是三个对象,此时又进行一次拼接操作,将S1和c进行拼接,这不就又生成一个对象S2存放最终的变量。
但是在JDK8之后,做了一个小优化,就是在拼接之前会对变量进行预估,预估一个大小相符的字符数组用来存储拼接,但是如果拼接数量庞大,那在预估过程中岂不是也会浪费很多时间。所以建议在大量使用拼接操作时直接使用StringBuilder。