必须知道的String知识点

1.String 类型的概述

Java中String就是Unicode字符序列,例如,字符串“Java\u2122”由5个Unicode字符J、a、v、a和 ™ 组成。不像C/C++中,字符串只是字符数组,Java中字符串String是一个java.lang包中的类。

但是,在Java中String和普通的类不一样,是一个特殊的类:

  • String可以直接使用双引号""进行赋值,而不用调用构造函数。
  • '+'可以拼接两个字符串,但是其他对象不能使用'+'。
  • String是不可变的,也就是说,String对象一旦创建就不能被修改。例如,toUpperCase() 方法创建并返回一个新字符串,而不是修改原理字符串的内容。

String类型的常用方法总结:

// 长度
int length()        // 返回字符串的长度
boolean isEmpty()   // 判断长度是否==0

// 比较
boolean equals(String another)  // Java中不能使用 '==' 或者 '!=' 来比较两个字符串
boolean equalsIgnoreCase(String another)
int compareTo(String another)   // 返回 0 如果字符串相同;< 0 如果字典序小于another;否则 > 0
int compareToIgnoreCase(String another)
boolean startsWith(String another)
boolean startsWith(String another, int fromIndex)   // 在fromIndex位置开始
boolean endsWith(String another)

// 检索 & 索引下标
int indexOf(String search)
int indexOf(String search, int fromIndex)
int indexOf(int character)
int indexOf(int character, int fromIndex)
int lastIndexOf(String search)
int lastIndexOf(String search, int fromIndex)
int lastIndexOf(int character)
int lastIndexOf(int character, int fromIndex)

// 截取字符串的字符或一部分字符串(子串)
char charAt(int index)  // 下标从 0 到 length - 1
String substring(int fromIndex)
String substring(int fromIndex, int endIndex)   // 包前不包后

// 根据原字符串创建一个新字符串或字符数组
String toLowerCase()
String toUpperCase()
String trim()       // 返回一个移除前后空白的新字符串
String replace(char oldChar, char newChar)  // 返回一个替换后的字符串
String concat(String another)               // 字符串拼接
char[] toCharArray()                        // 返回一个字符串数组
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)   // 复制到 dst 字符数组中

// 静态方法,将基本类型转换为字符串
static String valueOf(type arg)     // type可以是基本类型或者字符数组

// 静态方法,将字符串进行格式化输出
static String format(String formattingString, Object... args)   // 类似于printf()

// 正则表达式
boolean matches(String regex)
String replaceFirst(String regex, String replacement)
String replaceAll(String regex, String replacement)
String[] split(String regex)
String[] split(String regex, int limit)
静态方法 String.format()

这个方法用来输出一个格式化的字符串,类似于 C 系列的 printf() 函数。例如,

String.format("%.1f", 1.234);   // 返回字符串 "1.2"

String.format() 可以非常方便地用来输出一个简单格式的字符串(比如,在 toStirng() 方法中使用)。对于复杂的字符串,使用StringBuffer/StringBuilder加一个Formatter。

2. String 一个特殊的类

因为程序会频繁的使用 String 类型,String 类型在Java中是很特殊的。自然,为了计算性能和存储效率,String 的性能很关键。为了提高提高语言的性能,Java设计者们在面向对象语言中,保留了基本数据类型,而不是一切都是对象。基本数据类型存储在调用栈上,从而占用更少的存储空间,以及更容易维护。而对象实例存储在堆上,需要更复杂的内存管理和更多的存储空间。

