字符序列

chapter 14 : 字符序列

14.1、字符编码

14.1.1、字符

我们已经知道,Java 语言中包括 8 种基本数据类型,其中 char 类型表示单个字符,比如:

char ch1 = 'A' ; // Java 语言中使用 单引号 来表示单个字符
char ch2 = '\u8fa3' ; // 也可以在 单引号 内部使用四位16进制形式的数字来表示单个字符(Unicode编码)
char ch3 = '\n' ; // 在 单引号 中也可以使用转义字符表示单个字符
char ch4 = 97 ; // 使用数字来表示字符 ( Unicode 编码中的 97 表示英文字符 `a` )

因此,如果需要表示多个字符,则需要使用 字符 数组来实现:

char[] chars = { '海','上','生','明','月','\n','天','涯','共','此','时' } ;

很明显,使用数组来表示一串字符是比较麻烦的,因此在 Java 语言中提供了表示多个字符组成的串的类型:

  • String 表示 字符串 ( 英文术语使用 character strings 表示 )
  • StringBuffer 表示字符缓冲区 ( 与 StringBuilder 不同的是,StringBuffer线程安全的 )
  • StringBuilder 表示字符串缓冲区 ( 与 StringBuffer 不同的是,StringBuilder不是线程安全的 )
  • CharBuffer 表示字符缓冲区 ( java.nio.CharBuffer 的实例是 为 nio 提供支持的字符缓冲区 )

它们都实现了同一个接口: java.lang.CharSequence,因此我们可以将它们称作表示 字符序列

所谓字符序列,就是多个字符按照一定顺序组成一个列表。

14.1.2、字符编码

实际上,在电子计算机中只能以 字节 形式来存储或处理数据,字符也不例外。

字符只是人类所能理解的概念,计算机处理在使用字符时必须将字符转换为字节后才能继续处理。

为便于人类与计算机交互,人们需要在字节与字符之间建立对应关系。

字节和字符之间的对应关系,就是所谓的 字符编码 (Character Encoding),比如在 ASCII 编码中

  • 字符 0 对应的数字是 48
  • 字符 9 对应的数字是 57
  • 字符 A 对应的数字是 65
  • 字符 Z 对应的数字是 90
  • 字符 a对应的数字是 97
  • 字符 z对应的数字是 122

ASCII 全称为 American Standard Code for Information Interchange,即美国信息交换标准代码。

ASCII编码是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。

它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。

ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。

在ASCII编码中,采用一个字节的低7位来表示字符,因此它至多能表示 128 个字符,其有效范围是:

0x00 ~ 0x7F ( 0b00000000 ~ 0b01111111 )

字符编码问题在计算机出现之前就存在了。
电报系统中使用的摩尔斯电码就是一种字符编码方案,发明于1835年。
由于电报传入中国,在1873年中国也设计了自己的编码方案。

  • 字符 对应的数字是 36771 (十六进制形式为 0x8FA3 )

同时每一种字 符编码方案 都规定了每个字符所占用的 字节 数目:

  • GBK 编码方案中,每个字符占用 2 个字节
  • UTF-8 编码方案中,每个字符占用 1 到 3 个字节

14.1.2.2、Unicode

因为 ASCII 编码采用单个字节来存储和处理字符,因此它所表示的字符个数非常有限。

为了表示更多的字符,各个国家和地区往往会根据当地的语言环境来制定适用于当地的字符编码方案。

为了解决传统的字符编码方案的局限性,同时也为了满足跨语言、跨平台进行文本转换、处理的要求,Unicode Consortium 制定并公布了 Unicode 编码,它是一种兼容 ASCII 编码的 字符编码方案,几乎可以表示世界上各个国家和地区出现过的所有字符,因此也被称作 统一码 、万国码 、单一码 。

Unicode的第一个版本诞生于1991年。
Unicode的最特别之处在于,打破了自电报时代起所固有的传统思路,即每个字符必须直接和一串比特序列进行对应。
在这样的直接对应关系中间增加了一个新的层次:码点(Code Point),这是一个整数。
通过 码点 实现每个字符都有唯一的一个整数与之对应。
因此,字符首先对应到码点,然后码点再和对应的二进制编码对应。

