JAVA String类

目录

String基本概念

字符串的不可变性

字符串的常量池

StringBuilder、StringBuffer

常用方法

0.求字符串长度,length

1. 字符串转数组,toCharArray

2.字符串比较,equals

3.字符串切割,split

4.替换指定内容,replace

5.查找子串 indexOf


String基本概念

字符串的不可变性

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

来看它的源码:

可以看到,我们平时 String str = "abc",其中的"abc"都是存储于String内的数组中的,但是String并不是使用它完成“不可变”这一特点的,String的不可变是因为没有对外提供改变内部数组的方法,并且由于String是被final修饰的类,无法被继承/实现,那么也就无法通过子类去修改它。

所以它的不可变特点是如何完成的?

答案是:内部字符串数组不可达

我们对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,但是在堆区地址不同,故不相同

 插一嘴,其实使用Scanner输入的String字符串是直接在堆区new哦。

StringBuilder、StringBuffer

刚才一直说什么“这个过程运用到了new StringBuilder”,现在就开始讲讲具体过程。

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

接下来就是StringBuilder类和StringBuffer类。

刚才一直说,

String str3 = str1 + str2; 

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

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

如果你没有XJad或者你不会使用,可以先阅读这篇博客,两种方式教你查看反编译代码:

JAVA查看反汇编代码

代码:

String str1 = "abc";
String str2 = "def";
String str3 = "abc" + "def";
String str4 = srt1 + str2;

运行后将.class文件放入XJad中后显示:

可以看到str3创建时,直接将"abc"和"def"从常量池中取出连接起来。

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

再看看我们平时打印一个数是怎么打印的,是System.out.println(),然后在里面放一个数1、2.0、true、、、然后直接输出他们呢?不,其实也是借助String来完成的。

来看看关于System.out.println的源代码:

以输出boolean和char来举例 (忽略synchronized,这是线程方面的,不影响阅读)

 先是调用print方法,再换行,那我们再来看看print方法

 看到了吗?其实不管你用print输出的是什么,它都会先给你转成字符串再打印到控制台

那这又与StringBuilder有什么关系呢?

我们用XJad查看以下代码的反编译文件

String name = "小明";
int age = 18;
System.out.println("我叫" + name + ", 我今年" + age + "岁了");

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

你看,只要里面有字符串拼接,编译器都会帮你,先创建一个StringBuilder 类,由于它是可变的,可以使用append()方法将其他int、double、boolean...连接起来,最后,再调用toString()方法转换成String类。即:print方法内部只能有String,有其他的也会转换成String。

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

StringBuilder

线程不安全,效率高

StringBuffer

线程安全,效率低

什么叫“线程安全”呢?就是StringBuffer类的操作是原子级别的,不存在这个线程正在执行StringBuffer的操作呢,另外一个线程直接插入进来了。但是为了实现线程安全,就付出了效率的代价。

关于线程,简单的提一嘴就结束了(++操作不是原子级别哦~挺好玩的)

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

常用方法

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方法进行升序排列

查看源码:

 发现他其实是根据String底层的字符数组使用System.arraycopy()方法拷贝一份给你。

2.字符串比较,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、一个字符串在另一个字符串中出现的位置

  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值