Java的String常用方法及部分源码解析【读懂源码是关键】

1.字符与字符串

字符串内部包含一个字符数组,String 可以和 char[] 相互转换.

1. 字符数组转为字符串【new String()】

   private static void CharToString() {
        char[] value = {'a','b','c','d'};
        String str = new String(value);
        System.out.println(str);

        str = new String(value, 1, 2);// 从偏移量为 1 的位置开始,取 2 个字符,构建 String 对象
        System.out.println(str);
    }

abcd
bc

new String(字符数组)源码
在这里插入图片描述

new String(字符数组, 起始索引, 拷贝个数)
会调用Arrays.copyOf(数组, 长度)进行拷贝给一个新的数组

在这里插入图片描述

new String(“字符串”)源码
在这里插入图片描述
我们 ctrl+左键查看this.value
在这里插入图片描述

发现这些都是 String 类的成员数据
我们用的字符串的本质就是一个数组,由于被 final修饰,所以不可更改

2. 字符串转为字符数组【toCharArray&.charAt】

    private static void StringToChar(){
        String str = "hello";
        for (int i = 0; i < str.length(); i++) {
            System.out.print(str.charAt(i));
        }
		System.out.println();
        char[] chars = str.toCharArray();// 将字符串以字符数组的方式进行存储
        System.out.println(chars);
    }

hello
hello

在这里插入图片描述

charAt(索引)
如果索引越界就抛出异常否则就反悔对应的下标字符

思考:System.out.println(chars);
为什么字符数组可以直接打印呢?
我们按住 ctrl+鼠标左键进入源码查看
在这里插入图片描述
发现 char[] 数组可以像 double, String 这样的基础数据类型直接出书而不需要调用 Arrays.toString() 方法
而对于其它类型的数组则需要调用 Arrays.toString() 方法
在这里插入图片描述

2. 字节与字符串

1. 字节转换为字符串【new String()】

  private static void ByteToString(){
        byte[] bytes = {97, 98, 99, 100};
        String str = new String(bytes);
        System.out.println(str);
        str = new String(bytes, 1, 2);
        System.out.println(str);
        str = new String(bytes, 1);//弃用
        System.out.println(str);
  }

abcd
bc
šŢţŤ

分析new String(字节数组)源码
在这里插入图片描述

发现是当前类的一个重写方法,传入了一个(字节数组, 默认的0索引, 字节数组长度)

再点击 this 查看当前这个重写方法
在这里插入图片描述

我们发现了个新的方法StringCoding.decode(bytes, offset, length)

在进入函数 decode 中查看发现是一个带有 decode功能编码格式的StringCodeing 类,里边的其它函数来实现各种格式的编码【gbk, utf-8】
在这里插入图片描述

明文规定@Deprecated弃用的函数最好别用,否则实际使用中会出现意料之外的错误
在这里插入图片描述
方法被@Deprecated注解,说明是已经弃用的方法。所以最好不要用,一面如上述代码打印未知内容

