0


JAVA String类

String基本概念

字符串的不可变性

众所周知,String的特点是不可变,所以为什么是不可变呢?怎么实现的不可变呢?

来看它的源码:

可以看到,我们平时 String str = "abc",其中的"abc"都是存储于String内的数组中的,并且这个数组是被final修饰的,所以String类就能借助这个数组实现“不可变”这个特点。

我们对str操作时,总会感觉改变了str的值,其实你只是把str换了个指向罢了

str1 = "abc";
str1 = "def";
你只是把str1从指向"abc"变成了指向"def",并没有改变str1的值哦

字符串的常量池

同时,你会不会为搞不懂常量池、不知道怎么判断两个字符串是否相等而发愁?如下:

String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
str1 == str2
str1 != str3

由于String类型实在是太常用了,为了节约空间,JAVA类库的设计者在实现时增添了一个东西,名为常量池。

(以前常量池在方法区,现在在堆区) 每生成一个字符串,该字符串都将在常量池"登记",当要第二次使用时,就共享它,而不是再创建一个新的同样的字符串。

当你直接将str1指向字符串"abc"时,会先在常量池中寻找这个字符串,如果找到了,直接将str1指向"abc",如果没找到,会在常量池中创建一个"abc",并将str1指向它。看一下代码执行过程:

String str1 = "abc"; 这一步执行时,str1没找到"abc",所以它会在常量池创建"abc",并将str1指向"abc"。

String str2 = "abc"; 这一步执行时,因为str1已经组建了"abc",所以str2直接指向"abc"。

接下来看看String str3 = new String("abc");

它并不是直接将str3指向"abc",而是在堆区的其他地方new 一个String类大小的空间,开头说过,String内部其实是有一个数组存放字符的,此时这个数组就会指向"abc"。注意,并不是str3指向"abc"哦,是str3内部的数组。

在了解字符串的不可变性和常量池之后,你是不是对String的理解更为深入了呢?来看看这几道题吧。

1、在执行以下操作后 , Str1 = ______;

String str1 = "hello";
String str2 = str1;
str2 = "abc";

答案 :hello

解析 :只是改变了str2的指向,而不是它的内容

2、一大波题目正在来临

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = "H";
String s7 = "ello";
String s8 = s6 + s7;

1、System.out.println(s1 == s2);  // true
2、System.out.println(s1 == s3);  // true
3、System.out.println(s1 == s4);  // false
4、System.out.println(s1 == s8);  // false
5、System.out.println(s4 == s5);  // false

1、s1 == s2 :上面已经讲过,都直接指向常量池,相等

2、s1 == s3 :相等,如果右侧都是常量,常量池中直接连接这两个字符串;如果右侧出现变量,本质是new了一个StringBuilder然后一个一个连接,

3、s1 == s4 :s4可以看作右侧是一个常量+一个变量,故本质是new Stringbuilder(),不相等

4、s1 == s8 :s8是两个变量,本质是new Stringbuilder(),故 s1 != s8。

5、s4 == s5 :虽然都是new 出的String,但是在堆区地址不同,故不相同

常用方法

0.求字符串长度,length

String a = "shsiwie";
int n = a.length();

1. 字符串转数组,toCharArray

将String字符串转换为字符数组。

String 类的内容和长度是固定的(final),要对其操作就要借助一些方法,虽然这些方法可以对字符串的内容进行操作,但这不改变对象实例,而是生成了一个新的实例。

有些题给了字符数组,没给字符串,而字符串是不能单个操作的。就要把它转换成字符数组进行操作。

如:给已知字符串排序

String str = "sojssj";
char[] a = str.toCharArray();
Arrays.sort(a);

先将str字符串转换成字符数组,调用Arrays.sort方法进行升序排列

2.字符串比较,equals

==的介绍

C++中可以用 == 进行字符串内容之间的比较,而用于java时,== 进行的是地址的比较。

String str1 = "abc";

这样的初始化方法是:先在常量池中寻找 "abc" 这样的字符串,若找到,直接将 str1 指向它,若没找到,会创建一个再将str1指向它

