????简单说明:
在C语言中是没有字符串这种数据类型的,但是在Java中有字符串类型 ➡ String。
- 字符:使用单引号引起的一个字符。'a','中'
- 字符串:使用双引号引起的若干个字符。"abcde","a"
????注意:在Java中字符串没有所谓的以 '' 结尾的说法!
目录
一、创建字符串
????常见的构造 String 的方式
????引用
????方法传参
二、字符串常量池
????常量池的概念
三、字符串比较相等
????易错题
????equals方法
四、理解字符串不可变
????分析字符串拼接
????修改字符串的方法
五、字符,字节与字符串
????字符与字符串
????字节与字符串
六、字符串常见操作
????字符串比较
????字符串查找
????字符串替换
????字符串拆分
????字符串截取
????其他操作方法
七、StringBuffer 和 StringBuilder
????StringBuilder 的使用
????普通的String拼接,底层会被优化为StringBuilder
????StringBuffer 和 StringBuilder的区别
????String 与 StringBuffer 或 StringBuilder 之间的转换
????StringBuffer类中部分方法的使用
八、字符串的常见问题
一、创建字符串
✨String 类是被final修饰的,所以它不可以被继承。
????常见的构造 String 的方式
1
2
3
4
5
6
7
8
9
10
11public class TestDemo { public static void main(String[] args) { // 方式一:直接赋值 String str = "Hello Java"; // 方式二:调用构造方法进行构造对象 String str2 = new String("Hello Java"); // 方式三:使用数组 char[] array = {'a', 'b', 'c'}; String str3 = new String(array); } }
官方文档可以看到 String 还支持很多其他的构造方式, 用到的时候去查就可以了。
????注意事项:
- "hello" 这样的字符串字面值常量, 类型也是 String.
- String 也是引用类型。
1String str = "Hello";
代码内存布局
????引用
- 在介绍数组的文章中就提到了引用的概念.
- 引用类似于 C 语言中的指针, 只是在栈上开辟了一小块内存空间保存一个地址. 但是引用和指针又不太相同, 指针能进行各种数字运算(指针+1)之类的, 但是引用不能, 这是一种 "没那么灵活" 的指针.
- 也可以把引用想象成一个标签, "贴" 到一个对象上. 一个对象可以贴一个标签, 也可以贴多个。如果一个对象上面一个标签都没有, 那么这个对象就会被 JVM 当做垃圾对象回收掉.
- Java 中数组, String, 以及自定义的类都是引用类型
String 是引用类型, 因此对于以下代码
1
2String str1 = "Hello"; String str2 = str1;
内存布局
如果修改 str1 , str2 会不会随之变化呢?
事实上, str1 = "world" 这样的代码并不算 "修改" 字符串, 而是让 str1 这个引用指向了一个新的 String 对象。
内存布局
补充:
- 虽然str1与str2指向了同一个字符串"Hello",但是不可以通过str1修改字符串"Hello"的值,因为双引号引起来的是字面常量,常量是不可以被修改的。
- 上面的代码是修改的是str1的指向。
????方法传参
✨传引用不一定可以改变实参的值,要看这个引用具体做了什么。
1
2
3
4
5
6
7
8
9
10
11
12
13public class TestDemo1 { public static void func(String s,char[] array){ s = "abcdef"; array[0] = 'P'; } public static void main(String[] args) { String str = "Hello"; char[] chars = {'W','o','r','l','d'}; func(str,chars); System.out.println(str); System.out.println(Arrays.toString(chars)); } }
内存布局
运行结果
二、字符串常量池
????常量池的概念
????Class文件常量池:
- 程序编译后生成字节码文件,在字节码文件中会有一个常量池(Class文件常量池)。
- 例如:int a = 10; 在编译期间,10就放在Class文件常量池。
- Class文件常量池存放在磁盘上的。
????运行时常量池:
- 当程序把编译好的字节码文件加载到JVM当中后,会生成一个运行是常量池,这个常量池存放在方法区。
- 运行时常量池实际上是Class文件常量池被加载后放到了JVM中。
????字符串常量池:
- 主要存放字符串常量。
- 字符串常量池本质上是一个哈希表(StringTable)。
- 双引号引起来的内容会放到字符串常量池中。
- 从JDK1.8开始,将字符串常量池存放在堆里面。
????理解 "池" (pool)
- "池" 是编程中的一种常见的, 重要的提升效率的方式,。"池" 有很多,例如: "内存池", "线程池", "数据库连接池"。
- 以字符串常量池举例:需要使用的字符串在常量池中,用的时候直接在常量池里拿,常量池里面没有需要的字符串就去创建,下一次使用的时候就不用再创建了,直接在常量池里拿。
????哈希表:
- 是一种数据结构,描述和组织数据的一种方法。
- 存储数据的时候,会根据一个映射关系进行存储,至于如何映射,需要设计一个函数(哈希函数)。
这里只是简单的描述一下哈希表。有关哈希表的具体内容会在后面的数据结构中详细介绍。
因为双引号引起来的内容会存放到字符串常量池中,字符串常量池本质上是一个哈希表,所以字符串会挂到哈希表的结点上。
三、字符串比较相等
????易错题
???? 易错 1:
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "hello"; String str2 = new String("hello"); System.out.println(str1==str2); } } //运行结果:false
内存布局
✨代码分析:
最终str1产生了一个"hello"字符串对象
str2产生了两个"hello"字符串对象:
- 常量池中的"hello"对象赋给了str2
- str2 new 的一个"hello"对象
???? 易错 2:
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "hello"; String str2 = "hello"; System.out.println(str1==str2); } } //运行结果:true
内存布局
???? 易错 3:
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "hello"; String str2 = "he"+"llo"; //此时,两个都是常量,在编译的时候就已经确定是"hello" System.out.println(str1==str2); } } //运行结果:true
???? 易错 4:
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str1 = "hello"; String str2 = "he"; String str3 = str2 + "llo"; //str3 是一个变量,在编译的时候并不确定里面是什么 System.out.println(str1 == str3); } } //运行结果:false
内存布局
???? 易错 5:
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "11"; String str2 = new String("1")+new String("1"); System.out.println(str1==str2); } } //运行结果:false
内存布局
???? 易错 6:
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str2 = new String("1")+new String("1"); str2.intern();//手动入池 String str1 = "11"; System.out.println(str1==str2); } } //运行结果:true
内存布局
???? 易错 7:针对上面的代码换一个顺序
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str1 = "11"; str1.intern();//手动入池 -》当字符串常量池中没有该对象的时候,就会入池。如果有就不会入池。 String str2 = new String("1")+new String("1"); System.out.println(str1==str2); } } //运行结果:false
✨代码分析:
- 将str1的"11"字符串先入池,定义str2最终拼接出字符串"11",字符串常量池中有"11"对象,所以不会将str2指向的对象入池。
- str1 与 str2 指向不同的地址,所以打印出来的结果是false。
- 这个代码的内存布局图与第(5)个一样。
⭐String 使用 == 比较并不是在比较字符串内容, 而是比较两个引用是否是指向同一个对象
????Java 中要想比较字符串的内容, 必须采用String类提供的 equals 方法
????equals方法
????Java底层中的 equals 方法的分析
Object.java
String.java
- Java底层中的 equals 方法在 Object 类中比较两个引用是否相等(是否指向同一个对象)。
- 在String类中重写了 equals 方法,重写 equals 方法后,先比较两个字符串长度是否相等,如果不相等就返回false,如果相等再比较两个数组中的每一个字符 ,如果字符都相等就返回 true,只要有一个字符不相等就返回 false。
???? equals 方法的使用(1):
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "hello"; String str2 = new String("hello"); System.out.println(str1.equals(str2)); } } //运行结果:true
???? equals 方法的使用(2):
1
2
3
4
5
6
7public class TestDemo1 { public static void main(String[] args) { String str1 = "hello"; //"Hello"这样的字面值常量, 本质上也是一个 String 对象, 完全可以使用 equals 等 String 对象的方法 System.out.println("hello".equals(str1)); } }
???? equals 方法的使用(3):
1
2
3
4
5
6
7public class TestDemo1 { public static void main(String[] args) { String str1 = null; //str1这个引用不指向任何对象 String str2 = " "; //str2这个引用指向的字符串是空 //这两个对象不可能相等 } }
????注意:任何一个引用在调用方法时都要预防空指针异常。
✨如果将str1放到括号里面就不会出现空指针异常,因为调用方法的 str2 不是空指针。
⭐底层的 equals 方法中一开始是判断两个引用是否是相等,如果相等就返回true。
四、理解字符串不可变
- 字符串是一种不可变对象. 它的内容不可改变.
- String 类的内部实现也是基于 char[] 来实现的, 但是 String 类并没有提供 set 方法之类的来修改内部的字符数组。
????分析字符串拼接
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str = "hello" ; str = str + " world" ; str += "!!!" ; System.out.println(str); } } //运行结果:hello world!!!
形如 += 这样的操作, 表面上好像是修改了字符串, 其实不是。这段代码中共创建了5次对象:
- "hello"
- "world"
- "hello world"
- "!!!"
- "hello world!!!"
+= 之后 str 打印的结果却是变了, 但不是 String 对象本身发生改变, 而是 str 引用到了其他的对象。
????修改字符串的方法
- 例如, 现有字符串 str = "Hello" , 想改成 str = "hello"
????常见办法: 借助原字符串, 创建新的字符串
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str = "Hello"; str = "h" + str.substring(1); System.out.println(str); } } //运行结果:hello
- 关于substring()方法的使用在文章的后面会介绍
????特殊办法:使用 "反射" 操作可以破坏封装, 访问一个类内部的 private 成员。
- 通过前面的内容可以知道,字符串是将字符保存在 value 数组中的。通过"反射"更改value数组中的字符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import java.lang.reflect.Field; public class TestDemo1 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { String str = "Hello"; // 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的. Field valueField = String.class.getDeclaredField("value"); // 将这个字段的访问属性设为 true valueField.setAccessible(true); // 把 str 中的 value 属性获取到. char[] value = (char[]) valueField.get(str); // 修改 value 的值 value[0] = 'h'; System.out.println(str); } } //运行结果:hello
关于反射
- 反射是面向对象编程的一种重要特性, 有些编程语言也称为 "自省".
- 指的是程序运行过程中, 获取/修改某个对象的详细信息(类型信息, 属性信息等), 相当于让一个对象更好的 "认清自己"
- 这里只是简单的用了一下反射,关于反射的具体内容会在后面的文章中详细介绍。
五、字符,字节与字符串
????字符与字符串
- 字符串内部包含一个字符数组,String 可以和 char[] 相互转换。
????将字符数组中的所有内容变成字符串
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { char ch[] = {'a','b','c','d','e'}; String str = new String(ch); System.out.println(str); } } //运行结果:abcde
????将部分字符数组中的内容变为字符串
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { char ch[] = {'a','b','c','d','e'}; String str = new String(ch,1,3); //从下标1开始,将后面的3个字符变成字符串 System.out.println(str); } } //运行结果:bcd
????获取指定索引(下标)位置的字符,索引从0开始
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str = "hello"; char ch = str.charAt(2);//获取2下标的字符 System.out.println(ch); } } //运行结果:l
????将字符串变成字符数组
1
2
3
4
5
6
7
8
9
10
11import java.util.Arrays; public class TestDemo1 { public static void main(String[] args) { String str = "hello"; char chars[] = str.toCharArray(); //将str指向的字符串对象,变成字符数组 System.out.println(Arrays.toString(chars)); } } //运行结果:[h, e, l, l, o]
练习
- 给定字符串一个字符串, 判断其是否全部由数字所组成。
????方法一:使用 charAt() 方法获取指定位置的字符,然后使用ASCII码值进行判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class TestDemo1 { public static boolean isNumberChar(String s){ for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if(ch<'0' || ch>'9'){ return false; } } return true; } public static void main(String[] args) { String str = "123456"; System.out.println(isNumberChar(str)); } } //运行结果:true
????方法二:使用 charAt()方法获取指定位置的字符,然后使用 Character 类中的 isDigit() 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class TestDemo1 { public static boolean isNumberChar(String s){ for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); //判断某个字符是不是数字(是返回 true,不是返回 false) boolean flag = Character.isDigit(ch); if(flag==false){ return false; } } return true; } public static void main(String[] args) { String str = "123456"; System.out.println(isNumberChar(str)); } } //运行结果:true
????字节与字符串
- 字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换。
????将字节数组变为字符串
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { byte[] bytes = {97,98,99,100}; String str = new String(bytes); System.out.println(str); } } //abcd
????将部分字节数组中的内容变为字符串
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { byte[] bytes = {97,98,99,100}; String str = new String(bytes,1,2); System.out.println(str); } } //运行结果:bc
????将字符串以字节数组的形式返回
1
2
3
4
5
6
7
8
9
10
11import java.util.Arrays; public class TestDemo1 { public static void main(String[] args) { String str = "abcd"; byte[] bytes = str.getBytes(); System.out.println(Arrays.toString(bytes)); } } //运行结果:[97, 98, 99, 100]
????编码转换处理
1
2
3
4
5
6
7
8
9
10
11
12import java.io.UnsupportedEncodingException; import java.util.Arrays; public class TestDemo1 { public static void main(String[] args) throws UnsupportedEncodingException { String str = "语言"; byte[] bytes = str.getBytes("utf-8"); System.out.println(Arrays.toString(bytes)); byte[] bytes2 = str.getBytes("GBK"); System.out.println(Arrays.toString(bytes2)); } }
运行结果
- 除数字和英文字母以外,不同编码格式转换后的字节数组不同。
????何时使用 byte[], 何时使用 char[]?
- byte[] 是将 String 按照一个字节一个字节的方式处理, 这种适合在网络传输, 数据存储这样的场景下使用. 更适合针对二进制数据来操作.
- char[] 是将 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候。
六、字符串常见操作
????字符串比较
- 前面简单的介绍过String类提供的 equals()方法,该方法本身是可以进行区分大小写的相等判断。除了这个方法之外,String类还提供了一些比较操作。
????区分大小写的比较
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "Hello"; String str2 = "hello"; System.out.println(str1.equals(str2)); } } //运行结果:false
????不区分大小写的比较
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str1 = "Hello"; String str2 = "hello"; System.out.println(str1.equalsIgnoreCase(str2)); } } //运行结果:true
????比较两个字符串的大小关系,使用 compareTo() 方法
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str1 = "abc"; String str2 = "Abc"; int ret = str1.compareTo(str2); System.out.println(ret); } } //运行结果:32
在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内容:
- 相等:返回0
- 小于:返回内容小于0
- 大于:返回内容大于0
????字符串查找
- 从一个完整的字符串之中可以判断指定内容是否存在,查找方式如下:
????判断子字符串是否存在主字符串中
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "abc"; boolean flag = str.contains(tmp); System.out.println(flag); } } //运行结果:true
- 该判断形式是从JDK1.5之后开始追加的,在JDK1.5以前要想实现与之类似的功能,就必须借助 indexOf() 方法完成。
????从头开始查找指定字符串的位置,找到了然后位置的开始索引,找不到返回-1
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "abc"; //返回主串中第一次出现子串的起始位置 int index = str.indexOf(tmp); System.out.println(index); } } //运行结果:2
- 使用indexOf():如果内容重复,它只能返回查找的第一个位置
????从指定位置开始查找子串的起始位置
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "abc"; //从下标为3的位置开始查找子串的起始位置 int index = str.indexOf(tmp,3); System.out.println(index); } } //运行结果:5
????从后向前查找字符的起始位置
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "abc"; //从后向前查找子串的起始位置 int index = str.lastIndexOf(tmp); System.out.println(index); } } //运行结果:5
????从指定位置开始,从后向前查找子串的起始位置
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "abc"; //从下标为4的位置开始从后向前查找子串的起始位置 int index = str.lastIndexOf(tmp,4); System.out.println(index); } } //运行结果:2
????判断一个字符串是否以指定字符串开头
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "ac"; //判断str指向的字符串是否以 "ac" 字符串开头 boolean flag = str.startsWith(tmp); System.out.println(flag); } } //运行结果:false
????从指定位置开始,判断一个字符串是否以指定字符串开头
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "c"; //从下标为4的位置开始,判断str指向的字符串是否以 "c" 字符串开头 boolean flag = str.startsWith(tmp,4); System.out.println(flag); } } //运行结果:true
????判断一个字符串是否以指定字符串结尾
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; String tmp = "cd"; //判断str指向的字符串是否以 "cd" 字符串结尾 boolean flag = str.endsWith(tmp); System.out.println(flag); } } //运行结果:true
????字符串替换
- 使用一个指定的新的字符串替换掉已有的字符串数据。
????替换所有的指定内容
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; //将字符串中的"ab"替换为字符串"vn" String s = str.replaceAll("ab","vn"); System.out.println(s); } } //运行结果:vnvncvncd
????替换第一个内容
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str = "ababcabcd"; //将字符串中的第一个"ab"替换为字符串"vn" String s = str.replaceFirst("ab","vn"); System.out.println(s); } } //运行结果:vnabcabcd
????注意事项: 由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串
????字符串拆分
- 可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串
????将字符串全部拆分(多次拆分)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class TestDemo1 { public static void main(String[] args) { String str = "name=zhangsan&age=20"; //先按 "&" 进行分割 String[] strings = str.split("&"); for (String ret : strings) { System.out.println(ret); //再按 "=" 进行分割 String[] ss = ret.split("="); for (String tmp : ss) { System.out.println(tmp); } } } } //运行结果: name=zhangsan name zhangsan age=20 age 20
????将字符串部分拆分
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class TestDemo1 { public static void main(String[] args) { String str = "hello world hello java"; //将字符串以空格" " 部分拆分成两部分,不会均匀拆分 String[] ret = str.split(" ",2); for (String s:ret) { System.out.println(s); } } } //运行结果: hello world hello java
????有些特殊字符作为分割符可能无法正确切分, 需要加上转义
????注意事项:
- 字符"|","*","+"都得加上转义字符,前面加上"".
- 而如果是"",那么就得写成"\\".
- 如果一个字符串中有多个分隔符,可以用"|"作为连字符.
????代码示例: 拆分IP地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class TestDemo1 { public static void main(String[] args) { String str = "192.168.1.1"; //因为'.'是一个特殊字符,要使用转义字符'' //又因为''也是一个特殊字符,要使用"\"表示'',然后通过''对'.'进行转义 String[] strings = str.split("\."); for (String s:strings) { System.out.println(s); } } } //运行结果: 192 168 1 1
????代码示例: 以 '' 字符进行拆分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class TestDemo1 { public static void main(String[] args) { //使用"\"表示'' String str = "192\168\1\1"; //以 '' 进行拆分,需要"\\" //因为''是一个特殊字符,要使用"\"表示'',''又会对后面的字符进行转义,所以要使用4个 String[] strings = str.split("\\"); for (String s:strings) { System.out.println(s); } } } //运行结果: 192 168 1 1
????代码示例: 符串中有多个分隔符,用 "|" 作为连字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class TestDemo1 { public static void main(String[] args) { String str = "Java30 12&21#hello"; String[] strings = str.split(" |&|#"); for (String s:strings) { System.out.println(s); } } } //运行结果: Java30 12 21 hello
????字符串截取
- 从一个完整的字符串之中截取出部分内容
????注意事项:
- 索引从0开始,返回原字符串
- 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
????从字符串指定索引位置截取到结尾
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str = "abcdefg"; //从下标为2的位置开始截取字符串 String s = str.substring(2); System.out.println(s); } } //运行结果:cdefg
????截取部分内容(左闭右开)
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { String str = "abcdefg"; //从下标为2的位置开始截取字符串到下标为3的位置 [2,4) String s = str.substring(2,4); System.out.println(s); } } //运行结果:cd
????其他操作方法
????去除字符串中的左右空格,保留中间的空格
1
2
3
4
5
6
7
8
9
10
11
12public class TestDemo1 { public static void main(String[] args) { String str = " hello world " ; System.out.println("["+str+"]"); System.out.println("["+str.trim()+"]"); } } //运行结果: [ hello world ] [hello world]
- trim()方法会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)
????字符串转大写
1
2
3
4
5
6
7
8
9
10
11public class TestDemo1 { public static void main(String[] args) { String str = "abcdeFYKL"; String ret = str.toUpperCase(); System.out.println(ret); } } //运行结果:ABCDEFYKL
????字符串转小写
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str = "abcdeFYKL"; String ret = str.toLowerCase(); System.out.println(ret); } } //运行结果:abcdefykl
????字符串入池操作
- 使用 intern() 方法,此方法在前面已经介绍了。
????字符串连接,连接后的字符串不会放到常量池中。
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str = "hello"; String ret = str.concat("world"); System.out.println(ret); } } //运行结果:helloworld
????获取字符串长度
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { String str = "abcdef"; int len = str.length(); System.out.println(len); } } //运行结果:6
- 注意:数组长度使用数组名称.length属性,而String中使用的是length()方法
????判断字符串是否为空。(字符串长度是否为0,不是null)
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class TestDemo1 { public static void main(String[] args) { String str1 = ""; //长度为0,所以该字符串为空 String str2 = " ";//空格,长度不为0,所以该字符串不为空 boolean flag1 = str1.isEmpty(); boolean flag2 = str2.isEmpty(); System.out.println(flag1); System.out.println(flag2); } } //运行结果: true false
????练习:将字符串的首字母变成大写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class TestDemo1 { public static String fistUpper(String s){ if("".equals(s) || s==null){ return s; } if(s.length()>1){ return s.substring(0,1).toUpperCase()+s.substring(1); } return s.toUpperCase(); } public static void main(String[] args) { String str = "hello"; String ret = fistUpper(str); System.out.println(ret); } } //运行结果:Hello
七、StringBuffer 和 StringBuilder
????回顾String类的特点:
- 任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。
- 通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和StringBuilder类。
- StringBuffer 和 StringBuilder 大部分功能是相同的,这里主要介绍 StringBuilder
- 在String中使用"+"来进行字符串连接,但是这个操作在StringBuffer类中需要更改为append()方法
????StringBuilder 的使用
????使用构造方法赋值
1
2
3
4
5
6
7
8
9
10
11
12public class TestDemo1 { public static void main(String[] args) { StringBuilder sb = new StringBuilder("abcde"); //StringBuilder 类中有一个 toString()方法,直接传引用会默认调用toString()方法打印 System.out.println(sb); //默认调用toString()方法 System.out.println(sb.toString()); //直接调用toString()方法 } } //运行结果 abcde abcde
????使用append()方法赋值
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); sb.append("abcde"); System.out.println(sb); } } 运行结果:abcde
1
2
3
4
5
6
7
8
9
10public class TestDemo1 { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); sb.append("abced"); sb.append("1234"); System.out.println(sb); } } //运行结果:abced1234
✨append()方法可以连用
1
2
3
4
5
6
7
8public class TestDemo1 { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); sb.append("abced").append("1234"); System.out.println(sb); } } //abced1234
????普通的String拼接,底层会被优化为StringBuilder
1
2
3
4
5
6
7
8
9public class TestDemo2 { public static void main(String[] args) { String str = "hello"; str +="abc"; System.out.println(str); } } //运行结果:helloabc
如果在循环里面进行字符串拼接,尽量不要使用String,优先使用StringBuffer 和 StringBuilder。
????StringBuffer 和 StringBuilder的区别
- 可以看到StringBuffer类中的方法都被 synchronized 关键字修饰,而StringBuilder类中的方法并没有。
- synchronized关键字可以保证线程安全。
- StringBuffer 一般用于多线程,StringBuilder 一般用于单线程
????String 与 StringBuffer 或 StringBuilder 之间的转换
✨String和StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:
⭐String变为StringBuffer:利用StringBuffer的构造方法或append()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/** * String -> StringBuffer 或 StringBuilder * 使用构造方法 * @return */ public static StringBuffer func1(){ String str = "abcde"; StringBuffer sb = new StringBuffer(str); return sb; } /** * String -> StringBuffer 或 StringBuilder * 使用append()方法 * @return */ public static StringBuffer func2(){ String str = "abcde"; StringBuffer sb = new StringBuffer(); sb.append(str); return sb; }
⭐StringBuffer变为String:调用toString()方法
1
2
3
4
5
6
7
8
9/** * StringBuffer 或 StringBuilder -> String * 调用 toString() 方法 * @return */ public static String func3(){ StringBuffer sb = new StringBuffer("abcde"); return sb.toString(); }
????StringBuffer类中部分方法的使用
????字符串反转
1public synchronized StringBuffer reverse()
代码演示:
1
2
3
4
5
6
7
8public class TestDemo1 { public static void main(String[] args) { StringBuffer sb = new StringBuffer("abcdef"); System.out.println(sb.reverse()); } } //运行结果:fedcba
????删除指定范围的数据
1public synchronized StringBuffer delete(int start, int end)
代码演示:
1
2
3
4
5
6
7
8
9public class TestDemo1 { public static void main(String[] args) { StringBuffer sb = new StringBuffer("hello world"); System.out.println(sb.delete(5, 10)); } } //运行结果:hellod
????插入数据
1public synchronized StringBuffer insert(int offset, 各种数据类型 b)
代码演示:
1
2
3
4
5
6
7
8public class TestDemo1 { public static void main(String[] args) { StringBuffer sb = new StringBuffer("helloworld"); System.out.println(sb.delete(5, 10).insert(0, "你好")); } } //运行结果:你好hello
八、字符串的常见问题
1、解释String类中两种对象实例化的区别?
- 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
- 构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern()方法手工入池。
2、为什么 String 要不可变?(不可变对象的好处是什么?)
- 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题。
- 不可变对象可以使线程是安全的。
- 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。
3、解释String、StringBuffer、StringBuilder的区别
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用 StingBuffer 或 StringBuilder。
- StringBuffer与StringBuilder大部分功能是相似的。
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作。
最后
以上就是优美帽子最近收集整理的关于【JavaSE】----- 认识 String 类目录一、创建字符串二、字符串常量池三、字符串比较相等四、理解字符串不可变五、字符,字节与字符串六、字符串常见操作七、StringBuffer 和 StringBuilder八、字符串的常见问题的全部内容,更多相关【JavaSE】-----内容请搜索靠谱客的其他文章。
发表评论 取消回复