字符串

  • java没有内置的字符串类型
  • 在标准 Java 类库中提供的一个预定义类String
  • 每个用双引号括起来的字符串都是 String 类的一个实例

子串

String.subString()方法

  • 可以从一个较大的字符串提取出一个子串
String greeting = "hello";
//提取字符串中位置从0到2的子字符串(不包含3)
String s = greeting.subString( 0, 3);
System.out.println(s);

输出:

hel

拼接

java可以利用‘+’进行拼接

字符串与字符串之间的拼接

String expletive = "Expletive"; 
String PC13 = "deleted";
String message = expletive + PC13;
System.out.println(message);

输出

Expletivedeleted

注意:中间没有空格



字符串与非字符串之间的拼接

非字符串会被转换成字符串

例1:

int age = 13;
String rating = "PG" + age;
System.out.println(rating);

输出:

PG13



这种特性通常用在输出语句中

例2:

System.out.println("The answer is " + answer) ;

使用定界符分割的拼接

  • 使用静态join方法
String all = String.join(" / ", "S", "M", "L", "XL"); 
System.out.println(all);

输出

S / H / L / XL



不可变字符串

java 文档中将 String 类对象称为不可变字符串

原因:

  • String 类没有提供用于修改字符串的方法


那么如何修改字符串呢?

例:

//将greeting字符串修改为"help!"
String greeting = "hello";
greeting = greeting.substring(0, 3) + "p!";
System.out.println(greeting);

输出

help!

操作:

  • 提取需要的字符
  • 再拼接上替换的字符串

那这么做是否会降低运行效率呢?

答案是:也对,也不对。


通过拼接创建新的字符串效率确实不高,但是却可以让字符串共享。
可以想象将各种字符串存放在公共的存储池中,字符串变量指向存储池中相应的位置。 如果复制一个字符串变量, 原始字符串与复制的字符串共享相同的字符。


总而言之, Java 的设计者认为共享带来的高效率远远胜过于提取、 拼接字符串所带来的低效率。



检测字符串是否相等

s.equals(t)

  • 检测两个字符串s和t是否相等,区分大小写
  • 如果相等,返回true
  • 如果不等,返回false
  • 其中s和t可以是字符串变量,也可以是字符串常量

s.equalsIgnoreCase(t)

  • 检测两个字符串s和t是否相等,且不区分大小写


一定不要使用"=="运算符检测两个字符串是否相等

==

  • 这个运算符只能够确定两个字串是否放置在同一个位置上

当然, 如果字符串放置在同一个位置上, 它们必然相等。但是, 完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上

//initialize greeting to a string
String greeting = "Hello"; 
if (greeting == "Hello")
	// probably true
	... ... 
if (greeting.substring(0, 3) == "Hel")
	// probably false
	... ...
  • 如果虚拟机始终将相同的字符串共享
  • 就可以使用"=="运算符检测是否相等
  • 但实际上只有字符串常量是共享的
  • + 或 substring 等操作产生的结果并不是共享的

因此, 千万不要使用 “==” 运算符测试字符串的相等性, 以免在程序中出现糟糕的bug



空串与null串

空串

  • 长度为0的字符串
  • 是一个 Java 对象, 有自己的串长度(0 ) 和内容(空)

检测方式:

if (str.length() = 0 || if (str.equals(""))){
	... ...
}

null值

  • String 变量还可以存放一个特殊的值
  • 表示目前没有任何对象与该变量关联

检测方式:

if (str == null){
	... ...
}

有时要检查一个字符串既不是 null 也不为空串, 这种情况下就需要使用以下条件:

if (str != null && str.length() != 0){... ...}

首先要检查 str 不为 null



码点和代码单元

java字符串由char值序列组成

char数据类型:

  • 一个采用 UTF-16 编码表示 Unicode 码点的代码单元

其中
码点 ( code point ) 是指与一个编码表中的某个字符对应的代码值

  • 在 Unicode 标准中, 码点采用十六进制书写,并加上前缀U+,
  • 例如U+0041就是拉丁字母A的码点


可以引用其中的一个小例子来感受一下

public static void main(String[] args) {
     String hello = "hi𝕆";
     System.out.println(hello.length());//4
     System.out.println(hello.codePointCount(0, hello.length()));//3
 }

