3.1. 字符串

3.1.1. 使用API文档

使用Java SE的API文档。Java官方提供了Java 8在线API文档,网址 是http://docs.oracle.com/javase/8/docs/api/

3.1.2. Java 8在线API文档

提示 很多读者希望能够有离线的中文Java API文档,但Java官方只提供了Java 6的中文API文档,该文件下载地址是http://download.oracle.com/technetwork/java/javase/6/docs/zh/api.zip,下载完成后解压api.zip文件,找到其中的index.html文件,双击就会在浏览器中打开API文档了。

image0

类窗口页面其他内容

compareTo方法详细描述 查询API的一般流程是:找包→找类或接口→查看类或接口→找方法或变量。读者可以尝试查找一下String、StringBuffer和StringBuilder这些字符串类的API文档,熟悉一下这些类的用法。

image1

3.1.3. 不可变字符串

String

创建String对象可以通过构造方法实现,常用的构造方法:

- String():使用空字符串创建并初始化一个新的String对象。

- String(String original):使用另外一个字符串创建并初始化一个新的 String 对象。

- String(StringBuffer buffer):使用可变字符串对象(StringBuffer)创建并初始化一个新的 String对象。

- String(StringBuilder builder):使用可变字符串对象(StringBuilder)创建并初始化一个新的String 对象。

- String(byte[] bytes):使用平台的默认字符集解码指定的byte数组,通过byte数组创建并初始化一个新的 String 对象。

- String(char[] value):通过字符数组创建并初始化一个新的 String 对象。

- String(char[] value, int offset, int count):通过字符数组的子数组创建并初始化一个新的 String 对象;offset参数是子数组第一个字符的索引,count参数指定子数组的长度。

创建字符串对象的示例代码

package com.company;

public class Main {

    public static void main(String[] args) {
        // write your code here
        // 创建字符串对象
        String s1 = new String();
        String s2 = new String("Hello World");
        String s3 = new String("\u0048\u0065\u006c\u006c\u006f\u0020\u0057\u006f\u0072\u006c\u0064");
        System.out.println("s2 = " + s2);       //s2 = Hello World
        System.out.println("s3 = " + s3);       //s3 = Hello World

        char chars[] = {'a', 'b', 'c', 'd', 'e'};
        // 通过字符数组创建字符串对象
        String s4 = new String(chars);
        // 通过子字符数组创建字符串对象
        String s5 = new String(chars, 1, 4);
        System.out.println("s4 = " + s4);           //s4 = abcde
        System.out.println("s5 = " + s5);           //s5 = bcde

        byte bytes[] = { 97, 98, 99 };
        // 通过byte数组创建字符串对象
        String s6 = new String(bytes);
        System.out.println("s6 = " + s6);       //s6 = abc
        System.out.println("s6字符串长度 = " + s6.length());     //s6字符串长度 = 3
    }
}

字符串池

使用new关键字与字符串常量都能获得字符串对象,但它们之间有一些区别。先看下面代码运行结果:

package com.company2;

public class Main {

    public static void main(String[] args) {
        String s7 = new String("Hello");
        String s8 = new String("Hello");
        String s9 = "Hello";
        String s10 = "Hello";
        System.out.printf("s7 == s8 : %b%n", s7 == s8);     //s7 == s8 : false
        System.out.printf("s9 == s10: %b%n", s9 == s10);    //s9 == s10: true
        System.out.printf("s7 == s9 : %b%n", s7 == s9);     //s7 == s9 : false
        System.out.printf("s8 == s9 : %b%n", s8 == s9);     //s8 == s9 : false
    }
}

image2

s10与s9是相同的引用,指向同一个对象,s7和s8是不同的引用,指向不同的对象。

字符串拼接

String字符串虽然是不可变字符串,但也可以进行拼接只是会产生一个新的对象。String字符串拼接可以使用+运算符或String的concat(String str)方法。

