Java 字符串

字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

创建增删

  • 创建字符串最简单的方式如下:
    String greeting = "Greeting";Copy to clipboardErrorCopied
    
  • 和其它对象一样,可以使用关键字和构造方法来创建 String 对象。String 类有 11 种构造方法,这些方法提供不同的参数来初始化字符串,比如提供一个字符数组参数:
    public class StringDemo{
       public static void main(String args[]){
          char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};
          String helloString = new String(helloArray);
          System.out.println( helloString );
       }
    }Copy to clipboardErrorCopied
    
  • 注意,String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。

字符串转义

点的转义:. ==> u002E
美元符号的转义:\$ ==> u0024
乘方符号的转义:^ ==> u005E
左大括号的转义:{ ==> u007B
左方括号的转义:[ ==> u005B
左圆括号的转义:( ==> u0028
竖线的转义:| ==> u007C
右圆括号的转义:) ==> u0029
星号的转义:\* ==> u002A
加号的转义:+ ==> u002B
问号的转义:? ==> u003F
反斜杠的转义: ==> u005CCopy to clipboardErrorCopied

譬如我们如果需要从 System.in 中输入 "C:\",在 Java 中的字符串表示的是: "C:\\",而如果要用正则表达式匹配 "\" 这个字符的时候,正则表达式要写成 "\\\\",即首先是根据 Java 语言本身的转义字符,转化为普通字符中的 "\\",其就等价于正则表达式中匹配 "\" 这个字符。

模板字符串

String.format("%s 今年%d 岁","我", "24");
MessageFormat.format("{0}  今年{1} 岁", "我",24);Copy to clipboardErrorCopied

Split | 截取分割

  • split 方法的结果是一个字符串数组,在 stingObj 中每个出现 separator 的位置都要进行分解:
    stringObj.split([separator,[limit]])
    // 根据转义字符进行分割
    String.split("\\.")
    String.split("\\|")
    // 使用正则表达式Copy to clipboardErrorCopied
    

子字符串

  • substring(beginIndex,endIndex),即截取的是 [beginIndex,endIndex-1] 这样的字符串。
    System.out.println("abcd".substring(0,1));Copy to clipboardErrorCopied
    

不可变性

  • 字符串实际上就是一个 char 数组,并且内部就是封装了一个 char 数组。并且这里 char 数组是被 final 修饰的:
    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];Copy to clipboardErrorCopied
    
  • 并且 String 中的所有的方法,都是对于 char 数组的改变,只要是对它的改变,方法内部都是返回一个新的 String 实例。

intern

String.intern() 方法可以用来处理在 Java 中字符串的重复问题,通过使用 intern() 方法,可以节省大量由重复字符串对象消耗的堆内存。如果一个字符串对象包含与另一个字符串相同的内容,但是占用了不同的内存位置,例如 str1 != str2str1.equals(str2) true,则称其为重复。由于 String 对象在普通 Java 应用程序中消耗大量堆内存,因此使用 intern() 方法减少重复,也可以使用 intern() 方法实例化 String 对象并将它们存储到 String pool 中以便进一步重用。

通过在此对象上调用 intern() 方法,可以指示 JVM 将此放入 String pool 中,并且每当其他人创建 abc 时,将返回此对象而不是创建新对象。

public class Test {
    public static void main(String[] args) {
        String s1 = "Test";
        String s2 = s1.intern();
        String s3 = new String("Test");
        String s4 = s3.intern();
        System.out.println(s1==s2); // True
        System.out.println(s1==s3); // False
        System.out.println(s1==s4); // True
        System.out.println(s2==s3); // False
        System.out.println(s2==s4); // True
        System.out.println(s3==s4); // False
    }
}

Character

Character 类用于对单个字符进行操作,Character 类在对象中包装一个基本类型 char 的值。

char ch = 'a';
// Unicode 字符表示形式
char uniChar = '\u039A';
// 字符数组
char[] charArray ={ 'a', 'b', 'c', 'd', 'e' };Copy to clipboardErrorCopied

然而,在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情况。为了解决这个问题,Java 语言为内置数据类型 char 提供了包装类 Character 类。Character 类提供了一系列方法来操纵字符。你可以使用 Character 的构造方法创建一个 Character 类对象,例如:

Character ch = new Character('a');Copy to clipboardErrorCopied

在某些情况下,Java 编译器会自动创建一个 Character 对象。例如,将一个 char 类型的参数传递给需要一个 Character 类型参数的方法时,那么编译器会自动地将 char 类型参数转换为 Character 对象这种特征称为装箱,反过来称为拆箱。

// 原始字符 'a' 装箱到 Character 对象 ch 中
Character ch = 'a';
// 原始字符 'x' 用 test 方法装箱
// 返回拆箱的值到 'c'
char c = test('x');Copy to clipboardErrorCopied

转义序列

前面有反斜杠(\)的字符代表转义字符,它对编译器来说是有特殊含义的。下面列表展示了 Java 的转义序列:

转义序列 描述
\t 在文中该处插入一个 tab 键
\b 在文中该处插入一个后退键
\n 在文中该处换行
\r 在文中该处插入回车
\f 在文中该处插入换页符
' 在文中该处插入单引号
" 在文中该处插入双引号
| 在文中该处插入反斜杠

类方法

下面是 Character 类的方法:

序号 方法与描述
1 isLetter() 是否是一个字母
2 isDigit() 是否是一个数字字符
3 isWhitespace() 是否是一个空白字符
4 isUpperCase() 是否是大写字母
5 isLowerCase() 是否是小写字母
6 toUpperCase() 指定字母的大写形式
7 toLowerCase() 指定字母的小写形式
8 toString() 返回字符的字符串形式,字符串的长度仅为 1

案例

提取字符中的大小写字母:

public class UpperLowerCase {
    public static void main(String []args) {
        String StrA="I am Tom.I am from China.";
        String StrB="";
        String StrC="";
        for(int i=0;i<StrA.length();i++){
            if(Character.isUpperCase(StrA.charAt(i)))
                StrB +=StrA.charAt(i)+"  ";
            if(Character.isLowerCase(StrA.charAt(i)))
                StrC +=StrA.charAt(i)+"  ";
            }
        System.out.println("字符串中大写字母有:"+StrB);
        System.out.println("字符串中小写字母有:"+StrC);
    }
}Copy to clipboardErrorCopied

输出结果为:

字符串中大写字母有:I  T  I  C
字符串中小写字母有:a  m  o  m  a  m  f  r  o  m  h  i  n  a

StringBuffer & StringBuilder

StringBuffer、StringBuilder 和 String 一样,也用来代表字符串。String 类是不可变类,任何对 String 的改变都 会引发新的 String 对象的生成;StringBuffer 则是可变类,任何对它所指代的字符串的改变都不会产生新的对象。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。StringBuffer 支持并发操作,线性安全的,适合多线程中使用。StringBuilder 不支持并发操作,线性不安全的,不适合多线程中使用。新引入的 StringBuilder 类不是线程安全的,但其在单线程中的性能比 StringBuffer 高。由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

StringBuffer sb = new StringBuffer(BASEINFO);  
for (int i = 0; i < COUNT; i++) {  
    sb = sb.append("miss");  
}
StringBuilder sb = new StringBuilder(BASEINFO);  
long starttime = System.currentTimeMillis();  
for (int i = 0; i < COUNT; i++) {  
    sb = sb.append("miss");  
}Copy to clipboardErrorCopied

StringBuffer

以下是 StringBuffer 类支持的主要方法:

序号 方法描述
1 public StringBuffer append(String s) 将指定的字符串追加到此字符序列
2 public StringBuffer reverse() 将此字符序列用其反转形式取代
3 public delete(int start, int end) 移除此序列的子字符串中的字符
4 public insert(int offset, int i) 将int 参数的字符串表示形式插入此序列中
5 replace(int start, int end, String str) 使用给定String 中的字符替换此序列的子字符串中的字符

MD5

MD5 是一种广泛使用的加密哈希函数,可产生 128 位的哈希。

MessageDigest

我们在 java.security.MessageDigest 类中具有哈希功能。这个想法是首先使用要用作 Singleton 参数的算法实例化 MessageDigest:

MessageDigest.getInstance(String Algorithm)Copy to clipboardErrorCopied
@Test
public void givenPassword_whenHashing_thenVerifying()
  throws NoSuchAlgorithmException {
    String hash = "35454B055CC325EA1AF2126E27707052";
    String password = "ILoveJava";
   
    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(password.getBytes());
    byte[] digest = md.digest();
    String myHash = DatatypeConverter
      .printHexBinary(digest).toUpperCase();
   
    assertThat(myHash.equals(hash)).isTrue();
    // 针对文件处理
    String filename = "src/test/resources/test_md5.txt";
    md.update(Files.readAllBytes(Paths.get(filename)));
}Copy to clipboardErrorCopied

Apache Commons

@Test
public void givenPassword_whenHashingUsingCommons_thenVerifying()  {
    String hash = "35454B055CC325EA1AF2126E27707052";
    String password = "ILoveJava";
    String md5Hex = DigestUtils
      .md5Hex(password).toUpperCase();
    assertThat(md5Hex.equals(hash)).isTrue();
}Copy to clipboardErrorCopied

Guava

@Test
public void givenFile_whenChecksumUsingGuava_thenVerifying()
  throws IOException {
    String filename = "src/test/resources/test_md5.txt";
    String checksum = "5EB63BBBE01EEED093CB22BB8F5ACDC3";
    HashCode hash = com.google.common.io.Files
      .hash(new File(filename), Hashing.md5());
    String myChecksum = hash.toString()
      .toUpperCase();
    assertThat(myChecksum.equals(checksum)).isTrue();
}
下一节:正则表达式定义了字符串的模式,正则表达式可以用来搜索、编辑或处理文本;正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。