String
String类
java.lang.String类代表字符串。
字符串的特点:
字符串的内容永不可变。【重点】
正是因为字符串不可改变,所以字符串是可以共享使用的。
字符串效果上相当于是char[]字符数组,但是底层原理是byte[]字节数组。
String 为什么是不可变的?
String
类中使用 final
关键字修饰字符数组来保存字符串,所以String
对象是不可变的。
修正:我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的(final 修饰引用类型变量的情况)。
String 真正不可变有下面几点原因:
- 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
- String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。
1 |
|
String类final + char[] private
字符串常量池
双引号创建
程序当中直接写上的双引号字符串,就在字符串常量池中,只有用””创造的字符串,才创建在常量池中
字符串常量池的作用:由于字符串常量池的存在,相同的字符串常量在内存中只会存在一份,这对于节省内存、提高性能以及避免错误是非常有益的。
实际上字符串常量池 HashTable 采用的是数组加链表的结构,链表中的节点是一个个的 HashTableEntry而 HashTableEntry 中的 value 则存储了堆上 String 对象的引用

使用字面量声明 String 对象时,”也就是被双引号包围的字符串,在堆上创建对象,并驻留到字符串常量池中(注意这个用词)
调用 intern()方法,当字符串常量池没有相等的字符串时,会保存该字符串的引用
我们在上面用到了一个词驻留,这里对它进行一下规范。当我们说驻留一个字符串到字符串常量池时,指的是创建 HashTableEntry ,再使它的 value 指向堆上的 String 实例,并把 HashTableEntry 放入字符串常量池,而不是直接把 String 对象放入字符串常量池中。简单来说,可以理解为将 String 对象的引用保存在字符串常量池中。
new创建
1 |
|
- 对于引用类型来说,**==是进行地址值的比较**
- 若要进行内容比较,使用equals方法
- 使用new创建的String对象在堆中,底层使用的是private final byte[] value,value的地址指向常量池

