String类

String类

String类型的基本认识

  1. String对象用于保存字符串,也就是一组字符序列

  2. 字符串常量的对象是用双引号括起的字符序列

  3. 字符串通常使用Unicode字符编码,一个字符(不管时字母还是汉字)占用两个字节

  4. 字符串String拥有很多的构造器image-20220705105223155

  5. String类时final类,不能被其他的类所继承。private final byte[] value;String类拥有value属性,并且为final性质,表示不能被做出修改。比如从TOM变成TOO。也就是一句话,当我们的str发生更改之后,其地址已经改变,原地址的内容不会发生改变。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package Learn;
    public class Test{
    public static void main(String[] args) {

    final char c[]={'t','t','0'};
    //这里我们可以发现我们成功更改了c的值,并且没有报错
    for (int i=0;i< c.length;i++){
    c[i]='v';
    }
    System.out.println(c);
    //但是当我们重新写一个数组
    final char b[]={'t','t','0'};
    //下面的这个赋值就是错误的
    //c=b;

    //说完上面的,我们来看一下String类不可变的
    String str1="tom";
    System.out.println(str1.hashCode());
    System.out.println(str1);
    //我将m替换为o,我们可以根据结果发现,这个根本就没有改变,而且地址没有改变
    //说明在replace过程中其实创建的新的去存储这个字符串,而源地址的字符串是不会发生改变的
    str1.replace('m','0');
    System.out.println(str1.hashCode());
    System.out.println(str1);


    //但是我们在平常的时候,明明可以改变String的值,这是因为我们new了一个string对象,str1指向的对象地址改变了
    String str2="jack";
    str1=str2;
    System.out.println(str1);
    System.out.println(str1.hashCode());
    }
    }

    image-20220705105255820

  6. image-20220705105304838

查看上述的图可以发现,String继承了以上的接口,而继承Serializable表示该字符可以在网络上传输,而对于Comparable则表示可以用于比较。

String类型的创建剖析

  1. 方式一:直接赋值

    1
    String str1="jack";

    先从常量池查看是否有”jack“是数据空间,如果有,则直接指向;如果没有,则重新创建,然后指向。str1最终指向的是常量池的地址空间。

    image-20220705105320961

  2. 调用构造器:

    1
    String str2=new String("tom");

    先在堆中创建空间,里面维护了value属性,指向常量池tom空间。如果常量池没有tom,重写创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。但是由于value是String的私有方法,所以我们无法查看value的值

image-20220705105340537

练习1

1
2
3
4
5
6
7
8
9
package Learn;
public class Test{
public static void main(String[] args) {
String str1="jack";
String str2=new String("jack");
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
}
}

image-20220705105607057

他们虽然值相等,但是他们的地址却不相等。

练习2

  • intern

    1
    public String intern()

    返回字符串对象的规范表示。

    最初为空的字符串池由StringString

    当调用intern方法时,如果池已经包含与equals(Object)方法确定的相当于此String对象的字符串,则返回来自池的字符串。 否则,此String对象将添加到池中,并返回对此String对象的引用。

    由此可见,对于任何两个字符串sts.intern() == t.intern()true当且仅当s.equals(t)true

    所有文字字符串和字符串值常量表达式都被实体化。 字符串文字在The Java™ Language Specification的 3.10.5节中定义。

    结果

    一个字符串与该字符串具有相同的内容,但保证来自一个唯一的字符串池。

1
2
3
4
5
6
7
8
9
10
package Learn;
public class Test{
public static void main(String[] args) {
String str1="jack";
String str2=new String("jack");
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
System.out.println(str2.intern()==str1);
}
}

image-20220705105704532练习3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package Learn;
public class Test{
public static void main(String[] args) {
Person person1 = new Person();
Person person2=new Person();
person1.name="jack";
person2.name="jack";
System.out.println(person1==person2);
System.out.println(person1.name== person2.name);
System.out.println(person1.name.equals(person2.name));

String str1=new String("tom");
String str2=new String("tom");
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
}
}
class Person{
String name;
}

