首页 > 代码库 > 深入分析Java的String类的方法与特点

深入分析Java的String类的方法与特点

字符串是任何编程语言都必须支持的变量类型,有些编程语言是直接提供了原生的变量类型,有些编程语言则使用语法特性以 SDK 的形式提供支持。在Java编程平台中,对字符串的支持使用了后者的形式,就是通过在 JDK中提供一个名为String的类,对应字符串这个变量类型。

源码分析

既然JDK中的String类对应了字符串变量类型,为了熟练地掌握Java中字符串相关的技能,我们必须深入地分析和研究一下这个类。编码界有一句名言叫做 “源码面前,了无秘密”,因此,我们第一步就是来看看String类的源码概括。重点部分摘录如下:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
        /** Cache the hash code for the string */
       private int hash; // Default to 0
}

我们得出的几个重点是:

  1. String类的底层使用 char 的数组保存数据。
  2. String类是一个 final 类,不允许被继承。
  3. String类是一个 immutable 类,该类的对象生成后,内容不会发生变化。该类中的所有返回String类型对象的成员方法都是返回一个新的String对象

JVM内存模型

Java作为一门半编译半解释或者即编译又解释的编程语言,Java源码文件需要先被编译器编译成 ByteCode(字节码) 文件,然后在 JVM(Java虚拟机) 上解释执行。为了理解和掌握String类的特性,必须清楚地知道JVM的内存模型。对于字符串类型,也就是String类,JVM从编译源码到执行字节码的整个过程中,都做了特定的调整与优化,正是这些调整与优化造成了String类与对象的一些诡异特性。

关于JVM内存模型的分析与理解,可以参看我的另一篇文件或者通过其他的书籍与资料学习。与本文内容紧密相关的两个知识点总结如下:

  1. JVM的内存模型中有一块区域名为 方法区, 用于存储加载的类信息、方法体和各种符号表。
  2. 方法区中有一块区域名为 常量区, 用于存储编译时和运行时的字符串常量。

方法剖析

String类作为对应字符串的类,该类中含有大量的方法用来完成字符串相关的构造、裁剪、拼接与替换等功能。具体而言,在 JDK8 时,该类中已经有多于80个方法,好在其中有大部分都是重载方法。我们应该按照他们的功能把他们分类如下:

方法名功能方法名功能
String 构造 codePoint* 取值
length 长度 getChars 取值
isEmpty 判空 getBytes 取值
charAt 取值 *equals* 判等
compareTo* 比较 regionMatches 正则
startWith 判断 *indexOf 取值
substring 截取 concat 拼接
replace* 替换 matches 正则
contains 包含 split 分割
join 拼接 to* 转换
trim 去空格 format 格式化
*valueOf 转换 intern 获取

特性剖析

由于字符串类型的特殊性和频繁性,出于功能和效率的考虑,Java在处理字符串类型时,提供了几个重要的特性。

equals与==

  1. 对于 ==,如果作用与基本数据类型(byte、short、char、int、long、float、double、boolean)的变量,则比较的是其存储的“值”是否相等;如果作用与引用类型的变量,则比较其所指向的对象的地址是否相同(即是否同一个对象)。在Java中,String是引用类型。
  2. String的 equals 方法继承自Java中的超级父类Object,Object的equals方法用来比较两个对象的引用是否相等(即是否同一个对象)。但是,String的equals方法不仅是简单地继承,而是进行了重写(Override),用来比较两个String对象所存储的字符序列值是否相等。

创建方式

对于Java中的类而言,创建对象的方式一般有 5种。它们分别是 new 关键字、Class类的 newInstance 方法、Constructor类的 newInstance 方法、String对象的 clone方法、反序列化机制。但是String对象还有一种特殊的创建方式,就是通过使用  或  包裹字符序列。现在,我们重点关注一下 new关键字 与 字符序列 这两种创建String对象的方式的异同。

我们直接以代码实例的方式来学习两种方式的优缺点。首先看代码片段一:

String strA = “www.tiantianbianma.com”;

String strB = new String(“www.tiantianbianma.com”);

String strC = new String(“www.tiantianbianma.com”);
System.out.println(strA.equals(strB));
 System.out.println(strA == strB);
System.out.println(strA.equals(strC));
System.out.println(strA == strC);
System.out.println(strB.equal(strC));
System.out.println(strB == strC);

建议思考得出答案后,再上机验证结果。正确的答案是 true、false、true、false、true、false。他们在JVM中的内存布局简图如下:技术分享

对照此图,答案就很清楚了。

编译优化

直接来看代码片段二:

final String str = “ma.com”;

String strA = “www.tiantianbianma.com”;
String strB = “wwww.tiantian” + “bianma.com”;
String strC = “www.tiantianbian” + str;
System.out.println(strA.equals(strB));
System.out.println(strA == strB);
System.out.println(strA.equals(strC));
System.out.println(strA == strC);

建议思考得出答案后,再上机验证结果。正确的答案是 true、true、true、true,希望没有出乎你的意料。背后的逻辑是Java编译器在编译源码时,对于编译时就可以确定的字符常量,包括字符序列和final字符变量,会自动进行拼接优化。

intern方法

String类中最诡异的一个方法就是 intern,还是先来看代码片段三:

String strA = “www.tiantianbianma.com”;
String strB = new String(“www.tiantianbianma.com”);
System.out.println(strA.intern().equals(strB.intern()));
System.out.println(strA.intern() == strB.intern());

建议思考得出答案后,再上机验证结果。正确的答案是 true、true。背后的原理是:无论是字符串常量区中的String对象,还是堆内存中的String对象,它们的intern方法都是去JVM中的字符串常量区获取相等字符序列的String对象返回。上述代码片段的JVM内存布局简图如下:

技术分享

对照此图,答案就很清楚了。

总结

本文通过从源码入手,再到方法和特性的分析,基本上覆盖了Java中String类的重点和难点。特别是其中的不可变特性与编译优化特性,更是在实际项目中和笔试面试题中经常遇到,还有就是intern方法的诡异性。当然了,String的这些特性也不是完美的,不可变特性在大量拼接字符串时就会带来性能的极大损耗,所以需要使用StringBuilder 类或者 StringBuffer 类来代替。另外,本文对于String类中的方法的具体功能与注意点都没有仔细分析,但需要读者熟练掌握,无论对于笔试面试还是实际工作都大有裨益。

声明: 本文转载自IT技术分析网站: 天天编程,转载自 http://www.tiantianbianma.com/deep-insight-java-string-class/

深入分析Java的String类的方法与特点