0
点赞
收藏
分享

微信扫一扫

Java中的String类

源码之路 2022-06-05 阅读 74

String类


一,常用方法

1.1,字符串的构造方法

在Java里面,我们知道String是一个类,叫做字符串类,那么作为一个类,自然有它自己的构造方法,常用的有下面三种(其余的可以用到的时候去查看API文档):

public class TestDemo220528 {
    public static void main(String[] args) {
//        1,使用常量串进行构造,其实和数组的静态赋值一样,这里也是省略的写法
        String str1 = "hello";
        System.out.println(str1);
//        2,使用关键字new一个对象
        String str2 = new String("hahaha");
        System.out.println(str2);
//        3,利用字符数组进行构造
        char[] arr = {'a','b','c','d'};
        String str3 = new String(arr);
        System.out.println(str3);

    }
}

//输出结果:
//hello
//hahaha
//abcd

注意:

首先,我们来看看String类的定义的小部分源码:

在这里插入图片描述


内 存 图 : \color{orange}{内存图:}

1,str1于str2:

在这里插入图片描述


2,str3:

因为str3在进行构造的时候,我们传入的是一个字符串数组,源码中定义的方法如下:

在这里插入图片描述

也即是说我们传入的字符数组会先被拷贝一份之后让value去引用这个数组。

在这里插入图片描述



1.2,String对象的比较

1,== 比较是否引用同一个对象

public class TestDemo220528 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println(a == b);
        
        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1 == str2);//str1,str2就是两个对象,地址的值肯定不相同
    }
}

2,equals方法

public class TestDemo220528 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println(a == b);

        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1 == str2);//str1,str2就是两个对象,地址的值肯定不相同

        System.out.println(str1.equals(str2));//true,字符串的内容是一样的

    }
}

3,compareTo()方法

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("hello");
        String str3 = new String("abcd");
        String str4 = new String("abcdef");

        System.out.println(str1.compareTo(str2));//输出0
        System.out.println(str1.compareTo(str3));//输出7
        System.out.println(str3.compareTo(str4));//输出-2
    }
}

4,compareToIgnoreCase()方法

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("HELLO");
        String str2 = new String("hello");
        System.out.println(str1.equalsIgnoreCase(str2));//忽略大小写进行比较  true
        System.out.println(str1.compareToIgnoreCase(str2));//忽略大小写进行比较  false
    }
}

1.3,字符串查找

String类提供的常用查找方法如下:

1,char charAt(int index)

作用:返回字符串index下标处对应的字符,如果index为负数或者越界都会抛出异。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        for (int i = 0; i < str1.length(); i++) {
            System.out.println(str1.charAt(i));//将字符串的字符一个个输出
        }
    }
}

2,int indexOf(int ch)

作用:返回字符ch第一次出现的位置,没有就返回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        int ret = str1.indexOf('l');
        System.out.println(ret);//输出2
    }
}

3,int indexOf(int ch, int fromIndex)

作用:从fromIndex位置开始找ch第一次出现的位置,没有返回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        int ret = str1.indexOf('l',3);
        System.out.println(ret);//输出3,注意,是从fromindex开始找,但是你的下标的参照还是从起始位置开始的哦
    }
}

4,int indexOf(String str)

作用:返回str第一次出现的位置,没有返回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        int ret = str1.indexOf("ll");
        System.out.println(ret);//输出2
    }
}

5,int indexOf(String str, int fromIndex)

作用:从fromIndex位置开始找字符串str第一次出现的位置,没有返回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("helloll");
        int ret = str1.indexOf("ll",4);
        System.out.println(ret);//输出5
    }

6,int lastIndexOf(int ch)

作用:从后往前开始找字符ch,如果没有则返回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        int ret1 = str1.lastIndexOf('e');
        System.out.println(ret1);//输出1
    }
}

7,int lastIndexOf(int ch, int fromIndex)

作用:从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返 回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("abcdcef");
        int ret = str1.lastIndexOf('c',3);
        System.out.println(ret);//输出2
    }
}

8,int lastIndexOf(String str)

作用:从后往前找,返回str第一次出现的位置,没有返回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("abcdcef");
        int ret = str1.lastIndexOf("dc");
        System.out.println(ret);//输出3
    }
}

9,int lastIndexOf(String str, int fromIndex)

作用:从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返 回-1。

public class TestDemo220528 {
    public static void main(String[] args) {
        String str1 = new String("abcdcedcf");
        int ret = str1.lastIndexOf("dc",5);
        System.out.println(ret);//输出3
    }
}

1.4,字符串转化

1,数值转字符串

class Student{
    public int age;
    public Student(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }
}
public class TestDemo220529 {
    public static void main(String[] args) {
//        将其他数据类型转换成字符串
        String str1 = String.valueOf(123);
        String str2 = String.valueOf(11.2);
        String str3 = String.valueOf(new Student(18));//也可以将一个对象转换成字符串

        System.out.println(str1);//输出123
        System.out.println(str2);//输出11.2
        System.out.println(str3);//输出 Student{age=18}
    }
}

