Java中的这些String特性可能需要了解下

打印 上一主题 下一主题

主题 910|帖子 910|积分 2730

先总结下,String类具有以下特性:

  • 不可变性(Immutable):String对象一旦创建就不能被修改。任何对String对象的操纵都会返回一个新的String对象,原始对象保持稳定。
  • 字符串表(String Table):StringTable表是一种存储字符串常量的内存区域,它可以提高字符串的重用率和性能。在创建字符串时,如果字符串已经存在于池中,则返回池中的字符串对象,否则会创建一个新的字符串对象并放入池中。
  • 值通报:在Java中,String对象是通过值通报的方式通报的。这意味着当将一个字符串通报给方法或赋值给另一个变量时,实际上通报的是字符串的副本而不是原始字符串对象。
下文将详细阐明这些特性。
本文基于JDK17阐明。
不可变性(Immutable)

String的不可变性指的是一旦创建了String对象,它的值就不能被修改。
这意味着在任何对String对象进行操纵时,都会返回一个新的String对象,而原始对象的值保持稳定。
这种特性有助于保护数据的同等性,并且在多线程环境下也更加安全。
下面是一个示例来阐明String的不可变性:
  1. public class ImmutableStringExample {
  2.     public static void main(String[] args) {
  3.         String original = "Hello";
  4.         String modified = original.concat(", World!");
  5.         
  6.         System.out.println("Original string: " + original);
  7.         System.out.println("Modified string: " + modified);
  8.     }
  9. }
复制代码
输出结果为:
  1. Original string: Hello
  2. Modified string: Hello, World!
复制代码
在这个例子中,虽然使用了 concat 方法对原始字符串进行了修改,但是原始字符串 original 的值并没有改变。相反,concat 方法返回了一个新的字符串对象,此中包含了修改后的值。
此中concat函数的主要代码如下:
  1. @ForceInline
  2. static String simpleConcat(Object first, Object second) {
  3.     String s1 = stringOf(first);
  4.     String s2 = stringOf(second);
  5.     if (s1.isEmpty()) {
  6.         // 直接返回s2参数
  7.         return new String(s2);
  8.     }
  9.     if (s2.isEmpty()) {
  10.         // 直接返回s1参数
  11.         return new String(s1);
  12.     }
  13.     // start "mixing" in length and coder or arguments, order is not
  14.     // important
  15.     long indexCoder = mix(initialCoder(), s1);
  16.     indexCoder = mix(indexCoder, s2);
  17.     byte[] buf = newArray(indexCoder);
  18.     // prepend each argument in reverse order, since we prepending
  19.     // from the end of the byte array
  20.     indexCoder = prepend(indexCoder, buf, s2);
  21.     indexCoder = prepend(indexCoder, buf, s1);
  22.     // 返回新建的String对象
  23.     return newString(buf, indexCoder);
  24. }
复制代码
String的不可变性对于计划具有很多长处。

  • 提供了一种简单且安全的数据结构,由于任何时候都可以确信一个String对象的值不会在不经意间被改变。
  • 由于String是不可变的,所以它们可以被安全地共享,而不必担心在共享的过程中被修改。
  • String的不可变性也有助于提高字符串操纵的性能,由于它可以避免频仍的复制和重建字符串对象。
String的不可变性使得它在Java中成为一种简单、安全且高效的数据结构。
不可变性怎么保证的

String 的不可变性是通过类的计划、内部实现和方法计划来保证的,这种不可变性使得 String 对象在多线程环境下更加安全,并且可以被方便地共享和重用。
String 的不可变性是通过以下几种方式来保证的:

  • String 类的计划:String 类被计划为 final 类,这意味着它不能被继承,也就是说无法创建 String 的子类来修改其行为。如许就防止了通过继承来修改 String 类的方法来改变其不可变性。
  • String 对象的内部实现:String 对象内部使用字节数组 byte[] 来存储字符串的值,而且这个字节数组是被声明为 final 的,即不可修改。一旦一个 String 对象被创建,它的值就会被存储在这个字节数组中,而且这个值是无法被修改的。也没对外提供get、set方法。
  • String 类的方法:String 类中的方法都被计划成不会修改原始对象的值,而是返回一个新的 String 对象,此中包含了修改后的值。好比,对于字符串连接操纵 concat()、子串提取 substring()、巨细写转换 toUpperCase() 和 toLowerCase() 等方法,都会返回一个新的 String 对象,而不会修改原始字符串。
