Java基础学习(八):字符串
目录本文为个人学习记录,内容学习自 黑马程序员
概述
java.lang
包是 Java 的核心包,其下的类都是无需手动导入即可使用的java.lang.String
是字符串类,Java 程序中所有字符串都为此类的对象
字符串的创建
创建 String 对象有两种方式:
-
直接赋值:
String name = "victoria";
-
使用 new 创建:
构造方法 说明 public String() 创建空白字符串,不含任何内容 public String(String original) 根据传入的字符串,创建字符串对象 public String(char[] chs) 根据字符数组,创建字符串对象 public String(byte[] chs) 根据字节数组,查ASCII码表,创建字符串对象
字符串内存分析
-
存储位置:
- 直接赋值得到的字符串存储在字符串常量池(StringTable)中,在 JDK7 之前字符串常量池位于方法区中,后来移到了堆中
- 通过 new 创建的字符串存储在堆中
-
底层机制:
-
通过直接赋值创建字符串时,如果字符串常量池中没有该常量,则创建一个新常量,并将地址传给字符串变量;如果字符串常量池中已经有了该常量,则会复用该常量
String a = "abc"; // a存储的地址值为 0x0011 String b = "abc"; // b存储的地址值为 0x0011
-
使用 new 创建的字符串和其他对象的创建一样,会在堆中开辟新内存存储数据,不存在相同字符串的复用
char[] chs = {'a', 'b', 'c'}; String a = new String(chs); // a存储的地址值为 0x0011 String b = new String(chs); // b存储的地址值为 0x0022
-
-
总结:使用直接赋值不仅简单,还节约内存
String 类常用方法
-
字符串的比较
由于引用数据类型的变量存储的是地址值,因此会出现下面的比较问题:
String a = "abc"; String b = "abc"; System.out.println(a == b); // 输出为 true
String a = new String("abc"); String b = new String("abc"); System.out.println(a == b); // 输出为 false
小知识:使用 Scanner 从键盘接收的字符串也是 new 创建的
为了实现对字符串本身的比较,String 类提供了两种方法:
equals()
:完全一样结果为true,否则为falseequalsIgnoreCase()
:不区分大小写
String a = new String("abc"); String b = new String("abc"); boolean result = a.equals(b); boolean result2 = a.equalsIgnoreCase(b);
-
字符串按索引获得字符
通过调用
charAt()
方法实现String str = "abc"; char ch = str.charAt(1); System.out.println(ch); // 输出为 'b'
-
获取字符串长度
通过调用
length()
方法实现String str = "abc"; int len = str.length(); System.out.println(len); // 输出为 3
-
截取字符串
通过调用
substring()
方法实现两种实现方法:
String substring(int beginIndex, int endIndex)
:第一个参数表示起始索引,第二个参数表示结束索引(不包含)String substring(int beginIndex)
:给定起始索引,默认截取到末尾
注意:1. 包头不包尾,包左不包右,2. 需要通过返回值接收截取后的字符串,因为字符串本身是不可修改的
String str = "abc"; String subStr1 = str.substring(0, 2); String subStr2 = str.substring(1); System.out.println(subStr1); // 输出为 "ab" System.out.println(subStr2); // 输出为 "bc"
-
字符串替换
通过调用
replace()
方法实现注意:需要通过返回值接收替换后的结果
String str = "abcaaacba"; String str2 = str.replace("aaa", "bbb"); System.out.println(str2); // 输出为 "abcbbbcba"
-
字符串提取字符数组
通过调用
toCharArray()
方法实现用途:常用于修改字符串内容
String str = "abc"; char[] chs = str.toCharArray(); // 得到的chs为['a', 'b', 'c']
StringBuilder 类
-
java.lang.StringBuilder 可以看成一个容器,创建之后里面的内容是可变的
-
作用:提高字符串的操作效率
-
两种构造方式:无参和有参
构造方法 说明 public StringBuilder() 创建一个空白可变字符串对象,不含任何内容 public StringBuilder(String str) 根据字符串的内容,来创建可变字符串对象 -
常用方法:
方法名 说明 public StringBuilder append(任意类型) 添加数据,并返回对象本身 public StringBuilder reverse() 反转容器中的内容 public int length() 返回长度(字符出现的个数) public String toString() 把 StringBuilder 转换成 String -
返回值是自身 vs 无返回值
- 相同点:都不需要创建一个新的变量用于接收返回值
- 不同点:返回值是自身时可以用于链式编程
-
方法示例:
StringBuilder sb = new StringBuilder(); // append方法 sb.append(1).append('a').append("zz"); // 链式编程 System.out.println(sb); // 输出为 "1azz"(和 String 类似,Java 底层在实现打印输出时进行了特殊处理,不是输出地址值,而是属性值) // reverse方法 sb.reverse(); System.out.println(sb); // 输出为 "zza1" // length方法 int len = sb.length(); System.out.println(len); // 输出为 4 // toString方法 String str = sb.toString(); System.out.println(str); // 输出为 "zza1"
StringJoiner 类
-
java.util.StringJoiner 也可以看成一个容器,创建之后里面的内容是可变的
-
作用:提高字符串的操作效率,而且代码编写简洁,但由于在 JDK8 才出现,因此使用不广
-
两种构造方式:只有有参
构造方法 说明 public StringJoiner(String str) 创建一个StringJoiner对象,指定拼接时的间隔符号 public StringJoiner(String str, String str, String str) 创建一个StringJoiner对象,指定拼接时间隔符号、开始符号、结束符号 -
常用方法:
方法名 说明 public StringJoiner add(String str) 添加数据(只能添加字符串),并返回对象本身 public int length() 返回长度(字符出现的个数) public String toString() 把 StringJoiner 转换成 String -
方法示例:
StringJoiner sj = new StringJoiner(", ", "[", "]"); sj.add("aaa").add("bbb").add("ccc"); System.out.println(sj); // 输出为 "[aaa, bbb, ccc]" int len = sj.length(); System.out.println(len); // 输出为 15 String str = sj.toString(); System.out.println(str); // 输出为 "[aaa, bbb, ccc]"
字符串相关类底层原理
-
字符串存储的内存原理
- 直接赋值:会复用字符串常量池中的
- new创建:不会复用,而是开辟一个新的空间
-
== 号比较的到底是什么?
- 基本数据类型:比较数据值
- 引用数据类型:比较地址值
-
字符串拼接的底层原理
-
拼接时等号右边没有变量:会触发字符串的优化机制,在编译的时候就已经是最终结果了
下例中,引用变量 s 中存储的是字符串常量池中 "abc" 的地址
String s = "a" + "b" + "c"; ===> String s = "abc";
-
拼接时等号右边有变量:
-
JDK8 之前:会在堆中创建新的 StringBuilder 对象和 String 对象
下例中,引用变量 s2 中存储的是堆中新建的 String 对象的地址
String s1 = "a"; String s2 = s1 + "b"; ===> String s2 = new StringBuilder().append(s1).append("b").toString();
-
JDK8 之后:会先预估最终字符串长度,并创建一个数组存储该字符串,最后通过数组在堆中创建一个新的字符串
-
-
总结:拼接时等号右边没有变量时,拼接后的新字符串存储在字符串常量池中;等号右边有变量时,拼接后新字符串存储在堆中
-
-
StringBuilder 提高效率原理
- 将所有要拼接的内容放到 StringBuilder 中,不会创建很多无用的空间,可以节约内存
-
StringBuilder 扩容机制
- StringBuilder 中的数据存储在 byte[] 数组中
- StringBuilder 有 容量 和 长度 两个概念,容量表征了最多容纳的字符个数,长度表征已经容纳的字符个数
- 默认的容量为16,当长度超出了默认容量时,会进行一次扩容,将容量扩成34,如果还是超出容量,则容量大小以实际长度为准