理解String 类(下)

简介: 理解String 类(下)

六、字符串查找



1. contains( ) 方法


主串通过 contains( ) 方法 调用子串


程序清单17:


public class Test17 {
    public static void main(String[] args) {
        String str1 = "abcdef";
        String str2 = "cde";
        System.out.println(str1.contains(str2));
    }
}


输出结果:


8f13be99a6f7461483d1dc26b059cfcc.png


2. indexOf( ) 方法


主串通过 indexOf( ) 方法 调用子串


如果主串中有子串的数据,那么就返回主串中的索引,即子串对应的第一个字符的位置,如果主串中没有子串的数据,则返回 -1.


程序清单18:


public class Test18 {
    public static void main(String[] args) {
        String str1 = "abcdef";
        String str2 = "cde";
        String str3 = "dec";
        System.out.println(str1.indexOf(str2));
        System.out.println(str1.indexOf(str3));
    }
}


输出结果:


4ba27ce9358b496889257153409fdc89.png


3. lastIndexOf( ) 方法


indexOf( ) 方法是在主串中从前往后找子串,lastIndexOf( ) 方法是在主串中从后往前找子串。虽然 lastIndexOf( ) 方法是从后往前找,但是输出的索引下标依然是从前往后算的。只不过,如果两个方法面对重复的字符,会出现不同的结果。 如下所示:


程序清单19:


public class Test19 {
    public static void main(String[] args) {
        String str1 = "{abcd}}";
        String str2 = "}";
        System.out.println(str1.lastIndexOf(str2));
        System.out.println(str1.indexOf(str2));
    }
}


输出结果:


02272ec2a0e045b78fd2728c1689db17.png


拓展:indexOf( ) 方法 和 lastIndexOf( ) 方法中可以添加第二个整型参数 : ( fromIndex ),表示从某个位置开始找,感兴趣的小伙伴可以自己测试一下。


4. startsWith( ) 方法


字符串通过调用 startsWith( ) 方法,来判断主串的头部是否是我们要输入的数据。传参的时候可以是一个参数,也可以是两个参数。第一个参数可以是字符,也可以是字符串;第二个参数表示的是偏移量,旨在通过某个位置开始判断。


代码如下:


程序清单20:


public class Test20 {
    public static void main(String[] args) {
        String str1 = "abcdef";
        System.out.println(str1.startsWith("a"));
        System.out.println(str1.startsWith("bcd"));
        System.out.println(str1.startsWith("c",2));
        //表示从偏移位置2 往后判断
    }
}


输出结果:


9d00188b259346a3b9eb6c344d326936.png


5. endsWith( ) 方法


startsWith( ) 方法是从头部开始判断,而 endsWith( ) 方法是从尾部开始判断,思想是相同的,但是 endsWith( ) 方法 在传参的时候,只能传一个参数,即我们要判断的字符或字符串。


程序清单21:

public class Test21 {
    public static void main(String[] args) {
        String str1 = "abcdef";
        String str2 = "cde";
        System.out.println(str1.endsWith("f"));
        System.out.println(str1.endsWith("bcd"));
    }
}


输出结果:


58949e2c240a4b8a8b54daab4c59eea8.png


七、字符串替换



1. replace( ) 方法


我们看到下图 replace( ) 方法,在底层的代码,当替换成功的时候,返回的是一个新创建的对象,而需要替换的字符的内容和输入字符的内容相同的时候,说明无须替换,那么这个时候,就返回原来的引用。


这就说明一个问题,在上文中的目录,我提到了一个关键点:字符串的内容不可变,也就是说,当我们表面上好像改变了原先字符串的数据,但实际上底层操作的时候,为我们 new 了一个新的对象,所以说我们看到被替换后的字符串,实际意义上是一个完完整整的一个新串,与原先的字符串没有半点关系。


9c16b54769cd4bcf924b0404792b1c99.png


程序清单22:


public class Test22 {
    public static void main(String[] args) {
        String str = "abcabeabdabxy";
        String ret1 = str.replace('a','z');
        String ret2 = str.replace("ab","zz");
        String ret3 = str.replace("ab","zzz");
        String ret4 = str.replaceFirst("ab","zz");
        System.out.println(ret1);
        System.out.println(ret2);
        System.out.println(ret3);
        System.out.println(ret4);
    }
}


输出结果:


1b875db52bf5476688f051e50f8b2d1e.png


八、字符串拆分



1. split( ) 方法


我们看到 split( ) 方法底层代码的返回类型是 String[ ] 类型,即一个字符串数组的类型。


ef3966b5d0d149dcb47d6981a014681f.png


程序清单23:


