Java核心技术第3章(3)

来源:互联网 发布:源计划亚索多少钱淘宝 编辑:程序博客网 时间:2024/05/22 05:06

3.6 字符串

从概念上将,Java字符串就是Unicode字符序列.Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类,叫做String.每个用双引号括起来的字符串都是String类的一个实例:
String e = "";// an empty stringString greeting = "hello";

3.6.1 子串

String类的substring方法可以从一个较大的字符串提取出一个子串,例如:
String greeting = "hello";String s = greeting.subString(0, 3);
创建了一个由字符"hel"组成的字符串.
substring方法的第二个参数是不想复制的第一个位置.这里要复制位置为0,1,2的字符.
substring的工作方式有一个优点:容易计算子串的长度.字符串s.substring(a,b)的长度为b-a.

3.6.2 拼接

Java语言语序使用+号连接(拼接)两个字符串.
String expletive = "Expletive";String PG13 = "deleted";String message = expletive + PG13;
上述代码将"Expletivedeleted"赋给变量message。
当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串(任何一个Java对象都可以转换成字符串).例如:
int age = 13;String rating = "PG" + age;rating被设置为"PG13";
这种特性通常用在输出语句中.例如:
System.out.println("The answer is " + answer);

3.6.3 不可变字符串

String类没有提供用于修改字符串的方法.如果希望将greeting的内容修改为"Help!",不能直接将greeting的最后两个位置的字符修改为'p'和'!'.如何修改这个字符串呢?在Java中,应该首先提取需要的字符,然后再拼接上替换的字符串:
greeting = greeting.substring(0, 3) + "p!";
上面这条语句将greeting修改为"Help!".
由于不能修改Java字符串中的字符,所以在Java文档中将String类对象称为不可变字符串.不能修改字符串中的任何一个字符,但可以修改字符串变量greeting,让它引用另外一个字符串.
这样是否会降低运行效率呢?看起来好像修改一个代码单元要比创建一个新字符串更加简洁.答案是:也对,也不对.的确,通过拼接"Hel"和"p!"来创建一个新字符串的效率确实不高.但是,不可变字符串却有一个优点:编译器可以让字符串共享.
为了弄清楚具体的工作方式,可以想象将各种字符串存放在公共的存储池中.字符串变量指向存储池中相应的位置.如果复制一个字符串变量,原始字符串与复制的字符串共享相同的字符.
总而言之,Java的设计者认为共享带来的高效率远远胜于提取,拼接字符串所带来的低效率.查看程序会发现:很少需要修改字符串,而是往往需要对字符串进行比较.
注释:C程序员第一次接触Java字符串的时候,常常会感到疑惑,因为总是将字符串认为是字符型数组:
char greeting[] = "hello";
这种认识是错误的,Java字符串更像 char* 指针.
char* greeting = "hello";
当采取另一个字符串替换greeting的时候,Java代码主要进行下列操作:
char* temp = malloc(6);strncpy(tmp, greeting, 3);strncpy(temp + 3, "p!", 3);greeting = temp;
的确,现在greeting指向字符串"help!".Java要比一连串的strncpy调用舒适的多.然而如果将greeting赋予另外一个值又会怎么样呢?
greeting = "howdy";
这样做会不会产生内存泄露呢?毕竟,原始字符串放置在堆中.十分幸运,Java将自动地进行垃圾回收.如果一块内存不再使用了,系统最终会将其回收.
C++ string对象也自动地进行内存的分配与回收.内存管理是通过构造器,赋值操作和析构器显式执行的.然而,C++字符串是可修改的.也就是说,可修改字符串中的单个字符.

3.6.4 检测字符串是否相等

可以使用equals方法检测两个字符串是否相等.对于表达式:
s.equals(t)
如果字符串s与字符串t相等,则返回 true;否则返回 false.s与t可以是字符串变量,也可以是字符串常量.
要检测两个字符串是否相等,而不区分大小写,可以使用equalsIgnoreCase方法.
"hello".equalsIgnoreCase("Hello");
一定不能使用==运算符检测两个字符串是否相等.这个运算符只能确定两个字符串是否放置在同一个位置上.当然,如果字符串放置在同一个位置上,它们必然相等.但是,完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上.
String greeting = "hello";if (greeting == "hello")    // probably trueif (greeting.substring(0, 3) == "hel")    // probably false
    如果虚拟机始终将相同的字符串共享,就可以使用 == 运算符检测是否相等.但实际上,只有字符串常量是共享,而 + 或 substring 等操作符产生的结果并不是共享的.因此,千万不要使用 == 运算符检测字符串的相等性.
    注释
:C++的string类重载了 == 运算符以便检测字符串内容的相等性.可惜Java没有采用这种方式,它的字符串"看起来,感觉起来"与数值一样,但进行相等性测试时,其操作方式又类似于指针.
    C程序员不实用 == 对字符串进行比较,而是用stcmpy函数.Java的compareTo方法与strcmp完全类似,因此可以这样使用:
if (greeting.compareTo("hello") == 0)
    不过使用equals看起来更为清晰.

3.6.5   空串与Null串

    空串""是长度为0的字符串.可以调用以下代码检测字符串是否为空:
if (str.length() == 0)
    或