Unicode 编码为每一个字符都分配了一个唯一的数字编号,该编号一般写成16进制,在前面加上U+,比如:

  • 字在 Unicode 编码中对应的是 U+9EBB

  • 字在 Unicode 编码中对应的是 U+8FA3

在 Java 语言中,默认采用的是 Unicode 编码 (注意,它是一种兼容 ASC II 的编码规范)。

Unicode 编码于1990年开始研发,1994年正式公布,目前由 Unicode Consortium 负责管理。

比较遗憾的是,Unicode标准制定小组仅仅确定了Unicode编码规范,没有给出具体的实现方案,于是就有了为 Unicode 提供支持的实现方案:

  • UTF-8
  • UTF-16
    • Java 语言中的 char 就是采用这种编码方案 ( 每个字符占用两个字节( 16个二进制位 ) )
  • UTF-16BE
  • UTF-16LE
  • UTF-32
  • UTF-32BE
  • UTF-32LE

它们之间的差别,以及在其中使用的 BELE 的区别,这里不予研究,有兴趣的同学可以自行查询。

Unicode 编码规范的众多实现方案中,最为常用的是 UTF-8 ,这也是我们在开发中建议采用的编码方案。

Java 语言除了支持 Unicode 编码规范的众多实现方案外,还支持其它编码方案,比较常见的是:

  • GBK 简体中文对应的字符编码方案
    • GBK 即“国标”、“扩展” 汉语拼音的第一个字母
    • GBK的英文名称为 Chinese Internal Code Specification
    • 使用地区为: 中国大陆 、新加坡 、印度尼西亚 等
  • Big5 繁体中文对应的字符编码方案

    • Big5 源于 台湾地区 推广的 五大码
    • 五大码 是 1984 年台湾地区为五大中文套装软件提供的中文内码
    • 五大中文套装是指: 宏碁 、神通 、佳佳 、零壹 、大众
    • 五大码 虽然最终失败了,但它影响比较深远,现在的 Big5 就是在此基础上发展延伸而来
    • 使用地区: 香港、澳门、海外华人聚居区域
  • ISO-8859-1 西欧众多国家使用的字符编码方案

    • 向下兼容 ASC II 编码,其中的 0x00 ~ 0x7F 完全与 ASC II 编码相同
    • 0x80 ~ 0x9F 之间是控制字符,0xA0 ~ 0xFF 之间是文字符号
    • 某些环境下,也将 ISO-8859-1 称作 Latin1 ( Latin1ISO-8859-1 的别名 )
    • 使用地区: 西欧大多数国家

除此之外,Java语言还支持大量的其它编码方案,因为并不是我们所常用的,所以这里不予研究。

14.1.3、Charset

一种字符编码可以支持所有字符为一个字符集 ,即定义了字节与字符之间对应关系的所有字符的集合。

比如:

  • ASCII编码支持的所有字符被称为 ASCII字符集

从 JDK 1.4 开始,Java 语言中提供了 Charset 类来表示 字符集

Charset 类 的实例 表示16 位的 Unicode 代码单元 序列 和 字节序列 之间的指定映射关系。

14.1.3.1、获取 Charset实例

java.nio.Charset 类中声明了用于获取 Charset 实例的 类方法 :

  • 获取当前系统平台默认的字符编码方案
public static Charset defaultCharset()
  • 获取当前系统平台所支持的所有字符编码方案
public static SortedMap< String , Charset > availableCharsets()
  • 获取指定 字符编码方案名称 对应的 字符编码方案
public static Charset forName( String charsetName )

举例:

package com.itlaobing.charsequences;

import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

public class CharsetTest1 {

    public static void main(String[] args) {

        // 获取当前系统平台使用的默认编码方案
        Charset charset = Charset.defaultCharset() ;
        System.out.println( charset );

        System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );

        // 获取当前系统平台所支持的所有字符编码方案
        // Map 集合中 key 是 字符编码方案 的名称,value 则是该编码方案对应的 Charset 实例
        SortedMap<String,Charset> charsetMap = Charset.availableCharsets();

