关于 Java String 的 5 个事实






3.96/5 (6投票s)
发现关于 Java String 的一些有趣事实
引言
String
是 Java 编程语言的基础之一。它不像 int
、long
或 double
那样是原始数据类型。它定义在 java.lang
包中,并将其内容封装在一个字符数组中。
在这篇文章中,我们将发现一些有趣的 String
事实,这些事实可以帮助我们更好地理解这个流行类的行为。
1. String 是不可变的
编程中有一个强大而简单的概念,它被严重低估了:不可变性。
基本上,一个对象在其创建后状态不会改变,那么它就是不可变的。因此,如果一个类的实例是不可变的,那么这个类就是不可变的。
使用不可变对象的有一个杀手级论据:它极大地简化了并发编程。想想看,为什么编写正确的并发编程是一项艰巨的任务?因为同步多个线程对资源的访问(对象或其他操作系统事物)很难。为什么同步这些访问很难?因为很难保证多个线程对多个对象进行读写访问时不会发生竞态条件。如果不再有写访问怎么办?换句话说,如果线程访问的对象的状态不会改变怎么办?那就无需同步了!
不可变类也适合用作哈希表的键。用于计算哈希值的对象必须是不可变的,以确保哈希值在一段时间内保持不变。事实上,哈希值是从对象的状态计算出来的。
String
是不可变的。当你认为你在修改一个 string
时,你实际上是在创建一个新的 string
对象。我们经常忘记这一点,我们会想写……
String str = ”foofoo”;
str.replace(“foo”, “FOO”);
…而我们实际上应该写
str = str.replace(“foo”, ”FOO”);
当然,这样做会以在进行一些密集的 string
计算时创建多个 string
对象为代价。在这种情况下,你需要使用 StringBuilder
类。
对于 Scala 这样的某些现代语言,倾向于使用不可变性。默认的集合类是不可变的。
2. String 是 JVM 中最受欢迎的类
了解项目中使用的最多的类型很有趣,因为这些类型必须设计、实现和测试良好。它们中的任何更改都可能影响整个项目。
让我们使用 JArchitect 分析 JVM,并查找最受欢迎的类型。为此,我们可以使用两个指标:
类型传入耦合(TypeCa):一个特定类被使用的类型数量。这是根据此指标所有受欢迎类型的计算结果:
不出所料,Object
是使用最多的,但还有另一个有趣的指标可以用来查找受欢迎的类型:TypeRank
。
TypeRank
值是通过在类型依赖图上应用 Google PageRank 算法计算得出的。应用 0.15 的同位度中心值,使 TypeRank
的平均值为 1
。
具有高 TypeRank
的类型应该更仔细地测试,因为这些类型中的错误可能会更具灾难性。
这是根据 TypeRank
指标所有受欢迎类型的计算结果:
使用更准确的 TypeRank
指标来查找受欢迎的类型,String
在 JVM 代码库中比 Object 更受欢迎。
3. String 是稳定的
建议最受欢迎的代码元素必须是稳定的,因为任何更改都可能影响许多其他类型。让我们看看 String
是否如此,为此我们可以比较 2004 年 9 月 30 日发布的 JVM 5 和 2013 年 10 月 15 日发布的 JVM 7 的最后一个更新。
添加的方法
这是 9 年间添加的方法:
仅添加了 3 个构造函数和 5 个方法。
已弃用的方法
自 2004 年以来,仅有一个构造函数被弃用。
4. String 设计用于优化内存使用
String
在 Java 中受到特殊处理,因为它们在程序中经常使用。因此,效率(计算和存储方面)至关重要。
有两种构造 string
的方法:通过赋值 string
字面量进行隐式构造,或通过 new 运算符和构造函数显式创建 String
对象。例如:
String s1 = “Hello”; // String literal
String s2 = new String(“Hello”); // String object
Java 提供了一种特殊的机制来保存 String
字面量——在一个所谓的 string
公共池中。如果两个 string
字面量的内容相同,它们将在公共池中共享相同的存储。这种方法是为了节省常用 string
的存储空间。另一方面,通过 new 运算符和构造函数创建的 String
对象被保存在堆中。堆中的每个 String
对象都有自己的存储,就像任何其他对象一样。即使两个 String
对象的内容相同,堆中也没有存储共享。
有关 String
池机制的更多详细信息,您可以参考这篇 文章。
5. String 设计用于优化 CPU 使用
我们可以列举两种 CPU 优化情况:
- 尽可能执行最少量的代码
以toLowerCase(Locale locale)
方法为例,以下是其实现的前几行:public String toLowerCase(Locale locale) { if (locale == null) { throw new NullPointerException(); } ……
locale
在方法体早期被使用,如果指针为null
,JVM 会抛出NullPointerException
,并且显式抛出此异常的情况非常少见。然而,String
中的许多方法都使用这种技术来避免在异常情况下执行更多代码,从而可以最大限度地减少 CPU 使用。 isEmpty
而不是equals(“”):
isEmpty
是在 Java 6 中引入的,它比equals(“”)
更快,因为它只是将string
的长度——存储在String
对象中——与零进行比较。public boolean isEmpty() { return 0 == count; }