【JAVA入门】Day06 - 字符串
文章目录
- 【JAVA入门】Day06 - 字符串
- 一、API
- 二、字符串
- 2.1 创建 String 对象的方式
- 2.2 字符串内存模型
- 三、字符串的相关操作
- 3.1 字符串的比较
- 3.2 遍历字符串
- 3.3 统计字符出现次数
- 3.4 拼接字符串
- 3.5 字符串反转
- 四、StringBuilder
- 3.1 构造方法
- 3.2 常用方法
- 3.3 链式编程
- 五、StringJoiner
- 六、字符串拼接的底层原理
一、API
API(Application Programming Interface)中文名称叫做应用程序编程接口。通俗理解,API 就是别人写好的东西,我们不需要自己编写,直接使用即可。
Java API 就是 JDK 中提供的各种功能类的 Java 类,这些类将底层的实现封装了起来,我们无需关注这些类如何实现,只需学习这些类如何使用即可。
二、字符串
java.lang.String 类代表了字符串,也就是说,Java 程序中所有的字符串都是此类的对象。而 java.lang 是不需要特地导入的,可以直接使用该包下的相关类。
字符串的内容是不能发生改变的,它的对象在创建后不能被更改。如果将两个字符串进行拼接等操作,只会产生新的字符串,即新对象。
String name1 = "赵四";
String name2 = "陈坤";
System.out.println(name1 + name2); // 产生了一个新的字符串
2.1 创建 String 对象的方式
- 第一种方式是以直接赋值的形式创建:
String s1 = "哥们儿我创建了一个字符串";
- 第二种方式是用 new 的方式来创建一个字符串对象:
public String(); // 创建空白字符串,不含任何内容
public String(String original); // 根据传入的字符串,创建字符串对象
public String(char[] chs); // 根据字符数组,创建字符串对象
public String(byte[] chs); // 根据字节数组,创建字符串对象
例:
String s1 = new String(); // 空串,内容为 ""
String s2 = new String("abc"); // 用一个字符串作为参数传递给 String类,再由它创建一个字符串对象
char[] chs = {'a', 'b','c','d'};
String s3 = new String(chs); // 根据字符数组中的内容,创建一个字符串"abcd"
byte[] bytes = {97, 98, 99, 100};
String s5 = new String(bytes); // 根据字节数组中的内容,创建一个字符串"abcd",这里的 abcd 是根据字节数组里的数字,从 ASCii 码表里查出来而得知的,先从 ASCii 码中找到对应的字符,再用之生成字符串
2.2 字符串内存模型
Java 中其实还存在过一块内存空间,名叫 StringTable(串池),用于专门存放字符串常量。从 JDK7 版本开始,这块空间从方法区中挪到了堆内存。
public class StringDemo {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
}
}
当使用双引号直接赋值的方式创建字符串,系统会检查该字符串在串池中是否已存在。若不存在,系统会首先把字符串存储到串池中,然后给予一个地址值;若存在了,也就是第二次赋同内容的串(即完全一致)时,系统还会从这个地址去寻找刚才的串,这就是串的复用。
如果使用 new 空间的方式创建数组,比如先创建了一个 chs 字符数组,再用这个字符数组作为参数传递给 String 类的构造函数,生成字符串对象,则如下所示。
public class StringDemo {
public static void main(String[] args) {
char[] chs = {'a', 'b', 'c'};
String s1 = new String(chs);
String s2 = new String(chs);
}
}
既然使用到了字符数组,就一定会在堆里开辟了一块空间来存储它,而生成的字符串也会存放在堆中。
每生成一个字符串,就会再开辟一块新空间来存放,不存在复用。
因此在创建字符串时,我们一般选用第一种方法,节省内存空间。
三、字符串的相关操作
3.1 字符串的比较
如果直接使用 == 号来进行比较字符串,是存在一定问题的。下面的例子可以解释。
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2); //true
String s1 = new String("abc");
String s2 = "abc";
System.out.println(s1 == s2); //false
为什么同样都是"abc",两次比较的结果不同?
这就要讲到 == 比较的原理:
如果 == 两边都是基本数据类型,那么进行比较的就是具体的值;但如果 == 两边是引用数据类型,那么进行比较的是左右两侧内容的地址值。
因此,字符串如果使用 == 比较,那么就是在比较左右两串的地址值。而,Java 中的字符串存在复用情况,因此两个直接赋值得到的字符串相等时,其地址也是同一个;而通过字符数组生成的字符串,和直接赋值得到的串明显不是同一个地址值(上面已经解释了),所以最后输出了 false。
因此,Java 中专门提供了方便字符串比较内容的函数。
- boolean equals(要比较的字符串) 完全一样结果才是true,否则为false。
- boolean equalsIgnoreCase(要比较的字符串) 忽略大小写的比较。
public class StringDemo1 {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = "abc";
boolean result1 = s1.equals(s2); // 调用equals 函数比较内容
System.out.println(s1 == s2); // false
System.out.println(result1); // true
}
}
public class StringDemo2 {
public static void main(String[] args) {
String s1 = "ABC";
String s2 = "abc";
boolean result1 = s1.equals(s2); // 调用equals 函数比较内容
boolean result2 = s1.equalsIgnoreCase(s2); // 调用 equalsIgnoreCase 函数比较内容,忽略大小写
System.out.println(result1); // false
System.out.println(result2); // true
}
}
3.2 遍历字符串
需求:从键盘录入一个字符串,使用程序实现在控制台遍历该字符串。
在字符串类中,提供了两个方便的方法:
- public char charAt(int index):根据索引返回字符。
- public int length():返回此字符串的长度。
import java.util.Scanner;
public class StringDemo1 {
public static void main(String[] args) {
//1.键盘录入字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.next();
//2.进行遍历
for(int i = 0; i < str.length(); i++) {
//i 依次索引字符串的每一个字符
char c = str.charAt(i);
System.out.println(c);
}
}
}
3.3 统计字符出现次数
需求:键盘录入一个字符串,统计该字符串中大小写字母、数字出现的次数。
import java.util.Scanner;
public class StringDemo2 {
public static void main(String[] args) {
//1.键盘录入字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.next();
//2.进行遍历,同时进行统计
int upperCount = 0;
int lowCount = 0;
int numCount = 0;
for(int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if(c >= 'a' && c <= 'z') {
lowCount++;
}
else if(c >= 'A' && c <= 'Z') {
upperCount++;
}
else if(c >= '0' && c <= '9') {
numCount++;
}
}
System.out.println("大写字母有" + upperCount + "个,小写字母有" + lowCount + "个,数字有" + numCount + "个。");
}
}
3.4 拼接字符串
需求:定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在控制台输出结果。例如:
数组为 int[] arr = {1, 2, 3};执行方法后的输出结果为:[1, 2, 3]。
public class StringDemo3 {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
String str = arrToString(arr);
System.out.println(str);
}
// 写了一个方法,把数组变成字符串
public static String arrToString(int[] arr) {
if(arr == null) {
return "";
}
if(arr.length == 0) {
return "[]";
}
String result = "[";
for(int i = 0; i < arr.length; i++) {
//i 索引 arr[i] 元素
if(i < arr.length - 1) {
result = result + arr[i] + ",";
}else{
result = result + arr[i];
}
}
result = result + "]";
}
}
3.5 字符串反转
需求:定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法,控制台输出反转字符串。
import java.util.Scanner;
public class StringDemo4 {
public static void main(String[] args) {
//1.键盘录入字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.next();
//2.调用字符串反转方法
System.out.println(reverser(str));
}
public static String reverser(String str) {
String result = "";
for(int i = str.length() - 1; i >= 0 ; i--) {
char c = str.charAt(i);
result = result + c;
}
return result;
}
}
四、StringBuilder
StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的,所以它也被叫做“可变字符串”。它的作用就是提高字符串的操作效率。
3.1 构造方法
- public StringBuilder() 创建一个空白可变字符串对象,不含有任何内容。
- public StringBuilder(String str) 根据字符串的内容,来创建可变字符串对象。
StringBuilder sb = new StringBuilder("abc");
3.2 常用方法
- public StringBuilder append(任意类型) 添加数据,并返回对象本身。添加的数据可以是任何类型,它们都会被转成字符串。
- public StringBuilder reverse() 反转容器中的内容。
- public int length() 返回长度(字符出现的个数)。
- public String toString() 通过toString() 就可以实现把 StringBuilder 转换为 String。
- System.out.println(sb) StringBuilder是 JAVA 已经写好的类,在它的底层逻辑上,通过打印方法打印出的是它的属性值而不是地址值(一般情况打印对象都是打印地址值),因此 StringBuilder 对象可以直接打印。
3.3 链式编程
我们都知道,Java 给 String 对象提供了很多实用的方法,而某些方法的返回值还是一个字符串,这就说明我们可以通过反复调用的办法简化程序,这就是链式编程。
int len = getString().substring(1).replace("A","Q").length();
【例】判断对称字符串
需求:键盘接受一个字符串,程序判断该字符串是否是对称字符串(如:123321、111),并在控制台打印是或不是。
public class StringBuilderDemo5 {
public static void main(String[] args) {
//1.键盘录入一个字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.next();
//2.反转键盘录入的字符串
String reverseStr = new StringBuilder().append(str).reverse().toString(); //链式编程,直接用类调用内部函数生成sb对象并反转转为字符串赋给新串,省去起对象名字的步骤了
//3.判断是不是等于原字符串
if(str.equals(reverseStr)){
System.out.println("是对称字符串。");
}else{
System.out.println("不是对称字符串。");
}
}
}
【例】拼接字符串
需求:定义一个方法,把 int 数组中的数据按照指定格式拼接成一个字符串返回,并在控制台输出结果。例如:数组是 int[] arr = {1, 2, 3};执行方法后输出:[1, 2, 3]。
public class StringBuilderDemo6 {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
String str = arrToString(arr);
System.out.println(str);
}
public static String arrToString(int[] arr) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0; i < arr.length;i++) {
if(i == arr.length - 1){
sb.append(arr[i]);
}else{
sb.append(arr[i]).append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
五、StringJoiner
StringJoiner 是 JAVA 新版本新提出的一种类,它的特点就是拼接字符串速度快且方便。
- 构造方法:
public StringJoiner(间隔符号) 创建一个StringJoiner对象,指定拼接时的间隔符号。
public StringJoiner(间隔符号, 开始符号, 结束符号) 创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号。
StringJoiner sj = new StringJoiner("---");
// 1---2---3
StringJoiner sj = new StringJoiner(", ","[", "]");
// [1, 2, 3]
- 成员方法:
public StringJoiner add(添加的内容) 添加数据并返回对象本身
public int length() 返回长度(字符出现的个数)
public String toString() 返回一个字符串(该字符串就是拼接之后的结果字符串)
String Joiner sj = new StringJoiner("---");
sj.add("aaa").add("bbb").add("ccc"); // 这里的参数只能填字符串,不如sb方便
System.out.println(sj); // 同 sb 一样,sj 也可以直接 sout
// aaa---bbb---ccc
int len = sj.length();
System.out.println(len); // 15
// 注意,length()函数统计的是sj容器中的字符总个数,包含间隔符号、开始符号和结束符号,当然也包含空格
六、字符串拼接的底层原理
字符串拼接有两种底层方式。其一如下:
String s = "a" + "b" + "c";
System.out.println(s);
这种拼接方式的特点是,没有变量参与加法,都是字符串,这个时候触发字符串优化机制,编译时就已经是最终结果,相当于如下写法:
String s = "abc";
System.out.println(s);
所以说s = “a”+“b”+"c"和s = "abc"本质上是一样的,二者如果同时出现,也会指向同一个地址值(复用)。
其二是有变量参与:
String s1 = "a";
String s2 = s1 + "b";
String s3 = s2 + "c";
System.out.println(s3);
这种拼接方式的特点是,系统预估字符串拼接后的长度,提前创建一段空间存放字符串(也就是产生了一个新的字符串)。但是这个预估是需要时间成本的,因此我们在拼接字符串时,一般还是使用 StringBuilder 来拼接字符串,可以节省时间。