【JavaSE】深入了解Java中的String类

String类的重要性

因为字符串应用的广泛,所以Java中提供了String类来对字符串进行操作

我们在刷题的时候,大多数题目都是关于字符串的,所以如果更进一步的了解String类,对于我们日常刷题也是达到了事半功倍的作用,除了面试笔试题的涉及,String类在面试中也是常客
所以大家跟着我一起来学习String类吧!

String类的常用方法

String类的构造方法

String类常用的构造方法有这三种(想查看更多的构造方法可以在官方文档中查):

String s = "hello"; //直接使用字符串常量
String s = new String("hello"); //通过new String传递一个字符串常量
char[] chars = {'h','e','l','l','o'};
String s = new String(chars); //通过new String传递一个字符数组

注意:String类是引用类型,内部并不存储字符串本身,而是存的是对象的地址

看一下String类的源码:发现字符串实际保存在成员变量字符数组value

在这里插入图片描述

字符串对象的比较

  1. 使用==比较,对于基本类型来说比较的是值是否相等,对于引用类型来说比较的是地址是否相同
        int a = 10;
        int b = 10;
        int c = 20;
        System.out.println(a==b); //true 值相等
        System.out.println(a==c); //false 值不相等

        String s1 = new String("a"); 
        String s2 = new String("a");
        String s3 = s2;
        System.out.println(s1==s2); //false 地址不同为false
        System.out.println(s2==s3); //true 地址相同为true
  1. 使用equals方法比较

equals方法是Object类中的方法,默认是按照==也就是地址比较,但是String类重写了equals方法,使得可以比较字符串的内容是否相等

        String str1 = new String("hello");
        String str2 = new String("hello");
        String str3 = new String("world");
        System.out.println(str1.equals(str2)); //true
        System.out.println(str2.equals(str3)); //false

注意: a.equals(b),此时a不能为null,否则会发生空指针异常

        String m = null;
        String n = "a";
        System.out.println(m.equals(n));

在这里插入图片描述

  1. 使用compareTo方法比较

前面两种比较都是比较是否相同,而compareTo可以比较大小,它的返回值类型为int,它比较大小的方式是按照字典次序比较,返回比较字符的次序差值(如果前面字符相同,返回长度差值)

        String s1 = "abc";
        String s2 = "abcd";
        int ret = s1.compareTo(s2); //-1,因为前面字符相同,差值为长度差
        System.out.println(ret);
        String s3 = "a";
        String s4 = "d";
        System.out.println(s4.compareTo(s3)); //3 d比a多了3

注意: a.compareTo(b),a和b都必须不能为null,否则会发生空指针异常

  1. 使用compareToIgnoreCase方法比较

该方法与上述compareTo方法比较方式相同,只是忽略了大小写进行比较

字符串的查找

字符串查找是字符串使用最频繁的操作的之一,经常刷题的小伙伴可以感受的到哦

方法功能
char charAt(int index)获取index位置上的字符
int indexOf(int c)返回c第一次出现的位置,没有返回-1
int indexOf(int c,int fromIndex)从fromIndex位置开始找,返回c第一次出现的位置,没有返回-1
int indexOf(String str)返回字符串str第一次出现的位置,没有返回-1
int indexOf(String str,int fromIndex)从fromIndex位置开始找返回字符串str第一次出现的位置,没有返回-1
int lastindexOf(int c)从后往前找,返回c第一次出现的位置,没有返回-1
int lastindexOf(int c,int fromIndex)从fromIndex位置开始从后往前找,返回c第一次出现的位置,没有返回-1
int lastindexOf(String str)从后往前找,返回字符串str第一次出现的位置,没有返回-1
int lastindexOf(String str,int fromIndex)从fromIndex位置开始从后往前找,返回字符串str第一次出现的位置,没有返回-1
        String s = "abcdef";
        System.out.println(s.charAt(2)); //c
        System.out.println(s.indexOf('b')); //1
        System.out.println(s.indexOf('d',1)); //3
        System.out.println(s.indexOf("cde")); //2
        System.out.println(s.lastIndexOf("bcd")); //1