如下是String对象的部分源码,可以看到value和对象都被final修饰。
  1. public final class String
  2.     implements java.io.Serializable, Comparable<String>, CharSequence,
  3.                Constable, ConstantDesc {
  4.     @Stable
  5.     private final byte[] value;
  6.     // ...
  7. }
复制代码
值通报

在Java中,String对象的通报是通过值通报(pass by value)进行的。
这意味着在将String对象通报给方法或赋值给另一个变量时,通报的是对象的副本而不是对象本身。
当你将一个String对象通报给方法时,实际上通报的是对象的引用的副本,而不是对象本身。这意味着方法内部的操纵不会影响原始的String对象,由于它们操纵的是副本。
下面是一个示例来阐明String的值通报:
  1. public class StringValuePassingExample {
  2.     public static void main(String[] args) {
  3.         String original = "Hello";
  4.         modifyString(original);
  5.         System.out.println("Original string after method call: " + original);
  6.     }
  7.     public static void modifyString(String str) {
  8.         str = str + ", World!";
  9.         System.out.println("Modified string inside method: " + str);
  10.     }
  11. }
复制代码
输出结果为:
  1. Modified string inside method: Hello, World!
  2. Original string after method call: Hello
复制代码
在这个例子中,虽然在 modifyString 方法内部对 str 进行了修改,但原始的 original 字符串并没有受到影响。这是由于在方法调用时,通报的是 original 字符串的副本,而不是原始对象本身。
因此,在方法内部对 str 的任何修改都不会影响原始的 original 字符串。
字符串存储在StringTable

StringTable是一种特殊的内存区域,用于存储字符串常量。
当创建字符串时,如果该字符串已经存在于StringTable中,则直接返回对该字符串的引用,而不会创建新的字符串对象;如果该字符串不在StringTable中,则会创建一个新的字符串对象,并将其添加到StringTable中。
如果字符串是动态创建的,好比通过new、concat、substring、toUpperCase动态创建的会放到堆内存中。
StringTable、字符串、堆的表示图如下所示:

StringTable的计划有几个主要缘故原由:

  • 节省内存空间:由于字符串常常是应用程序中使用的不可变的常量,因此可以被多个字符串引用。通过StringTable,可以确保相同的字符串常量在内存中只有一份拷贝,从而节省内存空间。
  • 提高性能:由于字符串常量在内存中只有一份拷贝,所以可以通过比较字符串的引用所在来比较字符串的内容,而不必比较字符串的实际内容,如许可以提高比较字符串的效率。
  • 保证字符串的唯一性:通过StringTable,可以确保在应用程序中使用的字符串常量是唯一的,这有助于减少由于字符串拼接等操纵而引起的错误。
  • 方便字符串的共享和重用:由于StringTable中的字符串常量是唯一的,因此可以方便地共享和重用字符串常量,从而提高应用程序的性能和效率。
字符串比拼
  1. import java.util.HashMap;
  2. public class StringTableDemo {
  3.     public static void main(String[] args) {
  4.         String str1 = "abc";
  5.         String str2 = new String("abc");
  6.         System.out.println(str1 == str2);//false
  7.         String str3 = new String("abc");
  8.         System.out.println(str3 == str2);//false
  9.         String str4 = "a" + "b";
  10.         System.out.println(str4 == "ab");//true
  11.         String s1 = "a";
  12.         String s2 = "b";
  13.         String str6 = s1 + s2;
  14.         System.out.println(str6 == "ab");//false
  15.         String str7 = "abc".substring(0, 2);
  16.         System.out.println(str7 == "ab");//false
  17.         String str8 = "abc".toUpperCase();
  18.         System.out.println(str8 == "ABC");//false
  19.         String s5 = "a";
  20.         String s6 = "abc";
  21.         String s7 = s5 + "bc";
  22.         System.out.println(s6 == s7.intern());//true
  23.     }
  24. }
复制代码
通过以上的例子可以总结出以下规律:

  • 单独使用""引号创建的字符串都是常量,编译期就已经确定存储到StringPool中。
  • 使用new String("")创建的对象会存储到heap中,是运行期新创建的。
  • 使用只包含常量的字符串连接符如"aa"+"bb"创建的也是常量,编译期就能确定已经存储到StringPool中。
  • 使用包含变量的字符串连接如"aa"+s创建的对象是运行期才创建的,存储到heap中。
  • 运行期调用String的intern()方法可以向String Pool中动态添加对象。
关于作者

来自全栈程序员nine的探索与实践,连续迭代中。
欢迎关注和点赞~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表