为了更好地性能,Java 的 String 被设计成介于基本数据类型和普通类之间的一个特殊类型。Stirng 的特性包括:

  • 用于基本数据类型操作的'+',被 String 重载用来拼接两个字符串。出于软件工程的考虑,Java 不支持操作符的重载。在支持操作符重载的语言中,比如 C++ ,甚至可以将 '+' 重载用来执行减法运算,这会使得代码难以阅读。Java 中 '+' 只被内部重载用于字符串拼接。'+' 并不能用于任意两个对象中,比如 Points 或者 Circles。
  • String 有两种创建方式:直接赋值 或者 通过 new 关键字创建(这种方式不推荐,也不常见)
    举个例子:

    String str1 = "Java is Hot";           // 隐式地通过 String 字面量创建
    String str2 = new String("I'm cool");  // 显式地使用 new 创建

    在第一条语句中,str1 被申明为一个 String 类型引用,并被 String 字面量 "Java is Hot" 初始化。在第二条语句中,str2 被申明为一个 String 类型引用,并通过 new 关键字构造的 "I'm cool" String 对象初始化。

  • Stirng 字面量存储在常量池中,相同内容的字符串是共享的,只会存储一份。通过 new 创建的 String 对象存储在堆上,相同内容的字符串不会共享。

2.1 String 字面量 VS String 对象

如上所述,有两种方式构造 String :通过 String 字面量隐式地构造,或者通过 new 显式地创建。比如:

String s1 = "Hello";              // String 字面量
String s2 = "Hello";              // String 字面量
String s3 = s1;                   // 相同的引用
String s4 = new String("Hello");  // String 对象
String s5 = new String("Hello");  // String 对象

Java 提供一个特别的机制,字符串常量池,来保存 String 字面量。如果两个 String 字面量内容相同,它们将会在字符串常量池中共享。频繁使用的字符串将会通过这种方式持久地保存。另一方面,通过new 创建的 String 对象被保存在堆中。每个存储在堆上的 String 对象,就像其他对象一样,拥有独立的存储地址。堆上的存储地址不会共享,即使两个 String 对象内容相同。

可以使用 String 类的 equals() 方法比较两个字符串的内容。也可以使用 '==' 来比较两个对象的引用(或者叫指针)。看看下面的代码:

s1 == s1;         // true, 相同的引用
s1 == s2;         // true, s1 和 s2 在常量池中共享
s1 == s3;         // true, s3 被赋值为 s1 的引用
s1.equals(s3);    // true, 内容相同
s1 == s4;         // false, 对象引用不同
s1.equals(s4);    // true, 内容
s4 == s5;         // false, 堆上的引用不同
s4.equals(s5);    // true, 内容相同

重点:

  • 在上面的例子中,通过'=='比较两个 String 对象的引用,说明了字符串存储在常量池和存储在堆上的区别。在程序中,使用 (str1 == str2) 来比较两个字符串的内容是逻辑上的错误。
  • String 直接赋值的内容将存储在字符串常量池中。不推荐使用 new 去创建一个堆上的字符串对象。

2.2 String 的不可变特性

因为字符串在常量池中是共享的,Java 的 String 被设计成不可变的。也就是说,一旦 String 被创建,它的内容就不能被修改。否则,其他共享内容的 Stirng 引用将会被无法预料变化影响。类似于 toUpperCase() 的方法就可能修改 String 对象的内容,而实际上它将返回一个新创建的 String 对象,原本的 String 对象引用会被释放,一旦没有引用了,就将会被GC。

因为 String 是不可变的,频繁修改 String 的效率很低(这将创建大量的字符串,并占用内存空间)。比如下面这段代码:

// 效率低的代码
String str = "Hello";
for (int i = 1; i < 1000; ++i) {
   str = str + i;
}

如果需要频繁修改字符串,可以考虑使用 StringBuffer 或者 StringBuilder。

3. StringBuffer & StringBuilder

如前所述,字符串是不可变的,因为存储在字符串常量池中的字面量是共享的。修改其中一个字符串的内容,将会对其他共享的字符串产生副作用。

JDK 提供了两个类来支持可变的字符串:StringBuffer 和 StringBuilder(都在核心包 java.lang 中)。StringBuffer 和 StringBuilder 对象跟其他普通对象一样,存储在堆上,不会共享,因此修改它们不会对其他对象产生副作用。

StringBuilder 类是在JDK 1.5中引入的。它基本上与 StringBuffer 一致,除了它是线程不安全的以外。对于单线程的程序来说,StringBuilder 因为没有同步的开销,效率会更高。

猜你喜欢

转载自www.cnblogs.com/bluemilk/p/10459389.html