字符串的转换

  1. 数值和字符串的转换
        //数值转化为字符串
        int a = 10;
        double b = 20;
        String sa = String.valueOf(a); //10
        String sb = String.valueOf(b); //20.0

        //字符串转为数值
        String s = "10";
        int is = Integer.valueOf(s); //字符串转为Integer,Integer->int为自动拆箱,所以可以用int直接接收
        int is1 = Integer.parseInt(s); //将字符串解析为整形,返回值为int
        double ds = Double.valueOf(s);
        double ds1 = Double.parseDouble(s);

注意: 字符串转数值类型的时候,字符串必须是可转的,否则会报类型转化异常

        String ss = "12b";
        int ssi = Integer.valueOf(ss);

在这里插入图片描述

  1. 大小写转换

toUpperCase():将字符串所有的字符转换为大写
toLowerCase():将字符串所有的字符转换为小写

        String str = "abcdEF";
        System.out.println(str.toLowerCase()); //abcdef
        System.out.println(str.toUpperCase()); //ABCDEF
  1. 数组与字符串之间的转换
        String s = "hello";
        char[] c = s.toCharArray(); //字符串转换为字符数组

        String ss = new String(c); //字符数组转换为字符串

        int[] a = {1,2,3};
        String sa = Arrays.toString(a); //数组转为字符串,带中括号 [1,2,3]
        System.out.println(sa);
  1. 格式化
        String str = String.format("%d-%d-%d",2023,2,25); 
        System.out.println(str); //2023-2-25

字符串的替换

替换可以替换所有指定的内容,也可以替换第一次出现的内容

        String s = "hello";
        String ret = s.replaceAll("l","a"); //替换所有,将l替换为a
        System.out.println(ret); //heaao

        String ret1 = s.replaceFirst("l","a"); //替换第一次出现的l为a
        System.out.println(ret1); //healo

提示:字符串是不可变的,所以每次对字符串进行修改后,会重新生成一个String对象

字符串的分割

比如有这样的一个字符串:“张三,李四,王五”,我们需要将该字符按逗号进行分割

        String str = "张三,李四,王五";
        String[] array = str.split(",");
        for(String i : array){
            System.out.println(i);
        }

在这里插入图片描述

注意: 有些特殊的字符作为分割符号需要加上转义符号

  1. 字符"|“,”*“,”+"都得加上转义字符,前面加上 “\”
  2. 而如果是 “” ,那么就得写成 “\\” .
  3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符
        String str1 = "a*b*c*d";
        String[] arr = str1.split("\\*"); //*得使用转义符
        String str2 = "a-b-c+d-e-f+g-h-i";
        String[] arr1 = str2.split("\\+|-"); //按照+和-分割

字符串的截取

方法功能
String substring(int index)从index位置开始一直截取到末尾
String substring(int start,int end)从start位置开始截取到end位置,不包括end位置的字符
        String s = "abcdef";
        String ret1 = s.substring(2); //cdef  从位置2截取至末尾
        String ret2 = s.substring(2,4); //cd 截取区间为[2,4)
        System.out.println(ret1); //cdef
        System.out.println(ret2); //cd

字符串去除两边空格

使用trim()方法可以去除字符串两边的空格

注意: 不能去掉字符串中间的空格

        String ss = " abc def ";
        String ret3 = ss.trim();
        System.out.println(ret3); //abc def

字符串常量池(重点)

引入

观察下面一段代码,思考:为什么s1和s2引用的是同一个对象,而s3和s4不是呢?

        String s1 = "hello";
        String s2 = "hello";
        String s3 = new String("hello");
        String s4 = new String("hello");
        System.out.println(s1==s2); //true
        System.out.println(s1==s3); //false
        System.out.println(s3==s4); //false
    }

在Java中,为了使程序执行的更快并节省空间,Java为String类提供了字符串常量池

字符串常量池在JVM中是一个StringTable,实际是一个固定大小的HashTable,在Java8中字符串常量池的位置在

再谈对象的创建

  1. 直接使用字符串常量进行赋值
String s1 = "hello";
String s2 = "hello";

在这里插入图片描述

  • 使用String s1 ="hello’'创建对象时,先在字符串常量池中找,没找到,则创建字符串对象放在常量池中,再将字符串引用赋值给s1
  • 使用String s2 = "hello"创建对象时,先在字符串常量池中找,找到了,则直接将字符串引用赋值给s2
  1. 使用new创建String类对象
String s1 = "hello";
String s2 = "hello";
String s3 = new String("world");
String s4 = new String("world");