+运算符优势是可以连接任何类型数据拼接成为字符串,而concat方法只能拼接String类型字符串。

字符串拼接示例如下:

package com.zhijiekeji01;

public class Main {
    public static void main(String[] args) {
        String s1 = "Hello";
        // 使用+运算符连接
        String s2 = s1 + " ";
        String s3 = s2 + "World";
        System.out.println(s3);             //Hello World

        String s4 = "Hello";
        // 使用+运算符连接,支持+=赋值运算符
        s4 += " ";
        s4 += "World";
        System.out.println(s4);             //Hello World

        String s5 = "Hello";
        // 使用concat方法连接
        s5 = s5.concat(" ").concat("World");
        System.out.println(s5);             //Hello World

        int age = 18;
        String s6 = "她的年龄是" + age + "岁。";       //她的年龄是18岁。
        System.out.println(s6);
        char score = 'A';
        String s7 = "她的英语成绩是" + score;
        System.out.println(s7);                 //她的英语成绩是A

        java.util.Date now = new java.util.Date();
        //对象拼接自动调用toString()方法
        String s8 = "今天是:" + now;
        System.out.println(s8);                 //今天是:Mon Apr 20 19:52:32 CST 2020
    }
}

字符串查找

在给定的字符串中查找字符或字符串是比较常见的操作。在String类中提供了indexOf和lastIndexOf方法用于查找字符或字符串,返回值是查找的字符或字符串所在的位置,-1表示没有找到。这两个方法有多个重载版本:

- int indexOf(int ch):从前往后搜索字符ch,返回第一次找到字符ch所在处的索引。

- int indexOf(int ch, int fromIndex):从指定的索引开始从前往后搜索字符ch,返回第一次找到字符ch所在处的索引。

-int indexOf(String str):从前往后搜索字符串str,返回第一次找到字符串所在处的索引。

-int indexOf(String str, int fromIndex):从指定的索引开始从前往后搜索字符串str,返回第一次找到字符串所在处的索引。

-int lastIndexOf(int ch):从后往前搜索字符ch,返回第一次找到字符ch所在处的索引。

-int lastIndexOf(int ch, int fromIndex):从指定的索引开始从后往前搜索字符ch,返回第一次找到字符ch所在处的索引。

-int lastIndexOf(String str):从后往前搜索字符串str,返回第一次找到字符串所在处的索引。

-int lastIndexOf(String str, int fromIndex):从指定的索引开始从后往前搜索字符串str,返回第一次找到字符串所在处的索引。

提示 字符串本质上是字符数组,因此它也有索引,索引从零开始。String的charAt(int index)方法可以返回索引index所在位置的字符。

字符串查找示例代码如下:

package com.zhijiekeji02;

public class Main {
    public static void main(String[] args) {
        String sourceStr = "There is a string accessing example.";
        //获得字符串长度
        int len = sourceStr.length();
        //获得索引位置16的字符
        char ch = sourceStr.charAt(16);
        //查找字符和子字符串
        int firstChar1 = sourceStr.indexOf('r');
        int lastChar1 = sourceStr.lastIndexOf('r');
        int firstStr1 = sourceStr.indexOf("ing");
        int lastStr1 = sourceStr.lastIndexOf("ing");
        int firstChar2 = sourceStr.indexOf('e', 15);
        int lastChar2 = sourceStr.lastIndexOf('e', 15);
        int firstStr2 = sourceStr.indexOf("ing", 5);
        int lastStr2 = sourceStr.lastIndexOf("ing", 5);

        System.out.println("原始字符串:" + sourceStr);
        System.out.println("字符串长度:" + len);
        System.out.println("索引16的字符:" + ch);
        System.out.println("从前往后搜索r字符,第一次找到它所在索引:" + firstChar1);
        System.out.println("从后往前搜索r字符,第一次找到它所在索引:" + lastChar1);
        System.out.println("从前往后搜索ing字符串,第一次找到它所在索引:" + firstStr1);
        System.out.println("从后往前搜索ing字符串,第一次找到它所在索引:" + lastStr1);
        System.out.println("从索引为15位置开始,从前往后搜索e字符,第一次找到它所在索引:" + firstChar2);
        System.out.println("从索引为15位置开始,从后往前搜索e字符,第一次找到它所在索引:" + lastChar2);
        System.out.println("从索引为5位置开始,从前往后搜索ing字符串,第一次找到它所在索引:" + firstStr2);
        System.out.println("从索引为5位置开始,从后往前搜索ing字符串,第一次找到它所在索引:" + lastStr2);
    }
}