        // 获得 Map 集合中所有的 key 组成的 Set 集合
        Set<String> keySet = charsetMap.keySet();
        // 使用 for-each 循环 迭代 Set 集合
        for( String charsetName : keySet ) {
            System.out.println( charsetName );
        }

    }

}

尽管在 Charset类中还有许多其它方法,但此处主要研究 CharSequence 的几个实现类( String 、StringBuffer 、StringBuilder ),因此这里不会讲解这些方法的使用。

14.1.3.2、判断字符编码是否被支持

Charset类还提供了用于判断当前系统平台是否支持指定字符编码方案的方法:

public static boolean isSupported( String charsetName )

举例:

package com.itlaobing.charsequences;

import java.nio.charset.Charset;
import java.util.Scanner;

public class CharsetTest2 {

    public static void main(String[] args) {

        Scanner sc = new Scanner( System.in );

        System.out.println( "请输入一个字符编码方案的名称" );
        String charsetName = sc.nextLine() ;
        System.out.println( "你输入的是 " + charsetName );
        if( Charset.isSupported( charsetName ) ) {
            System.out.println( "支持 " + charsetName );
        } else {
            System.out.println( "不支持 " + charsetName );
        }

        sc.close();

    }

}

在学些过 Charset 类之后,将来在既可以使用String类型的字符编码方案的名称又可以使用 Charset类型 的情形下,建议优先使用 Charset 的实现。

比如,对于 String 类中的几个相互重载的 getBytes 方法来说:

public byte[] getBytes( Charset charset )
public byte[] getBytes( String charsetName ) throws UnsupportedEncodingException

我们编写代码时应该优先使用 getBytes(Charset) 而不是使用 getBytes( String)

切记!切记!切记!

14.2、String

因为大家已经学习过 String 类,所以这里不讲解 String 类中常用的方法,仅介绍 String 类的设计。

掌握 String 类的设计,不仅有利于建立面向对象的编程思想,也有利于对比 String 与 StringBuffer 、StringBuilder 的联系和区别。首先来看 String 类的声明:

package java.lang;

// 这里省略 import 语句

public final class String
       implements java.io.Serializable, Comparable<String>, CharSequence {
    // 这里省略 String 类中的所有代码
}

不论是 Java 9 之前,还是 Java 9 ~ Java 13 ,String 类都实现了三个接口:

  • java.io.Serializable
  • java.lang.Comparable
  • java.lang.CharSequence

同时在声明中可以看到,String 类由 final 修饰符所修饰,因此它决不肯存在子类。

14.2.1、实例变量

在上一节 字符编码 中,我们已经知道,如果需要表示多个字符组成的串,必须借助于基本数据类型来实现。

因此,在 Java 8 之前,String 类中声明了一个 char 类型的数组变量来存储它所表示的多个字符 :

private final char[] value ;

而从 Java 9 开始,String 类中声明了一个 byte 类型的数组变量来存储它所表示的多个字符 :

private final byte[] value;

所以,从来就没有什么 字符串 ,所有的所谓 字符串 最终都变成了基本数据类型的数组。

14.2.1.1、Java 9 之前 String 类中的实例变量

在 Java 9 之前,String 类中声明的的主要实例变量如下:

private final char[] value ;
private int hash ;

当创建了一个String实例后:

  • value 数组用来存储字符序列
  • hash变量来缓存该字符串的哈希值
    • 千万要注意,String 类重写了 从 Object 继承的 hashCode 方法
    • 重写后的 hashCode 方法根据其内部所存储的字符内容来重新计算哈希码值

通过 String 类的一个构造方法来体现出来实例变量 valuehash 的作用:

public String( String original ) {
   this.value = original.value;
   this.hash = original.hash;
}

当使用 一个 String 实例 作为 参数 创建另一个 Stirng 实例时:

String original = "malajava" ; // 本来已经存在的 String 实例
String s = new String( original ); // 创建新的 String 实例

此时:

  • 新创建的 String 实例中的数组与 原来 Strnig 实例 中的数组是同一个数组

  • 新创建的 String 实例中的 hash 值 与 原来 Strnig 实例 中的 hash 值相等