在这里插入图片描述

说明:只要是new出来的对象,都是唯一的,通过上面的例子,可以发现使用常量池的对象创建String字符串的方法效率更高,而且更节省空间

intern方法的介绍

intern方法是一个native方法(底层使用C++实现),作用是手动的将创建的String对象添加到字符串常量池中

看下面一段代码:

        char[] c = {'h','e','l','l','o'};
        String s1 = new String(c);
        //s1.intern();
        String s2 = "hello";
        System.out.println(s1==s2); //false

发现结果为false,因为通过字符数组创建String对象底层是将char数组进行拷贝,不会加入到常量池中,观察此时的内存分布:
在这里插入图片描述

将intren方法打开,观察结果:

        char[] c = {'h','e','l','l','o'};
        String s1 = new String(c);
        s1.intern();
        String s2 = "hello";
        System.out.println(s1==s2); //true

发现结果为true,观察此时内存分布:
在这里插入图片描述

字符串的不可变性

String是一个不可变的对象,也就是内容一旦定义就不可以改变,那为啥是不可变的呢?

看一下String底层的源码:

在这里插入图片描述

发现字符数组使用private final修饰的,使用final修饰说明指向不能改变,使用private修饰说明对外不提供访问接口,别的类中不能直接访问到字符数组,所以是不可变的

那字符串是如何进行修改的呢?

        String s = "hello ";
        s += "world";
        System.out.println(s);

说明:底层会创建一个StringBuilder对象,调用append对象进行字符串拼接,然后通过StringBuilder对象的toString方法构造一个新的String对象,将新构造的String对象的引用赋值给s

StringBuilder和StringBuffer

因为String对象的不可变性,为了方便字符串的修改,Java中提供了StringBuilder和StringBuffer类

StringBuilder的构造方法:

方法功能
StringBuilder()构造一个空的StringBuilder对象
StringBuilder(String s)使用字符串s构造StringBuilder对象

StringBuilder的常用方法:

方法功能
StringBuilder()构造一个空的字符串构建器
int length()返回构建器中代码单元的数量
StringBuilder append(String str)追加一个字符串
StringBuilder append(char c)追加一个代码单元
void setCharAt(int i,char c)将第i个代码单元设置为c
StringBuilder insert(int offset,String str)在offset位置插入一个字符串
StringBuilder insert(int offset,char c)在offset位置插入一个代码单元
StringBuilder delete(int starIndex,int endIndex)删除偏移量从starIndex到endIndex-1的代码单元
StringBuilder reverse()反转字符串
String toString()将所有字符按照String方式返回
String substring(int start)从start开始一直到末尾的字符以String的方式返回
        StringBuilder s = new StringBuilder("hello");
        s.append(' ');
        s.append("world");
        System.out.println(s); //hello world
        System.out.println(s.length()); //11
        s.insert(6,"and"); 
        System.out.println(s); //hello andworld
        s.reverse(); 
        System.out.println(s); // dlrowdna olleh
        System.out.println(s.substring(5)); //dna olleh
        System.out.println(s.toString()); //dlrowdna olleh

String类与StringBuilder类的转换:

        String s = "hello";
        
        StringBuilder sb = new StringBuilder(s); //String -> StringBuilder
        String str = sb.toString(); //StringBuilder -> String

StringBuffer和StringBuilder的功能差不多,只不过是StringBuffer在多线程环境下是线程安全的,而StringBuilder是不安全的

StringBuffer的关键方法都加了synchronized关键字保证线程安全:

在这里插入图片描述

常见面试题

  1. String,StringBuilder,StringBuffer的区别?

String是不可变的,其内容不可修改,StringBuilder和StringBuffer是可变的,内容可以修改
StringBuffer是线程安全的,其关键方法使用synchronized关键字修饰,StringBuilder是线程不安全的

  1. 以下共创建了多少个String对象(常量池中不存在)?
        String str1 = new String("abc");
        String str2 = new String("hello") + new String("world");
  • str1:一个new出来的String对象,一个常量池中的对象,一共是2
  • str2:两个new出来的String对象,两个常量池中的对象,拼接会创一个StringBuilder对象,StringBuilder对象转换为String时又会创建一个String对象,将String对象的引用赋值给str2,所以一共是6
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X_H学Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值