/*
原始字符串:There is a string accessing example.
        字符串长度:36
        索引16的字符:g
        从前往后搜索r字符,第一次找到它所在索引:3
        从后往前搜索r字符,第一次找到它所在索引:13
        从前往后搜索ing字符串,第一次找到它所在索引:14
        从后往前搜索ing字符串,第一次找到它所在索引:24
        从索引为15位置开始,从前往后搜索e字符,第一次找到它所在索引:21
        从索引为15位置开始,从后往前搜索e字符,第一次找到它所在索引:4
        从索引为5位置开始,从前往后搜索ing字符串,第一次找到它所在索引:14
        从索引为5位置开始,从后往前搜索ing字符串,第一次找到它所在索引:-1*/

字符串比较

字符串比较是常见的操作,包括比较相等、比较大小、比较前缀和后缀等。 1. 比较相等 String提供的比较字符串相等的方法:

  • boolean equals(Object anObject):比较两个字符串中内容是否相等。
  • boolean equalsIgnoreCase(String anotherString):类似equals方法,只是忽略大小写。
  1. 比较大小 有时不仅需要知道是否相等,还要知道大小,String提供的比较大小的方法:
    • int compareTo(String anotherString):按字典顺序比较两个字符串。如果参数字符串等于此字符串,则返回值 0;如果此字符串小于字符串参数,则返回一个小于 0 的值;如果此字符串大于字符串参数,则返回一个大于 0 的值。
    • int compareToIgnoreCase(String str):类似compareTo,只是忽略大小写。
  2. 比较前缀和后缀
    • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束。
    • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始

字符串比较示例代码如下:

package com.zhijiekeji03;

public class Main {
    public static void main(String[] args) {
        String s1 = new String("Hello");
        String s2 = new String("Hello");

        // 比较字符串是否是相同的引用
        System.out.println("s1 == s2 : " + (s1 == s2));
        // 比较字符串内容是否相等
        System.out.println("s1.equals(s2) : " + (s1.equals(s2)));

        String s3 = "HELlo";
        // 忽略大小写比较字符串内容是否相等
        System.out.println("s1.equalsIgnoreCase(s3) : " + (s1.equalsIgnoreCase(s3)));

        // 比较大小
        String s4 = "java";
        String s5 = "Swift";
        // 比较字符串大小 s4 > s5
        System.out.println("s4.compareTo(s5) : " + (s4.compareTo(s5)));
        // 忽略大小写比较字符串大小 s4 < s5
        System.out.println("s4.compareToIgnoreCase(s5) : " + (s4.compareToIgnoreCase(s5)));

        // 判断文件夹中文件名
        String[] docFolder = {"java.docx", " JavaBean.docx", "Objecitve-C.xlsx", "Swift.docx "};
        int wordDocCount = 0;
        // 查找文件夹中Word文档个数
        for (String doc : docFolder) {
            // 去的前后空格
            doc = doc.trim();
            // 比较后缀是否有.docx字符串
            if (doc.endsWith(".docx")) {
                wordDocCount++;
            }
        }
        System.out.println("文件夹中Word文档个数是: " + wordDocCount);

        int javaDocCount = 0;
        // 查找文件夹中Java相关文档个数
        for (String doc : docFolder) {
            // 去的前后空格
            doc = doc.trim();
            // 全部字符转成小写
            doc = doc.toLowerCase();
            // 比较前缀是否有java字符串
            if (doc.startsWith("java")) {
                javaDocCount++;
            }
        }
        System.out.println("文件夹中Java相关文档个数是:" + javaDocCount);
    }
}