同时要特别注意:

一个 String 实例一经创建,其内部所包含的内容将不可更改( value 数组是 final 修饰的 )。

(另外,这里的不可更改是通过正常的访问方式实现,不包括使用反射等技术来修改)

14.2.1.2、从 Java 9 开始 String 类中的实例变量

在 Java 9 之前,String 类中声明的的主要实例变量如下:

private final byte[] value ;
private int hash ;
private final byte coder ;

当创建了一个String实例后:

  • value 数组用来存储将 字符序列 编码后的 字节序列

  • hash 变量来缓存该String实例的哈希值

    • String 类重写了 hashCode 方法,根据String实例所存储的字符内容来重新计算哈希码值
  • coder 变量则用来存储将字符序列编码为字节序列时使用的编码类型

    • coder 变量的值在创建 String 实例时由 JVM 来确定,简单来说:
    • 字符序列 全部为 ASCII 编码中所包含的字符时,JVM会采用 LATIN1
    • 字符序列 中的字符包含 ASCII 编码中不包含的字符时,JVM会采用 UTF16

    • 从 Java 9 开始,String 类中声明了两个类变量,用来表示编码类型:

    static final byte LATIN1 = 0 ;
    static final byte UTF16  = 1 ;
    

这里仍然通过 String 类的构造方法来体现出来三个实例变量的作用:

public String(String original) {
    this.value = original.value;
    this.coder = original.coder;
    this.hash = original.hash;
}

当使用 一个 String 实例 作为 参数 创建另一个 Stirng 实例时:

String original = "malajava" ; // 本来已经存在的 String 实例
String s = new String( original ); // 创建新的 String 实例

此时:

  • 新创建的 String 实例中的数组与 原来 Strnig 实例 中的数组是同一个数组
  • 新创建的 String 实例中的 hash 值 与 原来 Strnig 实例 中的 hash 值相等
  • 新创建的 String 实例中的 coder 值 与 原来 Strnig 实例 中的 coder 值相等

与 Java 9 之前的设计相同,因为 byte 类型的数组变量 valuefinal 修饰的,所以除非使用反射等技术来修改,否则一个 String 实例一经创建,其内部所包含的内容将不可更改。

14.2.2、构造方法

14.2.2.1、创建空字符串实例

用于创建空字符串实例的构造方法:

public String()

举例:

String s = new String();
System.out.println( s.length() ); // 0 

14.2.2.2、使用字符串创建新字符串实例

用于已经存在的字符串实例创建新的字符串实例:

public String( String original ) 

举例:

String s = "malajava" ;
String x = new String( s );
System.out.println( s == x ); // false
System.out.println( s.equals( x ) ); // true

14.2.2.3、使用char数组创建字符串实例

通过字符数组创建字符串实例的构造方法:

public String( char[] value )
public String( char[] value , int offset , int count )

举例:

char[] chars = { '海','上','生','明','月','天','涯','共','此','时' } ;
String s = new String( chars );
System.out.println( s ); // 海上生明月天涯共此时
String x = new String( chars , 3 , 4 ); // 将 chars 数组中索引 3 开始的 4 个字符构造成字符串
System.out.println( s ); // 明月天涯

14.2.2.4、使用byte数组创建字符串实例

通过字节数组创建字符串实例的构造方法:

public String( byte[] bytes )
public String( byte[] bytes , int offset , int length )
public String( byte[] bytes , Charset charset )
public String( byte[] bytes , int offset , int length , Charset charset )
public String( byte[] bytes , String charsetName )
public String( byte[] bytes , int offset , int length , String charsetName )

举例:

Charset charset = Charset.forName( "UTF-8" );
System.out.println( charset ); // UTF-8

byte[] bytes = { -26, -104, -114, -26, -100, -120, -27, -92, -87, -26, -74, -81 } ;
String s = new String( bytes , charset );
System.out.println( s ); // 明月天涯

String x = new String( bytes , 0 , 6 , charset );
System.out.println( s ); // 明月

14.2.2.5、创建StringBuffer创建新字符串实例