2. 字符串转为字节数组【getBytes】

    private static void StringToByte(){
        String str = "abcdef";
        byte[] bytes = str.getBytes();
        System.out.println(Arrays.toString(bytes));

        str = "学习";
        try {
            bytes = str.getBytes("utf-8");// 1 个汉字 == 3个字节
            System.out.println(Arrays.toString(bytes));
            bytes = str.getBytes("gbk");// 1 个汉子 == 2 个字节
            System.out.println(Arrays.toString(bytes));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

str.getBytes()源码
在这里插入图片描述

当转换为字节数组的时候调用的是encode 方法而不是字节转换为字符串的时候decode方法

指定编码格式
str.getBytes(charsetName)源码
在这里插入图片描述
在点击 encode 查看
在这里插入图片描述

经过 if 条件判断完之后就会进行经过一些列的编码后就会return 出编码后的字节数组

3.小结

那么何时使用 byte[], 何时使用 char[] 呢?

  1. byte[] 是把 String 按照一个字节一个字节的方式处理, 这种适合在网络传输, 数据存储这样的场景下使用. 更适合 针对二进制数据来操作.
  2. char[] 是吧 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候.

4. 字符串常见操作

1. 字符串比较【equals, compareTo】

    private static void learn_equals(){
        String str = "hello";
        String str1 = new String("Hello");
        System.out.println(str == str1);// false
        System.out.println(str.equals(str1));//false
        System.out.println(str.equalsIgnoreCase(str1));//true
    }

false
false
true

equals()源码分析
在这里插入图片描述
equalsIgnoreCase()源码分析
在这里插入图片描述
我们查看一下regionMatches如何比较的
在这里插入图片描述
在这里插入图片描述

    private static void learn_compareTo(){
        String str = "AB";
        String str1 = "ABc";
        String str2 = "ABC";
        System.out.println(str.compareTo(str1));
        System.out.println(str.compareTo(str2));
    }

-1
-1

在这里插入图片描述

所以不要根据返回值来判断字母的ASCII的差
字符串的比较大小规则, 总结成三个字 “字典序” 相当于判定两个字符串在一本词典的前面还是后面. 先比较第一个字符的大小(根据 unicode 的值来判定), 如果不分胜负, 就依次比较后面的内容

2. 字符串查找【contains, indexOf】

   private static void learn_contains(){
        String str = "abcdefgh";
        System.out.println(str.contains(str));
        System.out.println(str.contains("cdef"));
        System.out.println(str.indexOf("c"));
        System.out.println(str.indexOf("abd"));
    }

true
true
2
-1

contains源码分析
在这里插入图片描述

我们发现 contains 函数是根据 indexOf 函数的返回值来进行判断的【因为 indexOf 函数找不到的时候会返回 -1

indexOf源码分析
在这里插入图片描述

发现是调用的 indexOf 方法,查找字符串 str 从 0 索引处开始匹配
进入 indexOf 中继续查看
在这里插入图片描述
发现还有一个 indexOf 方法,我们在此点进去查看
在这里插入图片描述

现在基本都是用contains()方法完成。
使用indexOf()需要注意的是,如果内容重复,它只能返回查找的第一个位置

3. 字符串替换【replace】

        String str = "ababcabcd";
        String str1 = str.replace('a', 'A');// 替换单个
        System.out.println(str1);
        String str2 = str.replace("ab", "A");// 替换多个
        System.out.println(str2);
        String str3 = str.replace("ab", "AB");// 替换多个
        System.out.println(str3);
        String str4 = str.replaceAll("ab", "AB");// 替换全部
        System.out.println(str4);
        String str5 = str.replaceFirst("ab", "AB");// 替换第一个
        System.out.println(str5);
    }

AbAbcAbcd
AAcAcd
ABABcABcd
ABABcABcd
ABabcabcd

由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串.
replace 的源码也未了解清楚

4. 字符串拆分【spilt】

private static void learn_split() {
        String str = "ab#abc#abcd";
        String[] arr1 = str.split("#");
        for (String s : arr1) {
            System.out.println(s);
        }
        System.out.println("=======");
        String[] arr2 = str.split("#", 2);
        for (String s : arr2) {
            System.out.println(s);
        }
        System.out.println("=======");
        String[] arr3 = str.split("#", 5);
        for (String s : arr3) {
            System.out.println(s);
        }

        System.out.println("拆分 IP 地址");
        String str1 = "192.168.0.1";
        String[] arr4 = str1.split("\\.");
        for (String s: arr4) {
            System.out.println(s);
        }

        System.out.println("多个分隔符");
        String str2 = "Java string-split#test";
        String[] arr5 = str2.split(" |-|#");
        for (String s: arr5) {
            System.out.println(s);
        }
        System.out.println("多次分割");
        String str3 = "name=zhangsan&pass=111";
        String[] arr6 = str3.split("&");
        for (int i = 0; i < arr6.length; i++) {
            String[] tmp = arr6[i].split("=");
            for (int j = 0; j < tmp.length; j++) {
                System.out.println(tmp[j]);
            }
        }
    }

ab
abc
abcd
=======
ab
abc#abcd
=======
ab
abc
abcd
拆分 IP 地址
192
168
0
1
多个分隔符
Java
string
split
test
多次分割
name
zhangsan
pass
111
  1. 字符 “|”, “*” , “+” 都得加上转义字符,前面加上"".
  2. 而如果是 “”,那么就得写成"\".
  3. 如果一个字符串中有多个分隔符,可以用 “|” 作为连字符.
  4. split的源码没有了解清楚

5. 字符串截取【substring】

  private static void learn_substring(){
        String str = "hello world";
        String ret = str.substring(2);// 从 2 下标开始截取到结尾
        System.out.println(ret);

        String ret1 = str.substring(2, 6);//[2, 6): 2下标开始包含2,6下标结束不包含6
        System.out.println(ret1);
    }

llo world
llo 

在这里插入图片描述

  1. 索引从0开始
  2. 注意前闭后开区间的写法, substring(2, 6) 表示包含 0 号下标的字符, 不包含 6 号下标

6. 其他操作方法

    private static void learn_other(){
        String str = "  H e L l O   ";
        String ret = str.trim();// 去除左右两边但是不能去除中间
        System.out.println(ret);

        String ret1 = str.toLowerCase();
        String ret2 = str.toUpperCase();
        System.out.println(ret1);
        System.out.println(ret2);

        String ret3 = str.concat("W o R l D");
        System.out.println(ret3);
        System.out.println(str.length());
        System.out.println(str.isEmpty());
    }

H e L l O
  h e l l o   
  H E L L O   
  H e L l O   W o R l D
14
false

5. StringBuffer && StringBuilder

首先来回顾下String类的特点:
任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指 向而已。

通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和 StringBuilder类。

StringBuffer 和 StringBuilder 大部分功能是相同的,我们主要介绍StringBuffer 在String中使用"+"来进行字符串连接,但是这个操作在StringBuffer类中需要更改为append()方法:

public synchronized StringBuffer append(各种数据类型 b)

1. 初始化

    private static void learn_StringBufferAnd_StringBuilder(){
        StringBuffer sb = new StringBuffer("hello");
        StringBuilder sb2 = new StringBuilder("hello");
        String s = "hello";
//        StringBuilder stringBuilder = "hello";// 不可以直接赋值
        s += " world";// 会产生两个对象:bit, s
        /*
        StringBuilder: 操作也一样并且包含 String 的所有方法
         */
        sb.append(" world");
        sb.append(1).append("synchronized");
        System.out.println(sb);
        sb.reverse();// 修改了源字符串
        System.out.println(sb);
    }

hello world1synchronized
dezinorhcnys1dlrow olleh

2. 字符串拼接对比String

public static void main(String[] args) {
        String str = "abc";
        str += "def";
        System.out.println(str);
        /*
        System.out.println("优化==========优化");
        str = "abc";
        StringBuilder stringBuilder = new StringBuilder();
        str = stringBuilder.append(str).append("def").toString();
        System.out.println(str);
        */
    }

查看反汇编
在这里插入图片描述
还原优化的代码

public static void main(String[] args) {
		str = "abc";
        StringBuilder stringBuilder = new StringBuilder();
        str = stringBuilder.append(str).append("def").toString();
        System.out.println(str);
}

在这里插入图片描述
优化的效果有多明显呢?
字符串拼接效率对比如下图
String使用 “+” 进行硬拼接
在这里插入图片描述
注意 goto 语句,执行代码有 6 行。如果拼接很多次的话会带来很多不必要的执行语句

StringBuilder 优化后的代码
在这里插入图片描述

**String和StringBuffer最大的区别在于: **

String的内容无法修改【每次的修改都会产生新的对象】
StringBuffer的内容可以修改。频繁修改字符串的 情况考虑使用StingBuffer【每次修改都在源对象上改动而不造成额外的空间浪费】

3. String 和 StringBuffer 区别

为了更好理解String和StringBuffer,我们来看这两个类的继承结构:

goto 语句执行了一句,每次循环只执行 1 一条语句,因此效率会增加

4. StringBuilder 和 StringBuffer的区别

查看源码得知 StringBuffer 的方法都是 synchronnized (多线程同步),所以是安全的;而 StringBuilder 是单线程的。
StringBuilder源码
在这里插入图片描述
StringBuffer源码
在这里插入图片描述

发现 StringBuffer 的每个重写方法都是被 synchronized 所修饰的方法,代表的是多线程同步,是安全的方法

String类StringBuffer类
public final class String implements java.io.Serializable, Comparable, CharSequencepublic final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence

可以发现两个类都是"CharSequence"接口的子类。这个接口描述的是一系列的字符集。所以字符串是字符集的子 类,如果以后看见CharSequence,最简单的联想就是字符串。

注意:String和StringBuffer类不能直接转换。如果要想互相转换.可以采用下规则:

  1. String变为StringBuffer: 利用StringBuffer的构造方法或 append 方法
  2. StringBuffer变为String: 调用toString()方法。

除了append()方法外,StringBuffer也有一些String类没油的方法

方法函数原型
字符串反转public synchronized StringBuffer reverse()
字符串删除public synchronized StringBuffer delete(int start, int end)
字符串插入public synchronized StringBuffer insert(int offset, 各种数据类型 b)

反转

		String str = "abcdef";
//        StringBuilder stringBuilder = str;// 不可以直接赋值
        StringBuilder stringBuilder = new StringBuilder(str);// 调用构造方法
        System.out.println(stringBuilder.reverse());

fedcba

删除

		String str = "abcdef";
//        StringBuilder stringBuilder = str;// 不可以直接赋值
        StringBuilder stringBuilder = new StringBuilder(str);// 调用构造方法
        stringBuilder.delete(1,2);// b
        System.out.println(stringBuilder);

acdef

插入

String str = "abcdef";
//        StringBuilder stringBuilder = str;// 不可以直接赋值
        StringBuilder stringBuilder = new StringBuilder(str);// 调用构造方法
        stringBuilder.delete(1,2).insert(2, "Z");// b
        System.out.println(stringBuilder);

acZdef

面试题:请解释String、StringBuffer、StringBuilder的区别:

  1. String 是不可变对象,StringBuilder 和 StringBuffer 的内容可以修改
  2. StringBuilder 和 StringBuffer 大部分功能类似的
  3. StringBuffer 采用线程同步处理是安全的;StringBuilder 未采用同步处理,属于不安全操作
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值