String类

  • String类:代表字符串。如“abc”都是该类的实现。
  • String是一个final,代表不可变的字符序列 。
  • 字符串是常量,用双引号表示。它们的值在创建之后不能更改。
  • String对象的字符内容是存储在一个字节数组byte[]中的。(String的底层就是一个byte[]实现)

在jdk9之前是存储在一个字符数组char[]中, 为了节省空间在jdk9之后采用了byte[]

图片1.png

String的不可变性

String类位于java.lang包中,主要用来处理在初始化后其不能被改变的字符串。
那么为什么不能被改变呢?

  • final修饰一个类时,表明这个类不能被继承。
  • final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
    图片2.png

不可变性体现在哪?

首先了解: java中 “ == ” 判断符的特性

  • 在与基本数据类型进行判断时,“ == ” 比较的是两者的
  • 在与引用数据类型进行判断时,“ == ” 比较的是两者的地址
  • 基本数据类型:long、int、short、byte、float、double、char、boolean
  • 引用数据类型:类、 接口类型、 数组类型、 枚举类型、 注解类型、 字符串型

再了解equlas()方法
equals方法是由Object类提供的,可以由子类来进行重写
图片3.png

在Object类中,equlas()方法实际上就是“ == ”判断符

了解这些后,那么再来思考一个问题:

String s1 = "abc";   // 字面量方式定义
String s2 = "abc";
System.out.println( s1 == s2);

答案

true


疑惑:不是讲“ == ”对于引用数据类型比较的是地址,那么s1与s2的地址为啥就相同呢?

首先了解下java的常量池概念:

常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。

通过字面量的方式(区别new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

但是字符串常量池中是不会存储相同内容的字符串的
内存图解1

这里可以看到s1与s2引用的内存区域是相同的,所以System.out.println( s1 == s2);输出的是true

接下来可以思考,当我把s1的值修改后,s1与s2的引用的内存区域还是相同的吗?

String s1 = "abc";   // 字面量方式定义
String s2 = "abc";
s1 = "java";
System.out.println( s1 == s2);

答案

false

内存图解2

可以很容易想到,既然s1与s2的内容不一样了,那么在常量池中也不能引用同一个区域,所以会开辟一个新区域给s1引用

所以当对字符串重新赋值时,需要重新指定内存区域,不能用原有的value进行赋值

字面量定义与new实例化String类的区别

String s1 = "abc";    // 字面量方式定义
String s2 = new String("abc");

System.out.println(s1 == s2);

答案

false

先要知道栈跟堆的区别:

  • 栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。
  • 堆内存用于存放由new创建的对象和数组

因为堆内存用于存放由new创建的对象和数组,而s1的地址在编译时就确定在字符串常量池中,s2则是在运行阶段确定地址在堆中,所以可以确定s1的地址不等于s2的地址。 因此输出的是 false

接着可以比较:System.out.println(s1.equals(s2)); 这输出的又是什么呢?
前面知道在Object类中,equlas()方法实际上就是“ == ”判断符
那么在类中,如果没有重写equlas()方法,则比较的永远是地址

前面知道,s1跟s2的地址不相等,那么我们可以提出假设:如果此时输出的是false则String类没有重写,如果输出的是true则String类的equals方法必定被重写了

看源码:
图片4.png
图片5.png

从源码中可以看出:String 类中重写了 equals() 方法用于比较两个字符串的内容是否相等。

String类常用的方法

1. 获取字符串长度:length()方法

String s1 = "abc";
System.out.println(s1.length()); // 3

2. 字符串的比较:equals(String anotherString)、compareTo(String anotherString)

compareTo()方法是按照字典顺序与参数s指定的字符串比较大小。

String s1 = "abc";
String s2 = "aba";
System.out.println(s1.compareTo(s2)); // 2 大于0

3. 获取字符串的子串:substring(int start,int end) //第二个参数可选

String s1 = "abcdefg";
String s2 = s1.substring(2); // cdefg
String s3 = s1.substring(2, 5); // cde

4.获取指定索引处的字符:charAt(int index)

String s1 = "abcdefg";
System.out.println(s1.charAt(2)); // c

5.获取str在字符串对象中第一次出现的索引:indexOf(String str)

String s1 = "abcdefg";
String s2 = "cd";
System.out.println(s1.indexOf(s2)); // 2

6.比较字符串的内容是否相同,忽略大小写:equalsIgnoreCase(String anotherString)

String s1 = "abcdefg";
String s2 = "ABCDEFG";
System.out.println(s1.equalsIgnoreCase(s2)); // true

StringBuffer类

StringBuffer类表示的是一个本身内容可变的字符串对象包含一个缓冲区,主要用于完成字符串的动态添加、插入、和替换等等操作

String与StringBuffer、StringBuilder的异同:

  • String:不可变的字符序列:底层使用byte[]存储
  • StringBuffer:可变的字符序列:线程安全的,但效率低;底层使用byte[]存储
  • StringBuilder:可变的字符序列:jdk5后才有的线程不安全的,但效率高;底层使用byte[]存储

String与StringBuffer的区别在于是否可变;
StringBuffer与StringBuilder的区别在于线程是否安全

为什么StringBuffer可变呢,怎么实现缓冲的呢?

来看一下源码,在没有传参的情况下默认构造初始容量为16的空字符缓冲区。
图片6.png

有参数的情况下,初始容量是16+字符串的长度,然后用append()方法将传入的字符放入字符缓冲区中:
图片7.png

图片8.png

如果在追加字符串的时候容量不够了怎么办,我们看到有个ensureCapacityInternal()的方法:
图片9.png

图片10.png

ensureCapacityInternal()中会判断需不需要扩容:
图片11.png

如果需要扩容,则会进入newCapacity()方法中重新设定大小

这里的重新设定大小不是改变数组的大小,而是把之前的数组进行拷贝放入一个大的数组中

图片12.png

StringBuffer类的常用方法

1. 添加操作:append(xxx)

StringBuffer s1 = new StringBuffer("abc");
s1.append("def");

System.out.println(s1);

2.插入操作:insert(int offset , xxx) offset参数是要插入的位置

StringBuffer s1 = new StringBuffer("abcdef");
s1.insert(2,"def");

System.out.println(s1);  // abdefcdef

3.删除操作:delete(int start, int end) star是删除的起始序号,end是删除的终止序号

注意:包括start,但不包括end

StringBuffer s1 = new StringBuffer("abcdef");
s1.delete(2,4);

System.out.println(s1);  // abef

4.内容替换:replace(int start,int end,String str)

StringBuffer s1 = new StringBuffer("abcdef");
s1.replace(2,4,"qqqq");

System.out.println(s1);  // abqqqqef

5.反转操作:reverse()

StringBuffer s1 = new StringBuffer("abcdef");
s1.reverse();

System.out.println(s1);  // fedcba
最后修改:2021 年 08 月 25 日 11 : 54 AM
如果觉得我的文章对你有用,请随意赞赏