public class Test23 {
    public static void main(String[] args) {
        String str = "name=Jack&age=23";
        String[] strings = str.split("&");
        for (String s:strings) {
            System.out.println(s);
        }
    }
}


输出结果:


6eb2d7424e5545ff9cd4fed25da6bb4c.png


在我们通过 IDEA 编译器调试之后,我们可以发现字符串数组中下标 0 和 1 放了两个字符串。


f3fe1b83689f443f8657bda4de47ef43.png


我们需要注意的是:如果我们单纯地调用 split( ) 方法,只会分割一次,因为底层代码就是这么实现的,在后面我会提到多次分割。


程序清单24:


public class Test24 {
    public static void main(String[] args) {
        String str = "name=Jack&age=23";
        String[] strings = str.split("=", 2);
        for (String s:strings) {
            System.out.println(s);
        }
    }
}


输出结果:


74fc4b3cebbf40ab8b41a150b6fdc49e.png


这里我们需要注意下面的代码:在 split( “=”, 2 ) 中的参数2,代表的是按 " = " 分割成两组,而不是均匀分割成两组。


String[] strings = str.split("=", 2);


2. 使用 split( ) 方法 进行二次分割


程序清单25:


public class Test25 {
    public static void main(String[] args) {
        String str = "name=Jack&age=23";
        String[] strings1 = str.split("&");
        for (String s:strings1) {
            String[] strings2 = s.split("=");
            for (String ss: strings2) {
                System.out.println(ss);
            }
        }
    }
}


输出结果:


51eddcbd238a45669f9c29a8a934a108.png


在这里我就不进行调试了,调试的过程中是动态的,感兴趣的小伙伴可以自己她是过后观察。


3. 字符串拆分的特殊情况


对于一些转义字符的特殊情况,我们在给 split( ) 方法传参的时候,需要考虑进去。比方说:在程序清单26中,我们通过字符 " . " 来分割字符串,然而我们不能直接就这么传参了,我们需要通过 ’ \ ’ 转义才能达到我们的目的。


此外,字符 " | ‘’," * " , " + " 等等都得加上转义字符。


程序清单26:


public class Test26 {
    public static void main(String[] args) {
        String str = "192.168.1.1" ;
        String[] strings = str.split("\\.") ;
        for(String s: strings) {
            System.out.println(s);
        }
    }
}

输出结果:


b6df67541c754f6fb6e72a8bb8e03d1a.png


4. 按连接符实现多次拆分


在程序清单26中,我们可以通过连接符 ’ | ’ 实现不同分隔符之间的拆分。


程序清单26:


public class Test26 {
    public static void main(String[] args) {
        String str = "name=Jack&age=23";
        String[] strings = str.split("=|&|=");
        for (String s:strings) {
            System.out.println(s);
        }
    }
}


输出结果:


2400685b1d764c9ba84b667dfb76d099.png


九、字符串截取



1. subString( ) 方法


subString( ) 方法的底层代码,可以看到其返回类型是 String 类型。

并且,返回时创建了新的对象。


4ab9a77a45964f52a8f29a8385ee3122.png


我们先来展示程序清单27,再来分析代码是如何实现的。


程序清单27:


public class Test27 {
    public static void main(String[] args) {
        String str1 = "abcdef";
        String str2 = str1.substring(2);
        String str3 = str1.substring(0);
        String str4 = str1.substring(2,5);
        System.out.println(str2);
        System.out.println(str3);
        System.out.println(str4);
    }
}


输出结果:


8645a92cad87468d937a78312466bd3e.png


分析程序清单27的代码:


输出 str2 :是从字符串下标索引为2的字符开始往后截取(包括2下标),底层代码 new 了一个新对象。


输出 str3 :字符串下标索引为0的字符开始往后截取,等于拿到了原先的对象,这时候并没有重新创建对象。


输出 str4 :subString( ) 方法遵循左闭右开,左边下标索引为2可以取到字符,右边下标索引为5取不到字符。同时,底层代码 new 了一个新对象。


十、其他的一些字符串操作


1. trim( ) 方法


trim( ) 方法 用来除去字符串两边的空格,代码如下:


程序清单28:


public class Test28 {
    public static void main(String[] args) {
        String str1 = "    abc  xyz    ";
        System.out.print(str1);
        System.out.println("------------");
        String str2 = str1.trim();
        System.out.print(str2);
        System.out.println("------------");
    }
}


输出结果:


27e77cd517e34b51a0fbade91893b52d.png


2. 将字符串中的字符转换成大写 / 小写 [ 常用 ]


toUpperCase( ) 方法 和 toLowerCase( ) 方法