通过StringBuffer来创建字符串实例的构造方法:

public String( StringBuffer buffer )

举例:

StringBuffer buffer = new StringBuffer( "落霞与孤鹜齐飞" );
String s = new String( buffer );
System.out.println( s ); // 落霞与孤鹜齐飞

14.2.2.6、创建StringBuilder创建新字符串实例

通过StringBuilder来创建字符串实例的构造方法:

public String( StringBuilder builder )

举例:

StringBuilder buffer = new StringBuilder( "秋水共长天一色" );
String s = new String( buffer );
System.out.println( s ); // 秋水共长天一色

14.2.3、实例方法

因为大家都已经学习过 String 类,所以这里暂时不列出 实例方法。

14.2.4、类方法

14.2.4.1、valueOf

将任意基本数据类型转换为字符串形式,可以使用 valueOf 方法:

public static String valueOf( boolean value )
public static String valueOf( char value )
public static String valueOf( int value )
public static String valueOf( long value )
public static String valueOf( double value )

另外,String 类还提供了将字符数组转换为字符串形式的valueOf方法:

public static String valueOf( char[] data )
public static String valueOf( char[] data , int offset , int count )

对于任意类型的对象,都可以通过以下方法获取该对象的字符串形式:

public static String valueOf( Object o )

因为 valueOf 方法的应用比较简单,这里不再举例。

14.2.4.2、format

使用指定的格式字符串和参数返回一个格式化字符串:

public static String format( String format , Object... args )

使用指定的语言环境、格式字符串和参数返回一个格式化字符串:

public static String format( Locale locale , String format , Object... args )

这里暂时不举例研究字符串格式化。(因为涉及格式太多,不是现在的研究重点)

14.2.4.3、join

从 Java 8 开始提供了将多个 字符序列 连接为一个字符串的支持:

public static String join( CharSequence delimiter , CharSequence... elements )
    
public static String join( CharSequence delimiter,
                           Iterable<? extends CharSequence> elements )

举例:

// 第一个参数为分隔符,将第二个参数及其之后的各个参数连接为同一个字符串
String s = String.join( "," , "张三丰" , "张翠山" , "张无忌" );
System.out.println( s ); // 张三丰,张翠山,张无忌

// Set 接口继承了 Iterable 接口
Set<String> set = new HashSet<>();
set.add( "郭襄" );
set.add( "殷素素" );
set.add( "赵敏" );
// 以第一个参数为分隔符,将 set 集合中所有字符串连接为同一个字符串
String x = String.join( "," , set  );
System.out.println( x  ); // 殷素素,郭襄,赵敏

这里需要注意,不仅仅 String 实例可以作为 join 方法的参数,StringBuffer和StringBuilder的实例也可以作为join方法的参数,因为String、StringBuffer、StringBuilder都实现了CharSequence接口。

14.2.4.4、copyValueOf

返回一个包含了指定字符数组中的字节序列的String实例:

public static String copyValueOf( char[] data )
public static String copyValueOf( char[] data , int offset , int count )

举例:

char[] chars = { '海','上','生','明','月','天','涯','共','此','时' } ;
String s = String.copyValueOf( chars );
System.out.println( s ); // 海上生明月天涯共此时
String x = String.copyValueOf( chars , 3 , 4 );
System.out.println( x ); // 明月天涯

14.3、StringBuffer

StringBuffer的实例表示线程安全的可变字符序列。

因为在StringBuffer内部通过数组来缓存字符序列的内容,因此也将 StringBuffer 称作 字符缓冲区

14.3.1、StringBuffer类的设计

首先看 StringBuffer 类的声明:

package java.lang ;

public final class StringBuffer 
                         extends AbstractStringBuilder
                         implements java.io.Serializable, Comparable<StringBuffer>, CharSequence {
                         
    private transient String toStringCache ;
    
}

由源代码可知,StringBuffer 类继承了 AbstractStringBuilder 类。AbstractStringBuilder是java.lang 包中一个没有 public 修饰符修饰的类,因此该类仅限java.lang包内部访问。

Java 9 之前 AbstractStringBuilder 类的设计:

package java.lang ;

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value ; // 采用 char 数组存储字符序列
    int count; // 统计字符个数
}  

Java 9 之后 AbstractStringBuilder 类的设计:

package java.lang ;

abstract class AbstractStringBuilder implements Appendable, CharSequence {
  byte[] value ; // 采用 byte 数组存储字节序列
  byte coder ; // 记录字符编码类型( 对应 String.LATIN1 和 String.UTF16 )
  int count; // 统计字符个数
}

由以上可知,StringBuffer 实现了以下接口:

  • java.io.Serializable
  • java.lang.Comparable
  • java.lang.CharSequence
  • java.lang.Appendable

另外,StringBuffer通过从父类AbstractStringBuilder中继承的字段来存储字节序列:

  • 在 Java 9 之前是通过 char 数组存储 ( char[] value )
  • 从 Java 9 开始使用 byte 数组存储 ( byte[] value )

但不论是 Java 9 之前的 char 数组 还是 Java 9 开始使用的 byte 数组,它们都没有 final 修饰符修饰,因此它们的值是可变的,这点与 String 是完全不同的。

14.3.2、构造方法

14.3.2.1、创建空缓冲区

StringBuffer的无参数构造方法可以创建一个初始容量为16的空缓冲区

public StringBuffer()

举例:

StringBuffer buffer = new StringBuffer();
System.out.println( "length : " + buffer.length() ); // length : 0
System.out.println( "capacity : " + buffer.capacity() ); // capacity : 16

14.3.2.2、创建指定容量的缓冲区

通过指定为构造方法指定参数来创建指定容量的字符缓冲区:

public StringBuffer( int capacity )

举例:

StringBuffer buffer = new StringBuffer( 10 );
System.out.println( "length : " + buffer.length() ); // length : 0
System.out.println( "capacity : " + buffer.capacity() ); // capacity : 10

14.3.2.3、创建包含指定字符串内容的的缓冲区

创建字符缓冲区时,可以通过参数指定在缓冲区中所放入的字符序列:

public StringBuffer( String s )

通过该构造方法创建的 字符缓冲区 的初始容量为 s.length() + 16 ,长度为 s.length()

举例:

String s = "malajava" ;

StringBuffer buffer = new StringBuffer( s );
System.out.println( "length : " + buffer.length() ); // length : 8
System.out.println( "capacity : " + buffer.capacity() ); // capacity : 24

14.3.2.4、使用任意字符序列创建缓冲区

StringBuffer 类中定义了接受任意 字符序列 做参数的构造方法:

public StringBuffer( CharSequence cs )

举例:

String s = "malajava" ;
StringBuffer buffer1 = new StringBuffer( s ); // String 实现了 CharSequence 接口

StringBuilder builder = new StringBuilder( "麻辣爪哇" );
StringBuffer buffer2 = new StringBuffer( builder ); // StringBuilder 实现了 CharSequence 接口

StringBuffer buffer3 = new StringBuffer( buffer2 ); // StringBuffer 实现了 CharSequence 接口

14.3.3、实例方法

14.3.3.1、获取缓冲区容量和长度

StringBuffer类中定义了可以获取字符串缓冲区容量的方法:

public synchronized int capacity()

另外还声明了用于获取字符串中字符个数的方法:

public synchronized int length()

举例:

StringBuffer buffer = new StringBuffer( "雪豹" );
System.out.println( "length : " + buffer.length() ); // length : 2
System.out.println( "capacity : " + buffer.capacity() ); // capacity : 18

14.3.3.2、在缓冲区中追加内容

  • 用于将基本数据类型的值以字符串形式追加到缓冲区中的方法:
public synchronized StringBuffer append( boolean value )
public synchronized StringBuffer append( char value )
public synchronized StringBuffer append( int value )
public synchronized StringBuffer append( long value )
public synchronized StringBuffer append( float value )
public synchronized StringBuffer append( double value )

举例:

StringBuffer buffer = new StringBuffer();

boolean b = true ;
buffer.append( b );
System.out.println( buffer ); // true

char c = 'A' ;
buffer.append( c );
System.out.println( buffer ); // trueA