String str1 = "abc";
String str2 = "abc";
String str3 =  new String("abc");
true : str1 == str2  
false : str1 == str3

String a = new String("123");
String b = new String("123");
false : a == b

str1和str2都是对方法区常量池中"abc"的引用,地址相同,故str1 == str2。

str3没有用到常量池,它是堆区内容,与str1 ,str2地址不同,故str1 != str2。

a 和 b 虽然都在堆区,但申请空间地址不同

equals方法

故想要比较字符串内容时,用到equals方法。

String a = "abc";
String b = new String("abc");
boolean isSame;
isSame = a.equals(b);
//isSame == true

当遇到 常量与变量进行比较时,建议把常量放在前面。

boolean isSame;
String a = "abc";
isSame = "abc".equals(a);
//isSame = true;

原因:

因为如果把变量写在前面

a.equals("abc") 万一 str 是 NULL ,程序会报错,空指针异常 NullPointerExcption

忽略大小写比较:equalsIgnoreCase()

a.equalsIgnoreCase(b);

3.字符串切割,split

类似C语言中的strtok,比它牛逼

strtok切割后返回的是地址,而split切割后返回的是已经切割好的字符串数组,故需要 String[] 接收

String a = "aa,bb,cc";
String[] b = a.split(",");
//遍历打印b : aabbcc
for(int i = 0; i < b.length; i++) {
    System.out.print(b[i]);
}

注意: split 方法中的参数是正则表达式,如果想切割字符串中的".",会切割失败

如:

String a = "c.c.c";
String[] b = a.split(".");

能运行但是没有输出(因为切割失败,b数组的长度为0)。

想要切割".",就要写成"\."的形式

String a = "abc.def.gh";
String[] b = a.split("\\.");

切割成功,b数组的长度为3

4.替换指定内容,replace

将字符串的所有符合要求的内容替换为指定内容。返回值为字符串

String a = "aaabaaabaaa";
String b = a.replace("b","a");
//b : aaaaaaaaaaa
String c = "你他妈的,你他妈的";
String d = c.replace("你他妈的","***");
// d : ***,***

5.查找子串 indexOf

int a.indexOf(b) 返回b在a中第一次出现的位置

String a = "abcde";
String b = "bc";
System.out.println(a.indexOf(b)); // 1
返回值为字串第一次出现的位置
找不到则返回-1

来做几道关于String常用方法的练习题:

1、字符数组创建字符串

2、反转字符串

3、一个字符串在另一个字符串中出现的位置

StringBuilder、StringBuffer

String类是不可变的,但是StringBuilder和StringBuffer是可变的。

接下来就是StringBuilder类和StringBuffer类。

刚才一直说

String str3 = str1 + str2; 

这个过程其实是StringBuilder来完成的,那么它到底是怎么实现的呢?

我们通过反编译软件XJad来完成这件事:

XJad下载地址:

链接: XJad下载

提取码: 5knf

在把.class文件放入XJad中后显示:

可以看到str4 的创建过程很复杂,它先是new 了一个StringBuilder类,又将str1和str2拼接上,最后再转为String类赋值给str4。这就是字符串拼接的本质。

那么我们就能理解平时打印的时候,

System.out.println("i = " + i);

其实它的过程比你想象的更加复杂哦:

先创建一个StringBuildder对象,连接上"i = ",再连接上i这个数字,再将StringBuilder转换成Strin类输出。

当然,如果你不想下载XJad的话,可以在IDEA查看反汇编代码哦~由于只是拓展,我只贴两张图:

接下来说说StringBuilder和StringBuffer的区别:

StringBuilder

线程不安全,效率高

StringBuffer

线程安全,效率低

由于本篇博客只是浅浅涉猎javase的String类,所以要想知道更多关于StringBuilder和StringBuffer的内容,可以参考这位大佬的博客:StringBuilder和StringBuffer的区别

标签: java

本文转载自: https://blog.csdn.net/qq_62939743/article/details/124317952
版权归原作者 小何┌ 所有, 如有侵权,请联系我们删除。

“JAVA String类”的评论:

还没有评论