在创建 s1 的时候,其实堆里已经创建了两个字符串对象 stringobject1 和 stringobject2 ,并且在字符串常量池中驻留了 stringobject2
当执行 s1.intern()方法时,字符串常量池中已经存在内容等于”Hydra”的字符串 stringobject2 ,直接返回这个引用并赋值给 s2
s1 和 s2 指向的是两个不同的 String 对象,因此返回 false
s2 指向的就是驻留在字符串常量池的 stringobject2,因此 s2==”Hydra”为 true,而 s1 指向的不是常量池中的对象引用所以返回 false
String的intern 方法有什么作用?
String.intern()
是一个 native(本地)方法,其作用是将指定的字符串对象的引用保存在字符串常量池中,可以简单分为两种情况:
- 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
- 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。
示例代码(JDK 1.8) :
1 |
|
String s1 = new String(“abc”); 这句话创建了几个字符串对象?
会创建 1 或 2 个字符串对象。
1、如果字符串常量池中不存在字符串对象“abc”的引用,那么它会在堆上创建两个字符串对象,其中一个字符串对象的引用会被保存在字符串常量池中。
示例代码(JDK 1.8):
1 |
|
对应的字节码:
ldc
命令用于判断字符串常量池中是否保存了对应的字符串对象的引用,如果保存了的话直接返回,如果没有保存的话,会在堆中创建对应的字符串对象并将该字符串对象的引用保存到字符串常量池中。
2、如果字符串常量池中已存在字符串对象“abc”的引用,则只会在堆中创建 1 个字符串对象“abc”。
示例代码(JDK 1.8):
1 |
|
对应的字节码:
这里就不对上面的字节码进行详细注释了,7 这个位置的 ldc
命令不会在堆中创建新的字符串对象“abc”,这是因为 0 这个位置已经执行了一次 ldc
命令,已经在堆中创建过一次字符串对象“abc”了。7 这个位置执行 ldc
命令会直接返回字符串常量池中字符串对象“abc”对应的引用。
String 类型的变量和常量做“+”运算时发生了什么?
先来看字符串不加 final
关键字拼接的情况(JDK1.8):
1 |
|
对于编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。
在编译过程中,Javac 编译器(下文中统称为编译器)会进行一个叫做 常量折叠(Constant Folding) 的代码优化。
对于 String str3 = "str" + "ing";
编译器会给你优化成 String str3 = "string";
。
并不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以:
- 基本数据类型(
byte
、boolean
、short
、char
、int
、float
、long
、double
)以及字符串常量。 final
修饰的基本数据类型和字符串变量- 字符串通过 “+”拼接得到的字符串、基本数据类型之间算数运算(加减乘除)、基本数据类型的位运算(<<、>>、>>> )
引用的值在程序编译期是无法确定的,编译器无法对其进行优化。
对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder
调用 append()
方法实现的,拼接完成之后调用 toString()
得到一个 String
对象 。
1 |
|
我们在平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder
或者 StringBuffer
。
不过,字符串使用 final
关键字声明之后,可以让编译器当做常量来处理。
示例代码:
1 |
|
被 final
关键字修饰之后的 String
会被编译器当做常量来处理,编译器在程序编译期就可以确定它的值,其效果就相当于访问常量。
如果 ,编译器在运行时才能知道其确切值的话,就无法对其优化。
示例代码(str2
在运行时才能确定其值):
1 |
|
String中内容的比较相关方法
如果确实需要字符串的内容比较,可以使用两个方法:
**public boolean equals(Object obj)**:参数可以是任何对象,只有参数是一个字符串并且内容相同的才会给true;否则返回false。
注意事项:
任何对象都能用Object进行接收。
equals方法具有对称性,也就是a.equals(b)和b.equals(a)效果一样。
如果比较双方一个常量一个变量,推荐把常量字符串写在前面。
- 推荐:”abc”.equals(str)
- 不推荐:str.equals(“abc”)
1 |
|
**public boolean equalsIgnoreCase(String str)**:忽略大小写,进行内容比较。
1 |
|
public int compareTo(String anotherString):按字典顺序比较两个字符串。
返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。
- 如果参数字符串等于此字符串,则返回值 0;
- 如果此字符串小于字符串参数,则返回一个小于 0 的值;
- 如果此字符串大于字符串参数,则返回一个大于 0 的值。
说明:
如果第一个字符和参数的第一个字符不等,结束比较,返回第一个字符的ASCII码差值。如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至不等为止,返回该字符的ASCII码差值。 如果两个字符串不一样长,可对应字符又完全一样,则返回两个字符串的长度差值。
1 |
|
String的相关方法
String的构造方法
1 |
|
String中与获取相关方法
**public int length()**:获取字符串当中含有的字符个数,拿到字符串长度
1 |
|
public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串
1 |
|
**public char charAt(int index)**:获取指定索引位置的单个字符。(索引从0开始)
1 |
|
**public int indexOf(String str)**:查找参数字符串在本字符串当中首次出现的索引位置,如果没有返回-1值
1 |
|
String中与截取相关方法
**public String substring(int index)**:截取从参数位置一直到字符串末尾,返回新字符串
1 |
|
**public String substring(int begin, int end)**:截取从begin开始,一直到end结束,中间的字符串。
备注:[begin,end),包含左边,不包含右边
1 |
|
public String trim()
返回字符串的副本,忽略前导空白和尾部空白。
String的比较
返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。
- 如果参数字符串等于此字符串,则返回值 0;
- 如果此字符串小于字符串参数,则返回一个小于 0 的值;
- 如果此字符串大于字符串参数,则返回一个大于 0 的值。
说明:
如果第一个字符和参数的第一个字符不等,结束比较,返回第一个字符的ASCII码差值。
如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至不等为止,返回该字符的ASCII码差值。 如果两个字符串不一样长,可对应字符又完全一样,则返回两个字符串的长度差值。
1 |
|
以上程序执行结果为:
1 |
|
String中与数组转换相关方法
**public char[] toCharArray()**:将当前字符串拆分成为字符数组作为返回值
1 |
|
**public byte[] getBytes()**:获得当前字符串底层的字节数组
1 |
|
字符串数组与字符串的转换
- public static String valueOf(char[] data) :返回 char 数组参数的字符串表示形式
- **public static String valueOf(char[] data, int offset, int count)**: 返回 char 数组参数的特定子数组的字符串表示形式,offset 参数是子数组的第一个字符的索引,count 参数指定子数组的长度。
String中分割与替换
**public String replace(CharSequence oldString, CharSequence newString)**:
将所有出现的老字符串替换成为新的字符串,返回替换之后的结果新字符串。
备注:CharSequence意思就是说可以接受字符串类型
1 |
|
public String[] **split(String regex)**:按照参数的规则,将字符串切分成为若干部分。
注意事项:返回的是字符串数组
split方法的参数其实是一个“正则表达式”
注意:如果按照英文句点“.”进行切分,必须写”\.”(两个反斜杠)
1 |
|
String的格式化
public static String format(String format,Object… args):使用指定的格式字符串和参数返回一个格式化字符串
1 |
|
保留2位小数
1 |
|
基本类型与String之间的转换
基本类型转换为String
基本类型转换String总共有三种方式
- **基本类型的值+ “ “**,最简单的方法
- 包装类的静态方法toString(参数),static String toString(int i) :返回一个表示指定整数的String对象
- String类的静态方法valueOf(参数),static String valueOf(int/boolean/double/float/long ) : 返回参数的字符串形式
1 |
|
String转换成对应的基本类型
包装类的parseXxx方法
所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型
public static char parseCharacter(String s) 将字符串参数转换为对应的char基本类型。
**public static byte parseByte(String s)**:将字符串参数转换为对应的byte基本类型。
**public static short parseShort(String s)**:将字符串参数转换为对应的short基本类型。
**public static int parseInt(String s)**:将字符串参数转换为对应的int基本类型。
**public static long parseLong(String s)**:将字符串参数转换为对应的long基本类型。
**public static float parseFloat(String s)**:将字符串参数转换为对应的float基本类型。
**public static double parseDouble(String s)**:将字符串参数转换为对应的double基本类型。
**public static boolean parseBoolean(String s)**:将字符串参数转换为对应的boolean基本类型。
1 |
|
注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出java.lang.NumberFormatException异常。
包装类的valueOf方法
public static Integer valueOf(String s):返回保存指定的 String 的值的 Integer 对象。
public static Double valueOf(String s):返回保存用参数字符串 s 表示的 double 值的 Double 对象。
public static Boolean valueOf(String s):返回一个用指定的字符串表示值的 Boolean 值。
public static Short valueOf(String s):返回一个保持指定 String 所给出的值的 Short 对象。
1 |
|