String 类在 Java 中代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("cde");
System.out.println(str1);
System.out.println(str2);
}
在上面代码中, 第 4 行实际上创建了两个 String 对象, 一个是 "cde" 本身, 另外一个则是由 new 关键字为对象申请开辟的内存空间。
[ 可结合文章最下面给出的第 10 条来理解 ]
通常, 使用 String(String original) 构造器来创建 String 对象要比直接使用字符串字面值的开销更加的大。
package net.yeah.fancydeepin.string;
public class Application {
public static void main(String[] args) {
String str = "abc";
str += "cde";
System.out.println(str);
}
}
当程序运行时, JVM 内存中的分配看起来应该像:
从上面的图来看, str 最初引用的是 "abc" 对象, 最终打印输出的结果是 abccde, 这并不是说 str 所引用的对象的内容发生了变化,
而是 str 在执行的过程中重新引用了另外的一个 String 对象 "abccde"。
从上面的图来看:
第 0 行, 将常量池中的 "abc" 对象压栈;
第 8 行, 调 String.valueOf(Object obj) [ 实际上是将 str 转成了 String 对象 ];
第 3、11 行, 是在创建 StringBuilder 对象, 通过 StringBuilder(String str) 构造器 [ 参数是第 8 行的 String 对象 ];
第 14 行, 将常量池中的 "cde" 对象压栈;
第 16 行, 调 StringBuilder 的 append 方法 [ 将 "cde" 拼在 "abc" 的后面 ];
第 19 行, 调 StringBuilder 的 toString() 方法。
使用 jad 工具, 可以更加容易的去读懂编译后的字节码文件内容: jad -o -a -s .java Application.class
结合上面的图可以看出, 在 java 中, 通过使用 "+" 符号来串联字符串的时候, 实际上底层会转成通过 StringBuilder 实例的 append() 方法来实现。
[ 关于对 String 类使用 "+" 符号来串联字符串, 在文章最下面的第 10 条继续来补充。 ]
String 类常用方法:
1> startsWith(String prefix)、endsWith(String suffix)
startsWith(prefix) 测试字符串是否是以指定的前缀 prefix 开始, endsWith(suffix) 测试字符串是否是以指定的后缀 suffix 结束:
public static void main(String[] args) {
String url = "/small-snail/archive/20130421.html";
System.out.println(url.startsWith("/small-snail/archive/")); //true
System.out.println(url.endsWith(".html")); //true
System.out.println(url.startsWith("/small-snail/category/")); //false
System.out.println(url.endsWith(".php")); //false
}
2> equals(Object anObject)
在 java 中, Object 是一个顶级类, 所有类都直接或间接或默认的继承了该类。
Object 类有一个 equals(Object obj) 方法, 因此, 所有类都默认的拥有了这个方法。
但 Object 的 equals(obj) 方法默认比较的是两个引用变量所引用的对象是否相同, 只有当两个引用变量引用了相同的一个对象的时候才会返回 true。
String 类重写了 Object 类的此方法, String 类的 equals 方法比较的是两个 String 对象的内容是否相同。
package net.yeah.fancydeepin.string;
public class Application {
public static void main(String[] args) {
String kitty1 = new String("HelloKitty");
String kitty2 = new String("HelloKitty");
StringBuilder kitty3 = new StringBuilder("HelloKitty");
System.out.println(kitty1.equals(kitty2)); //true
System.out.println(kitty1.equals(kitty3)); //false
}
}
第 11 行, 虽然 kitty3 的内容与 kitty1 的内容一样, 但由于 kitty3 不是一个 String 对象, 因此调 equals 方法的返回值为 false。
3> equalsIgnoreCase(String anotherString)
比较两个 String 对象的内容是否相同, 忽略大小写。
public static void main(String[] args) {
String param1 = "helloKitty";
String param2 = "HelloKitty";
System.out.println(param1.equals(param2)); //false
System.out.println(param1.equalsIgnoreCase(param2)); //true
}
4> getBytes(String charsetName)、String(byte[] bytes)
getBytes(String charsetName) 是使用指定的字符集 charset 将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
package net.yeah.fancydeepin.string;
import java.io.UnsupportedEncodingException;
public class Application {
public static void main(String[] args) throws UnsupportedEncodingException {
String param = "哈喽Kitty";
String charset;
charset = new String(param.getBytes("GBK")); //乱码
System.out.println(charset);
charset = new String(param.getBytes("GB2312")); //乱码
System.out.println(charset);
charset = new String(param.getBytes("ISO-8859-1")); //乱码
System.out.println(charset);
charset = new String(param.getBytes("UTF-8")); //正常
System.out.println(charset);
charset = new String(param.getBytes("UTF-16")); //乱码
System.out.println(charset);
}
}
上面给出来的是 java 开发过程中比较经常遇到的字符集编码。当一个字符串中含有中文字符的时候, 如果该字符串在编码和解码前后所使用的字符编码不一致,
就会导致中文乱码的问题。
由于我的 eclipse SDK 工作区所使用的是 UTF-8 编码, 所以上面只有 "UTF-8" 字符编码输出的内容是正常的, 其他情况就会出现中文乱码的问题。
5> getBytes(String charsetName)、String(byte[] bytes, String charsetName)
上面示例中出现了中文乱码问题, 其实现在网络上关于中文乱码这点事儿, 资料已经是非常的多了。下面接下来将首先模拟出一个中文乱码的问题, 然后来解决它:
package net.yeah.fancydeepin.string;
import java.io.UnsupportedEncodingException;
public class Application {
public static void main(String[] args) throws UnsupportedEncodingException {
String param = "哈喽Kitty";
param = new String(param.getBytes("UTF-8"), "ISO-8859-1"); //中文乱码
System.out.println(param);
//解决中文乱码
param = new String(param.getBytes("ISO-8859-1"), "UTF-8"); //恢复正常
System.out.println(param);
}
}
param = new String(param.getBytes("UTF-8"), "ISO-8859-1"); 意思是说:
将 param 以 UTF-8 编码方式去编码, 然后再按 ISO-8859-1 编码方式去解码编码后的内容, 来构造一个新的 String 对象 param。
由于 param 原本是按 UTF-8 编码方式编码出来的, 现在却使用 ISO-8859-1 编码方式去解码, 这个时候出现中文乱码是很正常的事情。
再者, ISO-8859-1 的编码方式本身是不支持中文的。
解决中文乱码问题, 无非就是使用正确的字符集编码去解码字符串的内容:
param = new String(param.getBytes("ISO-8859-1"), "UTF-8");
首先是将 param 以 ISO-8859-1 的编码方式编码出来, 因为在 java 的 JVM 中, 任何 String 都是一个 unicode 字符串,
接着再使用 UTF-8 去解码, 这个时候的中文就不再是乱码啦。。
为了避免引起误解, 补充说明一下, 上面不是一定要使用与 IDE 相同的编码方式 UTF-8 才不会引起中文乱码, 实际上也可以换成 GBK、GB2312 等兼容中文的
编码方式也是可以的, 只需要保证编码和解码使用的是相同的字符集编码方式即可。
6> indexOf(String str)、lastIndexOf(String str)、substring(int beginIndex, int endIndex)
indexOf 用于返回指定的子字符串在主字符串中第一次出现处的索引值; lastIndexOf 用于返回指定的子字符串在主字符串中最后一次出现处的索引值。
substring 则是用来切割主字符串, 根据开始索引值和结束索引值切割并返回一个新字符串。
package net.yeah.fancydeepin.string;
public class Application {
public static void main(String[] args) {
String param = "archive.logo.ico";
int firstIndex = param.indexOf("a");
int lastIndex = param.lastIndexOf("o");
int length = param.length();
System.out.println(firstIndex); // 0
System.out.println(lastIndex); // 15
System.out.println(length); // 16
System.out.println(param.substring(firstIndex, lastIndex)); // archive.logo.ic
param = param.substring(param.lastIndexOf("."), length); // .ico
System.out.println(param);
}
}
从上面代码可以看出, 索引值是从 0 开始的, substring(beginIndex, endIndex) 方法切割字符串的区间其实是左闭右开: [ beginIndex, endIndex )
7> replaceAll(String regex, String replacement)
用子字符串 replacement 来替换主字符串中所有由正则表达式 regex 匹配的子字符串。
package net.yeah.fancydeepin.string;
public class Application {
public static void main(String[] args) {
String packageName = Application.class.getPackage().getName();
String packagePath1 = packageName.replaceAll(".", "/"); //将所有的字符换成了'/'
System.out.println(packagePath1); // 打印 ///
String packagePath2 = packageName.replaceAll("\\.", "/"); //将所有的'.'换成'/'
System.out.println(packagePath2); // net/yeah/fancydeepin/string
String packagePath3 = packageName.replaceAll("e+", "E"); //凡是出现'e'一次或以上的用'E'替换
System.out.println(packagePath3); // nEt.yEah.fancydEpin.string
}
}
上面代码中需要注意的是 packagePack1, replaceAll 的第一个参数使用的是正则表达式, 正则表达式中的 '.' 可以匹配除“\n”之外的任何单个字符,
因此 packagePath1 打印输出的全是反斜杠'/', 如果要匹配'.', 则应该使用转义字符, 像上面代码中的 packagePath2。
8> split(String regex)
根据给定的正则表达式 regex 将主字符串拆分成一个字符串数组。
package net.yeah.fancydeepin.string;
public class Application {
public static void main(String[] args) {
String param = "Java,Android,PHP,C,C++,C#";
String[] languages = param.split(",");
for(String language : languages){
System.out.println(language);
}
}
}
9> trim()
忽略字符串的前导空白和尾部空白。
public static void main(String[] args) {
2
3 String param = " Hello Kitty ";
4 System.out.println(param.trim()); //Hello Kitty
5 }
从上面示例可以看出, 调 trim() 方法只是会忽略字符串的前导空白和尾部空白, 对于串中间的空白是不会被处理的。