小瓜牛漫谈 — String

来源:互联网 发布:淘宝天猫买药可靠吗 编辑:程序博客网 时间:2024/05/21 21:33

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 对象要比直接使用字符串字面值的开销更加的大。


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"。


可以使用 java 自带的反编译工具 javap 来查看编译后的字节码文件信息: javap -c Appliaction


从上面的图来看:

第 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 Kitty5 }

从上面示例可以看出, 调 trim() 方法只是会忽略字符串的前导空白和尾部空白, 对于串中间的空白是不会被处理的。
原创粉丝点击