image-20220705110015100String类型字符串的特性

  • 编译器会堆常量池的对象进行优化。下面的一段代码中,按照本意会生成三个对象,但是编译器会自动识别出其中的关系,从而仅仅创建一个adcdac对象。查看以下代码之后我们可以看出,在str3中是创建了一个新对象的,否则不会与4和5的对象不相同。在3执行的过程,首先创建了StringBuilder对象,利用append接受str1与str2,然后在将值给与str3,由于使用了StringBuilder,所以本次的str3与String str3=new String(“adcdac”);效果相同。所以常量的相加在,常量池中进行操作,如果是变量则是在堆中进行操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    public class Test{
    public static void main(String[] args) {

    String str1="adc";
    String str2="dac";
    String str3=str1+str2;
    String str4="adcdac";
    String str5="adc"+"dac";
    System.out.println(str1.hashCode());
    System.out.println(str2.hashCode());
    System.out.println(str3.hashCode());
    System.out.println(str4.equals(str3));
    System.out.println(str4==str3);
    System.out.println(str5==str3);
    System.out.println(str4==str5);
    }
    }

image-20220705110448855Java中当数组与字符串在方法中被更改时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public class Test {
public String str="java";
public final char[]c={'j','a','v','a'};
public void change(String str,char[]c){
str="hello";
c[0]='s';
System.out.println(str.hashCode());
}
public static void main(String[] args) {
Test test = new Test();
test.change(test.str, test.c);
System.out.println(test.str);
System.out.println(test.str.hashCode());
System.out.println(test.c);
String str2="hello";
System.out.println(str2.hashCode());
}
}

image-20220705110807142String类的常见方法

方法名 用法
equals 区分大小写,判断是否相等
equalsIgnoreCase 忽略大小写,判断是否相等
length 获得长度
indexOf 获得字符在字符串中第一次出现的索引,从0开始,找不到就返回-1
lastindexOf 获得字符在字符串中最终出现的位置,索引从0开始
substring(a,b) 截取指定范围的字符串(不包含位置b)
trim 去除前后空格
charAt 获取某索引处的字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Learn;
public class Test {
public static void main(String[] args) {
String str="java";
String str1="JAva";
System.out.println(str.equals(str1));
System.out.println(str.equalsIgnoreCase(str1));
System.out.println(str.indexOf('a'));
System.out.println(str.indexOf("av"));
System.out.println(str.lastIndexOf('a'));
System.out.println(str.substring(0,2));
System.out.println(str.charAt(0));
System.out.println(" hello".trim());
}
}

image-20220705110943469

方法名 用法
toUpperCase 变成大写
toLowerCase 变成小写
concat 获得长度
replace 替换字符串中的字符
split 分割字符串
compareTo 比较字符串的大小
toCharArray 转换成字符数组
format 格式化字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Learn;

import java.util.Arrays;
import java.util.Locale;

public class Test {
public static void main(String[] args) {
String str2="hello";
String str1="JAva";
System.out.println(str1.toUpperCase());
System.out.println(str1.toLowerCase());
System.out.println(str1.concat(str2));
System.out.println(str1.replace('a','n'));
String poem="楚河,焊接,北京";
String []a=poem.split(",");
for (int i=0;i<a.length;i++){
System.out.println(a[i]);
}

System.out.println(str1.compareTo(str2));
System.out.println(str1.compareTo("JAvaFH"));
System.out.println(str1.toCharArray());
System.out.println(String.format("我是字符串%s",str1));
}
}

image-20220705110925625注意

对于喜欢使用表情包的同学,例如,其实表情包的表示使用的是两个代码单元,这是想要如果我们想要得到的第三个char字符不是最后一个,如果此时想要精确使用,就需要我们调用另外的方法。

1
2
3
4
5
6
7
8
9
10
11
/**
* @author 21050
*/
public class Test {
public static void main(String[] args) {
String str="😄你好";
for (char i:str.toCharArray()){
System.out.println(i);
}
}
}

image-20220705111045879此时我们输出length为4,如果想要输出正确的结果,需要首先转换为数组

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author 21050
*/
public class Test {
public static void main(String[] args) {
String str = "😄你好";
//此时我们需要先将其转换为一个数组
System.out.println(str.length());
int []array=str.codePoints().toArray();
System.out.println(array.length);
}
}

image-20220705111105430

额,怎么顺利遍历正在思考中。。。,在《Java技术核心卷》中建议不要使用charAt方法,