if (str.equals("")
    空串是一个Java对象,有自己的串长度(0)和内容(空).不过String变量还可以存放一个特殊的值,名为null,这表示目前没有任何对象与该变量关联.要检查一个字符串是否为null,要使用以下条件:
if (str == null)
    有时候要检查一个字符串既不是null也不为空串,这种情况下就需要使用以下条件:
if (str != null && str.length() != 0)
    首先要检查str不为null(如果在null值上调用方法,会出现错误).

3.6.6   代码点与代码单元

    Java字符串由 char 序列组成,char 数据类型是采用UTF-16编码表示Unicode代码点的代码单元.大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示.
    length方法将返回UTF-16编码表示的给定字符串所需要的代码单元数量.例如:
String greeting = "hello";int n = greeeting.length();     // is 5
   要想得到实际的长度,即代码点的长度,可以调用:
int cpCount = greeting.codePointCount(0, greeting.length());

3.6.7   字符串API

    Java中的String类包含了50多个方法.绝大多数都非常有用.
    注释:本书中每一个API的注释都以形如java.lang.String的类名开始.类名之后是一个或多个方法的名字,解释和参数描述.
char charAt(int index)
    返回给定位置的代码单元,除非对底层的代码单元感兴趣,否则不需要使用这个方法.
int codePointAt(index)
    返回从给定位置开始或结束的代码点
int offsetByCodePoints(int startIndex, int cpCount);
    返回从startIndex代码点开始,位移cpCount后的代码点索引
int compareTo(String other)
    按照字典顺序,如果字符串位于other之前,返回一个负数;如果字符串位于other之后,返回一个正数;如果两个字符串相等,返回0.
boolean endsWith(String suffix)
    如果字符串以suffix结尾,返回 true
boolean equals(Object other)
    如果字符串与other相等,返回 true
boolean equalsIgnoreCast(String other)
    如果字符串与other相等(忽略大小写),返回 true
int index0f(String str)int index0f(String str, int fromIndex)int index0f(int cp)int index0f(int cp, int fromIndex)
    返回与字符串str或代码点cp匹配的第一个子串的开始位置.这个位置从索引0或fromIndex开始计算.如果在原始串中不存在str,返回-1
int lastIndex0f(String str)int lastIndex0f(String str, int fromIndex)int lastIndex0f(int cp)int lastIndex0f(int cp, int fromIndex)
    返回与字符串st或代码点cp匹配的最后一个子串的开始位置.这个位置从原始串尾端或fromIndex开始计算.
int length()
    返回字符串的长度
int codePointCount(int startIndex, int endIndex)
    返回startIndex和endIndex-1之间的代码点数量.没有配对成的代用字符将计入代码点
String replace(CharSequence oldString, CharSequence newString)
    返回一个新字符串.这个字符串用newString代替原始字符串中所有的oldString.可以用String或StringBuilder对象作为CharSequence参数.
boolean startsWith(String prefix)
    如果字符串以prefix字符串开始,返回 true
String substring(int beginIndex)String substring(int beginIndex, int endIndex)
    返回一个新字符串.这个字符串包含原始字符串中从beginIndex到串尾货endIndex-1的所有代码单元
String toLowerCase()
    返回一个新字符串.这个字符串将原始字符串中的所有大写字母改成小写字母
String toUpperCase()
    返回一个新字符串.这个字符串将原始字符串中的所有小写字母改成大写字母
String trim()
    返回一个新字符串.这个字符串将删除原始字符串中头部和尾部的空格

3.6.8   阅读联机API文档

    到官网下载API http://www.oracle.com/technetwork/java/javase/documentation/jdk8-doc-downloads-2133158.html.

3.6.9   构建字符串

    有时候,需要由较短的字符串构建字符串.采用字符串连接的方式达成此目的的效率比较低.每次连接字符串,都会构建一个新的String对象,既耗时,又浪费空间.使用StringBuilder类就可以避免这个问题的发生.
    如果需要许多小段的字符串构建一个字符串,那么应该按照下列步骤进行.首先构建一个空的字符串构建器:
StringBuilder builder = new StringBuilder();
    当每次需要添加一部分内容时,就调用append方法.
builder.append(ch);             // appends a single characterbuilder.append(str);            // appends a string
    在需要构建字符串时就调用toString方法,将可以得到一个String对象,其中包含了构建器中的字符序列.
String completedString = builder.toString();
    下面的API注释包含了StringBuilder类中的重要方法.
StringBuilder()
    构造一个空的字符串构建器
int length()
    返回构建器或缓冲器中的代码单元数量
StringBuilder append(String str)
    追加一个字符串并返回 this
StringBuilder append(char c)
    追加一个代码单元并返回 this 
StringBuilder appendCodePoint(int cp)
    追加一个代码点,并将其转换为一个或两个代码单元并返回 this
void setCharAt(int i, char c)
    将第 i 个代码单元设置为 c
<pre name="code" class="java">StringBuilder insert(int offset, String str)
    在offset位置插入一个字符串并返回 this
StringBuilder insert(int offset, char c)
    在offset位置插入一个代码单元并返回 this
StringBuilder delete(int startIndex, int endIndex)
    删除偏移量从startIndex到endIndex-1 的代码单元并返回 this
String toString()
    返回一个与构建器或缓冲器内容相同的字符串
0 0