其中 length方法将返回采用 UTF-16 编码表示的给定字符串所需要的代码单元数量:

String greeting = "Hello";
int n = greeting.length();
//输出位5

如果想得到实际的长度, 即码点数量, 可以调用

int cpCount = greeting.codePointCount(0, greeting.length());

调用 s.charAt(n) 将返回位置 n 的代码单元, n 介于 0 ~ s.length()-l 之间

char first = greeting.charAtO); // first is 'H'

String API

Java 中的 String 类包含了50多个方法
令人惊讶的是绝大多数都很有用,下面简单介绍一些常用的API

api 接口

说明

char charAt(int index)

返回给定位置的代码单元。

除非对底层的代码单元感兴趣, 否则不需要调用这个方法。

int codePointAt( int Index)

返回从给定位置开始的码点

int compareTo(String other)

按照字典顺序, 如果字符串位于 other 之前, 返回一个负数; 如果字符串位于 other 之后, 返回一个正数; 如果两个字符串相等, 返回 0。

IntStream codePoints()

将这个字符串的码点作为一个流返回。 调用 toArray 将它们放在一个数组中。

new String(int[] codePoints, int offset, int count)

用数组中从 offset 开始的 count 个码点构造一个字符串

boolean equals(0bject other)

如果字符串与 other 相等, 返回 true。

boolean equalsIgnoreCase(String other)

如果字符串与 other 相等 (忽略大小写,) 返回 true

boolean startsWith(String prefix)

boolean endsWith(String suffix)

如果字符串以 suffix 开头或结尾, 则返回 true。

int indexOf(String str)

int indexOf(String str, int fromlndex)

int indexOf(int cp)

int indexOf(int cp, int fromlndex)

返回与字符串 str 或代码点 cp 匹配的第一个子串的开始位置。这个位置从索引 0 或 fromlndex 开始计算。 如果在原始串中不存在 st,r 返回 - 1。

int 1astIndexOf(String str)

Int 1astIndexOf(String str, int fromlndex)

int lastindexOf(int cp)

int lastindexOf(int cp, int fromlndex)

返回与字符串 str 或代码点 cp 匹配的最后一个子串的开始位置。 这个位置从原始串尾端或 fromlndex 开始计算。

int length( )

返回字符串的长度

int codePointCount(int startlndex, int endlndex)

返回 startlndex 和 endludex- l 之间的代码点数量。 没有配成对的代用字符将计入代码点

String replace(CharSequence oldString,CharSequence newString)

返回一个新字符串。 这个字符串用 newString 代替原始字符串中所有的 oldString。 可 以用 String 或 StringBuilder 对象作为 CharSequence 参数

String substring(int beginlndex)

String substring(int beginlndex, int endlndex)

返回一个新字符串。这个字符串包含原始字符串中从 beginlndex 到串尾或 endlndex-l

的所有代码单元。

String toLowerCase( )

String toUpperCase( )

返回一个新字符串。 这个字符串将原始字符串中的大写字母改为小写, 或者将原始字符串中的所有小写字母改成了大写字母。

String trim( )

返回一个新字符串。 这个字符串将删除了原始字符串头部和尾部的空格。

String join(CharSequence delimiter, CharSequence… elements)

返回一个新字符串, 用给定的定界符连接所有元素

在 API 注释中, 有一些 CharSequence 类型的参数, 现在只需要知道只要看到 一个 CharSequence 形参, 完全可以传入 String 类型的实参

构建字符串

出现的问题

  • 需要由较短的字符串构建字符串
  • 采用字符串连接的方式达到此目的效率比较低
  • 每次连接字符串, 都会构建一个新的 String 对象

解决方案

  • 使用 StringBuilder 类

步骤

//1. 构建一个空的字符串构建器
StringBuilder builder = new StringBuilder();
//2. 当每次需要添加一部分内容时, 就调用 append 方法
builder.append(ch); // appends a single character
bui1der.append(str); // appends a string
//3. 在需要构建字符串时就凋用 toString 方法, 将可以得到一个 String 对象
String completedString = builder.toStringO;

StringBuilder类的前身是 StringBuffer, 其效率稍有些低, 但允许采用多线程的方式执行添加或删除字符的操作。 如果所有字符串在一个单线程中编辑 (通常都是这样) , 则应该用 StringBuilder 替代它

参考

java核心卷1