int x = 9527 ;
buffer.append( x );
System.out.println( buffer ); // trueA9527

long n = 199911200630L ;
buffer.append( n );
System.out.println( buffer ); // trueA9527199911200630
  • 用于将字符数组中的字符追加到缓冲区中的方法:
public synchronized StringBuffer append( char[] chars )
public synchronized StringBuffer append( char[] chars , int offset , int length )

举例:

StringBuffer buffer = new StringBuffer();

char[] chars = { '长' , '沙' , '保' , '卫' , '战' };
buffer.append( chars ) ;
System.out.println( buffer ); // 长沙保卫战

buffer.append( chars , 2 , 2 ) ;
System.out.println( buffer ); // 长沙保卫战保卫

buffer.append( chars , 0 , 2 ) ;
System.out.println( buffer ); // 长沙保卫战保卫长沙
  • 用于将任意字符序列(CharSequence)添加到缓冲区中的方法:
public synchronized StringBuffer append( CharSequence cs )
public synchronized StringBuffer append( CharSequence cs , int start , int end )

举例:

StringBuffer buffer = new StringBuffer();

// 接口类型(CharSequence)的引用变量(cs) 指向 实现类(String)类型 的对象
CharSequence cs = "今天天气好晴朗" ;

buffer.append( cs );
System.out.println( buffer ); // 今天天气好晴朗

buffer.append( cs , 4 , 6 );
System.out.println( buffer ); // 今天天气好晴朗好晴朗
  • 用于将字符串添加到缓冲区中的方法:
public synchronized StringBuffer append( String s )

举例:

StringBuffer buffer = new StringBuffer();

String s = "神舟飞船" ;

buffer.append( s );
System.out.println( buffer ); // 神舟飞船

buffer.append( "飞神州" );
System.out.println( buffer ); // 神舟飞船飞神州
  • 将已存在的缓冲区中的字符添加到当前缓冲区中的方法:
public synchronized StringBuffer append( StringBuffer buffer )

举例:

StringBuffer buffer1 = new StringBuffer( "龙井茶" );
StringBuffer buffer2 = new StringBuffer( "天堂水" );
buffer2.append( buffer1 ) ;
System.out.println( buffer2 ); // 天堂水龙井茶
  • 将任意对象的字符串形式添加到当前缓冲区中的方法:
public synchronized StringBuffer append( Object o )

举例:

StringBuffer buffer = new StringBuffer();

// 父类(Object)引用 指向 子类(Integer)对象
Object o = Integer.valueOf( "8FA3" , 16 );

buffer.append( o );
System.out.println( buffer ); // 36771

14.3.3.3、在缓冲区中插入内容

StringBuffer中定义了大量的用于在缓冲区中指定位置插入数据的方法:

public synchronized StringBuffer insert( int offset , boolean value )
public synchronized StringBuffer insert( int offset , char value )
public synchronized StringBuffer insert( int offset , int value )
public synchronized StringBuffer insert( int offset , long value )
public synchronized StringBuffer insert( int offset , float value )
public synchronized StringBuffer insert( int offset , double value )
public synchronized StringBuffer insert( int offset , char[] chars )
public synchronized StringBuffer insert( int offset , char[] chars , int offset , int length )
public synchronized StringBuffer insert( int offset , CharSequence cs )
public synchronized StringBuffer insert( int offset , CharSequence cs , int start , int end )
public synchronized StringBuffer insert( int offset , String s )
public synchronized StringBuffer insert( int offset , Object o )

举例:

StringBuffer buffer = new StringBuffer( "时维九月序属三秋" );
System.out.println( buffer ); // 时维九月序属三秋 

buffer.insert( 4 , ",");
System.out.println( buffer ); // 时维九月,序属三秋

14.3.3.4、替换缓冲区中的内容

StringBuffer提供了替换缓冲区中内容的方法:

public synchronized StringBuffer replace( int start , int end , String str )

举例:

StringBuffer buffer = new StringBuffer( "春眠不觉晓处处蚊子咬" );
System.out.println( buffer ); // 春眠不觉晓处处蚊子咬
buffer.replace( 5 , 10 , "处处闻啼鸟" );
System.out.println( buffer ); // 春眠不觉晓处处闻啼鸟