2,字符串转数值

public class TestDemo220529 {
    public static void main(String[] args) {
        int a = Integer.valueOf("100");
        int b = Integer.valueOf("100",8);//八进制转化
        System.out.println(a);//输出100
        System.out.println(b);//输出64

        int c = Integer.parseInt("123");
        System.out.println(c);//输出123
    }
}

在大多数的情况下,我们用parseInt()之类的方法比较多。


3,大小写转化

public class TestDemo220529 {
    public static void main(String[] args) {
        String s1 = new String("HELLO");
        System.out.println(s1.toLowerCase());//输出hello
        System.out.println(s1);//输出HELLO

        String s2 = new String("hello");
        System.out.println(s2.toUpperCase());//输出HELLO
        System.out.println(s2);//输出hello
    }
}

4,字符串转数组

public class TestDemo220529 {
    public static void main(String[] args) {
//      字符串转数组
        String s1 = new String("hello");
        char[] ch = s1.toCharArray();//字符串转数组的方法
        for (char x:ch) {
            System.out.println(x);
        }
//        数组转字符串
        char[] ch1 = {'h','e','l','l','o'};
        String s2 = new String(ch1);
        System.out.println(s2);
    }
}

5,格式化输出为字符串

public class TestDemo220529 {
    public static void main(String[] args) {
        String s1 = String.format("%d - %d - %d",2022,5,29);
        System.out.println(s1);//输出字符串 “2022 - 5 - 29”
    }
}

1.5,字符串替换

public class TestDemo220529 {
    public static void main(String[] args) {
        String s1 = new String("abcdefabdddd");
        //替换单个字符
        String ret = s1.replace('a','t');//替换生成一个新的对象
        System.out.println(ret);//tbcdeftbdddd
        
        //替换字符串
        String ret1 = s1.replace("ab","qq");
        System.out.println(ret1);//qqcdefqqdddd
        
        //替换字符串中的某一个全部的内容
        String ret2 = s1.replaceAll("ab","ss");
        System.out.println(ret2);//sscdefssdddd
        
        //替换第一次出现的内容
        String ret3 = s1.replaceFirst("ab","hh");
        System.out.println(ret3);//hhcdefabdddd
    }
}

1.6,字符串拆分

1,将字符串全部拆分

//注意split的参数是字符串不是字符
public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = new String("I am a student");
        String[] ret = s1.split(" ");
        for (String s:ret) {
            System.out.println(s);
        }
    }
}

字符串拆分的方法split的返回值是字符串数组,按照传入的字符串,把原字符串进行拆分。比如上面的空格,虽然是一个字符,但是你得当作一个字符串传入,也就是 “ ”。


2,将字符串进行部分的拆分

public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = new String("I am a student");
        String[] ret = s1.split(" ",2);
        for (String s:ret) {
            System.out.println(s);
        }
    }
}
//输出 I
//     am a student


注意:

1,字符"|","*","+"都得加上转义字符,前面加上 "\\" 。

public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = new String("192.168.11.11");
        String[] ret = s1.split(".");
        for (String s:ret) {
            System.out.println(s);
        }
    }
}

//按照常理而言,好像并没有任何的使用错误,但是实质就是没有任何的输出,调试后也会发现ret数组里面根本什么都没有,也即是没有拆分

//正确做法:
public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = new String("192.168.11.11");
        String[] ret = s1.split("\\.");
        for (String s:ret) {
            System.out.println(s);
        }
    }
}

2,如果存在多个分割符,可以用|来作为连字符。也就是表示|左右两边都是分隔符。

public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = new String("name==zhangsan&&age==20");
        String[] ret = s1.split("==|&&");
        for (String s:ret) {
            System.out.println(s);
        }
    }
}
//输出
//name
//zhangsan
//age
//20

当然,这是选择用|,其实也可以嵌套使用split达到相同的效果

public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = new String("name==zhangsan&&age==20");
        String[] ret = s1.split("&&");//首先按照&&拆分一次
        for (String s:ret) {
            String[] ret1 = s.split("==");//把按照&&拆分的每一部分再按照==拆分一次就好
            for (String ss:ret1) {
                System.out.println(ss);
            }
        }
    }
}

1.7,字符串截取

public class TestDemo220530 {
    public static void main(String[] args) {
        String s = new String("hello world");
        //截取beginIndex到字符串的末尾
        String ret1 = s.substring(3);
        
        //截取beginIndex到endIndex,但是注意这个范围是一个左闭右开的!!!!!
        String ret2 = s.substring(3,7);
        System.out.println(ret1);//输出  lo world
        System.out.println(ret2);//输出  lo w
    }
}

1.8,其他操作方法

String trim()//去掉字符串左右两边的空格
public class TestDemo220530 {
    public static void main(String[] args) {
        String s = new String("   ab ababab abab    ");
        System.out.println(s);
        System.out.println(s.trim());
    }
}

程序运行截图:

在这里插入图片描述


1.9,字符串常量池