程序清单29:


public class Test29 {
    public static void main(String[] args) {
        String str1 = "abcdWXYZ123";
        String str2 = str1.toUpperCase();
        String str3 = str1.toLowerCase();
        System.out.println(str2);
        System.out.println(str3);
    }
}


输出结果:


343e8acb147041a78532d3bf7d94b0a5.png


3. 拼接字符串


程序清单30:


public class Test30 {
    public static void main(String[] args) {
        String str1 = "abcd";
        String str2 = "wxyz";
        String str3 = str1.concat(str2);
        System.out.println(str3);
    }
}


输出结果:


e7185b17f6874311bee98e726ab30843.png


4. 求字符串长度 [ 常用 ]


求字符串长度并不难,而我想说的是,这里需要区分:字符串求长度的格式 str.length( ) 和 数组求长度的个数的格式 arr.length。注意点:一个有括号,一个没括号。


程序清单31:


public class Test31 {
    public static void main(String[] args) {
        String str = "abcde";
        System.out.println(str.length());
        int[] arr = {1,2,3,4,5};
        System.out.println(arr.length);
    }
}


输出结果:


2402447289684232b9a0692c5afe449f.png


5. 判断字符串是否为空


程序清单32:


public class Test32 {
    public static void main(String[] args) {
        String str1 = "abcd";
        String str2 = " ";
        String str3 = "";
        System.out.println(str1.isEmpty());
        System.out.println(str2.isEmpty());
        System.out.println(str3.isEmpty());
    }
}


输出结果:


6902896337e94f21b6b19569241c1b53.png


十一、理解引用



程序清单33:


public class Test {
    public void change(String s, char[] ch){
        s = "world";
        ch[0] = 'g';
    }
    String str = new String("hello");
    char[] chars = {'a', 'b', 'c'};
    public static void main(String args[]){
        Test test = new Test();
        test.change(test.str, test.chars);
        System.out.println(test.str);
        System.out.println(test.chars);
    }
}


输出结果:


4cf154151e844f0d9d68306ae6ce5a31.png


这一题在我第一次做的时候,自己认为输出结果会是下面代码:


//错误答案
world
gbc