StringBuffer也提供了替换缓冲区中单个字符的方法:

public synchronized void setCharAt( int index , char ch )

举例:

StringBuffer buffer = new StringBuffer( "张俊宝" );
System.out.println( buffer ); // 张俊宝
buffer.setCharAt( 1 , '君' );
System.out.println( buffer ); // 张君宝

14.3.3.5、查询字符串索引

与 String 类一样,StringBuffer 提供了搜索字符串在缓冲区位置的的方法:

public int indexOf( String str )
public int indexOf( String str , int fromIndex )
public int lastIndexOf( String str )
public int lastIndexOf( String str , int fromIndex )

这些方法的使用方法与 String 类中的同名方法=是相同的,因此这里不再举例说明。

14.3.3.6、删除缓冲区中的内容

StringBuffer提供了删除缓冲区中内容的方法:

public synchronized StringBuffer delete( int start , int end ) 

也提供了删除单个字符的方法:

public synchronized StringBuffer deleteCharAt( int index ) 

举例:

StringBuffer buffer = new StringBuffer( "StringBuffer实现了Appendable接口因此此它支持序列化操作" );

// 因为 buffer 中的 "因此此" 应该写作 "因此" ,所以需要删除最后一个 '此' 字
int last = buffer.lastIndexOf( "此" ); // 获取最后一个 此 字的位置
if( last != -1 ) {
  buffer.deleteCharAt( last ) ; // 删除指定位置的字符
  System.out.println( buffer ); // StringBuffer实现了Appendable接口因此它支持序列化操作
}

// 因为 StringBuffer类 实现了 Serializable 接口,所以才支持序列化操作,不是因为实现了 Appendable 接口
// 这里选择将缓冲区中 "实现了Appendable接口因此此它" 删除,以保证缓冲区中描述的是正确的

final String sub = "实现了Appendable接口因此它" ;
int start = buffer.indexOf( sub ); // 寻找 sub 对应的字符串首次出现的位置
if( start != -1 ) {
  int end = start + sub.length() ; // 计算 end 的值
  buffer.delete( start , end ) ; // 从 buffer 中删除 [ start , end ) 之间的字符
  System.out.println( buffer ); // StringBuffer支持序列化操作
}

14.3.3.7、截取子串

与String类类似,StringBuffer也提供了截取子串的方法:

public String substring( int start )
public String substring( int start , int end )
public synchronized CharSequence subSequence( int start , int end ) 

这三个方法都可以从缓冲区中获取并返回部分字节序列,区别是:

  • subSequence 是线程安全的 ( 有 synchronized 修饰符修饰 )
  • substring 不是线程安全的 ( 没有 synchronized 修饰符修饰 )

14.3.3.8、翻转缓冲区内容

当需要将字符缓冲区中的整个字节序列翻转时,可以使用以下方法:

public synchronized StringBuffer reverse()

举例:

StringBuffer buffer = new StringBuffer( "壹贰叁肆伍陆柒捌玖拾" );
System.out.println( buffer ); // 壹贰叁肆伍陆柒捌玖拾
buffer.reverse();
System.out.println( buffer ); // 拾玖捌柒陆伍肆叁贰壹

14.3.3.9、设置缓冲区

StringBuffer类中声明了两个与缓冲区容量有关的方法:

  • 将字符序列调整为指定长度(可能涉及缓冲区容量变化)
public synchronized void setLength( int newLength )

确切地说,通过 setLength 方法可以调整 缓冲区 中字符序列的长度,

因为这个长度可能超过当前缓冲区容量,因此才涉及缓冲区容量变化。

  • 将缓冲区容量调整为与字符序列个数相同
public synchronized void trimToSize()

举例:

StringBuffer buffer = new StringBuffer( "渔舟唱晚" );

System.out.println( buffer.length() + " , " + buffer.capacity() ); // 4 , 20
buffer.setLength( 15 );
System.out.println( buffer ); // 渔舟唱晚

标签

© 2021 成都云创动力科技有限公司 蜀ICP备20006351号-1