/*      s1 == s2 : false
        s1.equals(s2) : true
        s1.equalsIgnoreCase(s3) : true
        s4.compareTo(s5) : 23
        s4.compareToIgnoreCase(s5) : -9
        文件夹中Word文档个数是: 3
        文件夹中Java相关文档个数是:2*/

字符串截取

Java中字符串String截取方法主要的方法如下:

  • String substring(int beginIndex):从指定索引beginIndex开始截取一直到字符串结束的子字符串。
  • String substring(int beginIndex, int endIndex):从指定索引beginIndex开始截取直到索引endIndex -1处的字符,注意包括索引为beginIndex处的字符,但不包括索引为endIndex处的字符。

字符串截取方法示例代码如下:

package com.zhijiekeji04;

public class Main {
    public static void main(String[] args) {
        String sourceStr = "There is a string accessing example.";
        // 截取example.子字符串
        String subStr1 = sourceStr.substring(28);

        // 截取string子字符串
        String subStr2 = sourceStr.substring(11, 17);
        System.out.printf("subStr1 = %s%n", subStr1);
        System.out.printf("subStr2 = %s%n", subStr2);

        // 使用split方法分割字符串
        System.out.println("-----使用split方法-----");
        String[] array = sourceStr.split(" ");
        for (String str : array) {
            System.out.println(str);
        }
    }
}

/*subStr1 = example.
subStr2 = string
-----使用split方法-----
There
is
a
string
accessing
example.*/

3.1.4. 可变字符串

可变字符串在追加、删除、修改、插入和拼接等操作不会产生新的对象。

StringBuffer和StringBuilder

Java提供了两个可变字符串类StringBuffer和StringBuilder,中文翻译为“字符串缓冲区”。

StringBuffer是线程安全的,它的方法是支持线程同步 ,线程同步会操作串行顺序执行,在单线程环境下会影响效率。

StringBuilder是StringBuffer单线程版本,Java 5之后发布的,它不是线程安全的,但它的执行效率很高。

StringBuffer和StringBuilder具有完全相同的API,即构造方法和普通方法等内容一样。StringBuilder的中构造方法有4个:

- StringBuilder():创建字符串内容是空的StringBuilder对象,初始容量默认为16个字符。

- StringBuilder(CharSequence seq):指定CharSequence字符串创建StringBuilder对象。CharSequence接口类型,它的实现类有:String、StringBuffer和StringBuilder等,所以参数seq可以是String、StringBuffer和StringBuilder等类型。

- StringBuilder(int capacity):创建字符串内容是空的StringBuilder对象,初始容量由参数capacity指定的。

- StringBuilder(String str):指定String字符串创建StringBuilder对象。

上述构造方法同样适合于StringBuffer类,这里不再赘述。 提示 字符串长度和字符串缓冲区容量区别。

字符串长度是指在字符串缓冲区中目前所包含字符串长度,通过length()获得;字符串缓冲区容量是缓冲区中所能容纳的最大字符数,通过capacity()获得。当所容纳的字符超过这个长度时,字符串缓冲区自动扩充容量,但这是以牺牲性能为代价的扩容。

package com.zhijikeji05;