1,为什么引入常量池


2,字符串常量池的介绍

字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(哈希表)。


3,再谈字符串对象的创建

3.1,直接使用字符串常量进行赋值

当然,所有的字符串常量需要创建的,在字节码文件加载的时候就已经创建好了。

public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1 == s2);
    }
}

对于上面的这段代码,可能主观上会觉得s1与s2是两个对象,所以输出结果为false,但是实际是true,但是为什么就是true呢?从内存图上看的会清楚,如下:

在这里插入图片描述


3.2,通过new创建String对象

public class TestDemo220530 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = new String("hello");
        String s3 = new String("world");
        System.out.println(s1 == s2);//输出 false
    }
}

内 存 图 : \color{orange}{内存图:} :
在这里插入图片描述

所以,总的来说,直接使用常量串进行赋值会比new字符串对象在空间的节省以及速度上都会好很多。


3.3,intern方法

public class TestDemo220530 {
    public static void main(String[] args) {
        char[] ch = {'a','b','c'};
        String s1 = new String(ch);

        String s2 = "abc";
        System.out.println(s1 == s2);//输出false
    }
}

上面这段代码毫无疑问的会输出false,因为s1,s2是两个不同的对象的引用,地址肯定是不同的。

在这里插入图片描述

public class TestDemo220530 {
    public static void main(String[] args) {
        char[] ch = {'a','b','c'};
        String s1 = new String(ch);

        s1.intern();
        String s2 = "abc";
        System.out.println(s1 == s2);//输出true
    }
}

内存图:

在这里插入图片描述

通过intern()方法将s1添加进常量池后,因为s1与s2的字符串的内容是一样的,所以在常量池里面就只会有一个相同的字符串对象,那么自然s1 == s2就是true了。

注意:

public class TestDemo220530 {
    public static void main(String[] args) {
        char[] ch = {'a','b','c'};
        String s1 = new String(ch);


        String s2 = "abc";
        s1.intern();
        System.out.println(s1 == s2);
    }
}

如同现在这段代码就会直接输出false,因为常量池中已经存在有相同内容的字符串对象s2,所以s1.intern()不会把s1添加进常量池,所以s1 == s2是不成立的。


总结,既然几种情况下的String对象的创建都已经介绍清楚了,那下面来看看面试题,进行下相关的总结:

【面试题:请解释String类中几种对象实例化的区别】


1.10,字符串的不可变性

String是一种不可变对象. 字符串中的内容是不可改变。字符串不可被修改

在这里插入图片描述

在这里插入图片描述


1.11,字符串的修改

//以拼接字符串为例
public class TestDemo220603 {
    public static void main(String[] args) {
        String s = "hello";
        s += "world";
        System.out.println(s);//输出helloworld
    }
}

在这里插入图片描述

//模拟实现下上面的编译过程
public class TestDemo220603 {
    public static void main(String[] args) {
        String s = "hello";
//        想要实现在后面追加一个world
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("hello");
        stringBuilder.append("world");
        s = stringBuilder.toString();
        System.out.println(s);
    }
}

那正如上面代码所展示的,我们在对字符串进行修改的时候底层上利用的是StringBuilder,那为什么StringBuilder就可以呢?

在这里插入图片描述



二,StringBuilder,StringBuffer

上面既然用到了StringBuilder与StringBuffer,那么下面就给大家详细介绍下这两个类。

public class TestDemo220603 {
    public static void main(String[] args) {
        StringBuilder stringBuilder1 = new StringBuilder("hello");//"hello"还是在常量池
        stringBuilder1.append("world");
        System.out.println(stringBuilder1);//追加 相当于String类的 +=

        System.out.println(stringBuilder1.length());//求字符串的长度

        stringBuilder1.reverse();//将字符串进行逆置
        String s1 = stringBuilder1.toString();//用String类型的变量接收
        System.out.println(s1);
    }
}

//输出结果:
//helloworld
//10
//dlrowolleh

【String类与StringBuilder类的转化问题:】

1,String转化为StringBuilder:利用StringBuilder的构造方法或者append方法

StringBuilder stringBuilder1 = new StringBuilder("hello");

StringBuilder stringBuilder2 = new StringBuilder();
stringBuilder.append("hello");

2,StringBuilder转化为String:利用StringBuilder的toString方法

StringBuilder stringBuilder1 = new StringBuilder("hello");
String s1 = stringBuilder1.toString();

【常见面试题:】

1,String,StringBuilder,StringBuffer的区别

在这里插入图片描述


2,以下总共创建了多少个String对象【前提不考虑常量池之前是否存在】

String str = new String("ab"); // 会创建多少个对象
会创建两个对象 new String()一个,然后常量池"ab"中一个

String str = new String("a") + new String("b"); // 会创建多少个对象
会创建六个对象 new String()一个,常量池"a"一个,new String()一个,常量池"b",前后拼接会创建一个StringBuilder对象
,赋值给str默认调用toString()方法的时候会new一个String对象

举报

相关推荐

0 条评论