后来,我才想明白,当我们传参的时候,传了一个是 String 类型,一个是 char[ ] 类型,这两者在传参的时候传的其实都是引用类型。(这其实就是实参和形参之间对应的关系,通过改变形参并不影响传入的实参状态


说白了,就是把对象的地址传过去了。那么在 change( ) 方法接收的时候,等于拿到了两者的地址。后来,我们改变了引用变量 s ,让它指向了 " world “,那么就不再指向原先的” hello ",所以最终引用 s 与主函数中的引用 str 没有半毛钱关系。而引用 ch 就不同了,它是通过 ch[0] 直接访问到原先数组,通过改变数组下标为 0 的元素值,这会破坏原先数组的结构,所以最终数组元素变成了 [ gbc ]。 下图辅助理解:


2c29c02ccbc14338b51141fcd237d802.png


十二、StringBuilder 类 和 StringBuffer 类



我们查看底层代码,发现 StringBuilder 类 在底层实现的时候,调用了 append( ) 方法,在 append( )方法中返回的是原先的对象,并没有重新 new 一个新的对象。append( ) 方法在底层代码实现的作用就是:可以拼接字符串,然后一次一次地将字符串放入原先的对象中。


a1095a64039845af9527cb4436e8e272.png587d2ebd0f7848b3b63a0235837215b9.png546813e90e994160a022936050600b18.png


我们查看底层代码,发现 StringBuffer 类 在底层实现的时候,和上面的 StringBuilder 类很相似,唯一不同的就是 StringBuffer 类多了一个限定符 synchronized,这表示 StringBuffer 采用了同步处理,属于线程安全操作,会使程序执行起来更加安全,当然也会使程序执行起来相对 StringBuilder 于更慢一点。


de8ac7f820b849968adc924412df6aa0.pngb532f6df08fc445d915ee1ebf5dfbabe.pngb9eb8909521f49d29b7575984945fdad.png


在介绍过 StringBuilder 类 和 StringBuffer 类 两者对应的底层代码之后,因为两者很多地方是相似的,那么我直接以 StringBuilder 类 来演示一些操作字符串的方法。


1. append( ) 方法


append( ) 方法可以用来拼接很多不同类型的变量放入一个 StringBuilder 类的变量中,我们先来看一下编译器中 append( ) 方法,可以拼接的东西应有尽有!


9b495c46c32341678919a68df90b3b78.png


程序清单34:


public class Test34 {
    public static void main(String[] args) {
        StringBuilder str = new StringBuilder("abc");
        System.out.println(str);
        System.out.println("----------------");
        str.append("opq");
        System.out.println(str);
        System.out.println("----------------");
        str.append("xyz");
        System.out.println(str);
    }
}


输出结果:


47a72cac9a19461689335a4b64d5613d.png


在程序清单34中,我们可以发现 append( ) 方法在放字符串的时候,每次都放入了 str 中,那么实际上被改变的就是原先对象中的字符串内容。


2. 字符串逆置


程序清单35:


public class Test35 {
    public static void main(String[] args) {
        StringBuilder str = new StringBuilder("abcde");
        System.out.println(str);
        System.out.println(str.reverse());
    }
}


输出结果:


7b2786616ec849b8a294c8efa9744d77.png



3. StringBuilder 类 和 StringBuffer 类 的其他一些方法



在程序清单36中,通过 delete( ) 方法,我们删除对应索引下标的字符,遵循左闭右开原则。


程序清单36:


public class Test36 {
    public static void main(String[] args) {
        StringBuffer str = new StringBuffer("abcdefg");
        System.out.println(str);
        System.out.println(str.delete(1,4));
    }
}


输出结果:




在程序清单37中,通过 insert( ) 方法,我们在对应的索引下标处添加对应的数据。


程序清单37:


public class Test37 {
    public static void main(String[] args) {
        StringBuffer str = new StringBuffer("abcde");
        System.out.println(str);
        System.out.println(str.insert(0, "你好"));
        System.out.println(str.insert(2, "hello"));
    }
}


输出结果:


297bf1360f274996a84e27932153cff4.png


4. String、StringBuffer、StringBuilder 三者的区别


① String的内容不可修改,StringBuffer与StringBuilder的内容可以修改

② StringBuffer与StringBuilder大部分功能是相似的

③ StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作


5. String 和 StringBuffer、StringBuilder 之间的转换


在这里,我们需要理解一点:String 表示 String 类,而 StringBuilder 是另一种类,StringBuffer 同样也是另一种类,这三者性质不同,但是可以互相转换。


在程序清单38中,我演示了两种转换形式:


① StringBuilder / StringBuffer 类 =》String 类
调用 toString( ) 方法
② String 类 =》StringBuilder / StringBuffer 类
利用 StringBuilder / StringBuffer 的构造方法


程序清单38:


public class Test {
    public static String transform1(){
        StringBuilder strb = new StringBuilder();
        strb.append(111);
        strb.append("hello");
        return strb.toString();
        //return strb; //error
    }
    public static StringBuilder transform2(){
        String str = "world";
        return new StringBuilder(str);
        //return str; //error
    }
    public static void main(String[] args) {
        System.out.println(transform1());
        System.out.println(transform2());
    }
}


输出结果:


9d19e3a8b7bf463daa8e114c27e59d9b.png


十三、其他类型与字符串类型之间的转换



1. 整型转字符串


程序清单39:


public class Test {
    public static String transform1(){
        return Integer.toString(123);
    }
    public static String transform2(){
        return Character.toString('c');
    }
    public static void main(String[] args) {
        System.out.println(transform1());
        System.out.println(transform2());
    }
}


输出结果:


b64e91c875d54051bae80ba95fc46e32.png


在上面,我演示了 toString( ) 方法


当然,我们也可以使用 String.valueOf ( ) 方法,此外,我们可以进行转换的类型有很多很多。


image.png


2. 字符串转整型


在下面的程序中,我先将整个字符串分割为一个数组元素中的三个元素,我将数组的第一个元素转换成了整数,使用了【 Integer.parseInt() 】方法,后两个元素直接赋值给了字符串。后来我又将刚刚数组的第一个元素转换成了字符串,通过给其加上一个空字符即可。


程序清单40:


public class Test {
    public static void main(String[] args) {
        String str = "123;opq;xyz";
        String[] result = str.split(";");
        for (String s : result) {
            System.out.print(s +" ");
        }
        int a = Integer.parseInt(result[0]);
        String b = result[1];
        String c = result[2];
        System.out.print("\n"+a +" ");
        System.out.print(b +" ");
        System.out.print(c +" \n");
        System.out.println("------------------");
        String str2 = result[0] + "";
        System.out.println(str2);
    }
}


输出结果:


6942a7be8ac141f7ad2ed207dfaf888e.png

目录
相关文章
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
40 0
java基础(13)String类
|
4月前
|
API 索引
String类下常用API
String类下常用API
44 1
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
58 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
34 2
|
3月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
56 4
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
25 2
|
2月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
37 4
|
2月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
68 2