public class Main {
    public static void main(String[] args) {
        // 字符串长度length和字符串缓冲区容量capacity
        StringBuilder sbuilder1 = new StringBuilder();
        System.out.println("包含的字符串长度:" + sbuilder1.length());
        System.out.println("字符串缓冲区容量:" + sbuilder1.capacity());
        StringBuilder sbuilder2 = new StringBuilder("Hello");
        System.out.println("包含的字符串长度:" + sbuilder2.length());
        System.out.println("字符串缓冲区容量:" + sbuilder2.capacity());
        // 字符串缓冲区初始容量是16,超过之后会扩容
        StringBuilder sbuilder3 = new StringBuilder();
        for (int i = 0; i < 17; i++) {
            sbuilder3.append(8);
        }
        System.out.println("包含的字符串长度:" + sbuilder3.length());
        System.out.println("字符串缓冲区容量:" + sbuilder3.capacity());
    }
}

/*
包含的字符串长度:0
字符串缓冲区容量:16
包含的字符串长度:5
字符串缓冲区容量:21
包含的字符串长度:17
字符串缓冲区容量:34*/

字符串追加

StringBuilder在提供了很多修改字符串缓冲区的方法,追加、插入、删除和替换等,这一节先介绍字符串追加方法。字符串追加方法是append,append有很多重载方法,可以追加任何类型数据,它的返回值还是StringBuilder。StringBuilder的追加法与StringBuffer完全一样。

字符串追加示例代码如下:

package com.zhijiekeji06;

public class Main {
    public static void main(String[] args) {
        //添加字符串、字符
        StringBuilder sbuilder1 = new StringBuilder("Hello");
        sbuilder1.append(" ").append("World");
        sbuilder1.append('.');
        System.out.println(sbuilder1);

        StringBuilder sbuilder2 = new StringBuilder();
        Object obj = null;
        //添加布尔值、转义符和空对象
        sbuilder2.append(false).append('\t').append(obj);
        System.out.println(sbuilder2);


        //添加数值
        StringBuilder sbuilder3 = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            sbuilder3.append(i);
        }
        System.out.println(sbuilder3);
    }
}

/*
Hello World.
false   null
0123456789*/

字符串插入、删除和替换

StringBuilder中实现插入、删除和替换等操作的常用方法说明如下:

- StringBuilder insert(int offset, String str):在字符串缓冲区中索引为offset的字符位置之前插入str,insert有很多重载方法,可以插入任何类型数据。

- StringBuffer delete(int start, int end):在字符串缓冲区中删除子字符串,要删除的子字符串从指定索引start开始直到索引end - 1处的字符。start和end两个参数与substring(int beginIndex, intendIndex)方法中的两个参数含义一样。

- StringBuffer replace(int start, int end, String str)字符串缓冲区中用str替换子字符串,子字符串从指定索引start开始直到索引end - 1处的字符。start和end同delete(int start, int end)方法。

以上介绍的方法虽然是StringBuilder方法,但StringBuffer也完全一样,

示例代码如下:

package com.zhijiekeji07;

public class Main {
    public static void main(String[] args) {
        // 原始不可变字符串
        String str1 = "Java C";
        // 从不可变的字符创建可变字符串对象
        StringBuilder mstr = new StringBuilder(str1);
        // 插入字符串
        mstr.insert(4, " C++");
        System.out.println(mstr);

        // 具有追加效果的插入字符串
        mstr.insert(mstr.length(), " Objective-C");
        System.out.println(mstr);

        // 追加字符串
        mstr.append(" and Swift");
        System.out.println(mstr);

        // 删除字符串
        mstr.delete(11, 23);
        System.out.println(mstr);
    }
}

/*
Java C++ C
Java C++ C Objective-C
Java C++ C Objective-C and Swift
Java C++ C and Swift*/

3.1.5. 总结

Java字符串类分为:

可变字符串类(String)和不可变字符串类(StringBuilder和StringBuffer)。

然后分别介绍了这些字符串类的用法。

3.1.6. 日期和时间字符串格式化

1.常用的日期格式化转换符

image3

import java.util.*;

public class Eval { // 新建类
    public static void main(String[] args) { // 主方法
        Date date = new Date(); // 创建Date对象date
        String year = String.format("%tY", date); // 将date进行格式化
        String month = String.format("%tB", date);
        String day = String.format("%td", date);
        System.out.println("今年是:" + year + "年"); // 输出信息
        System.out.println("现在是:" + month);
        System.out.println("今天是:" + day + "号");
    }
}

2.常用的时间格式化转换符

image4

import java.util.*;

public class GetDate { // 新建类
    public static void main(String[] args) { // 主方法
        Date date = new Date(); // 创建Date对象date
        String hour = String.format("%tH", date); // 将date进行格式化
        String minute = String.format("%tM", date);
        String second = String.format("%tS", date);
         // 输出的信息
        System.out.println("现在是:" + hour + "时" + minute + "分"
                + second + "秒");
    }
}

3.格式化常见的日期时间组合

image5

import java.util.*;

public class DateAndTime { // 创建类
    public static void main(String[] args) { // 主方法
        Date date = new Date(); // 创建Date对象date
        String time = String.format("%tc", date); // 将date格式化
        String form = String.format("%tF", date);
         // 将格式化后的日期时间输出
        System.out.println("全部的时间信息是:" + time);
        System.out.println("年-月-日格式:" + form);
    }
}

3.1.7. 常规类型的格式化

常规类型的格式化可以用于任何的参数类型

image6

public class General { // 新建类
    public static void main(String[] args) { // 主方法
        String str = String.format("%d", 400 / 2); // 将结果以十进制显示
        String str2 = String.format("%b", 3 > 5); // 将结果以布尔形式显示
        String str3 = String.format("%x", 200); // 将结果以十六进制格式显示
        System.out.println("400的一半是:" + str); // 输出格式化字符串
        System.out.println("3>5正确吗:" + str2);
        System.out.println("200的十六进制数是:" + str3);
    }
}

/*
400的一半是:200
3>5正确吗:false
200的十六进制数是:c8
 */

3.1.8. 使用正则表达式

正则表达式的元字符

image7

正则表达式的限定修饰符

image8

public class Judge {
    public static void main(String[] args) {
         // 定义要匹配E_mail地址的正则表达式
        String regex = "\\w+@\\w+(\\.\\w{2,3})*\\.\\w{2,3}";
        String str1 = "aaa@"; // 定义要进行验证的字符串
        String str2 = "aaaaa";
        String str3 = "1111@111ffyu.dfg.com";
        if (str1.matches(regex)) { // 判断字符串变量是否与正则表达式匹配
            System.out.println(str1 + "是一个合法的E-mail地址格式");
        }
        if (str2.matches(regex)) {
            System.out.println(str2 + "是一个合法的E-mail地址格式");
        }
        if (str3.matches(regex)) {
            System.out.println(str3 + "是一个合法的E-mail地址格式");
        }
    }
}

3.1.9. 字符串生成器

public class Jerque { // 新建类
    public static void main(String[] args) { // 主方法
        String str = ""; // 创建空字符串
        // 定义对字符串执行操作的起始时间
        long starTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) { // 利用for循环执行10000次操作
            str = str + i; // 循环追加字符串
        }
        long endTime = System.currentTimeMillis(); // 定义对字符串操作后的时间
        long time = endTime - starTime; // 计算对字符串执行操作的时间
        System.out.println("Sting消耗时间:" + time); // 将执行的时间输出

        StringBuilder builder = new StringBuilder(""); // 创建字符串生成器
        starTime = System.currentTimeMillis(); // 定义操作执行前的时间
        for (int j = 0; j < 10000; j++) { // 利用for循环进行操作
            builder.append(j); // 循环追加字符
        }
        endTime = System.currentTimeMillis(); // 定义操作后的时间
        time = endTime - starTime; // 追加操作执行的时间
        System.out.println("StringBuilder消耗时间:" + time); // 将操作时间输出
    }
}

/*
Sting消耗时间:465
StringBuilder消耗时间:1
 */