【问题标题】:Where are string objects when created using tostring methods stored in memory in Java?使用存储在 Java 内存中的 tostring 方法创建的字符串对象在哪里?
【发布时间】:2015-07-24 10:30:42
【问题描述】:

我正在做一项作业,我得到了以下问题。

使用toString()方法创建的字符串对象在内存中存储在哪里?

String a = Integer.toString(10);
  1. 在常量池中
  2. 在堆上(新操作符对象的区域)

【问题讨论】:

    标签: java string


    【解决方案1】:

    它们都进入堆内存。只有 字符串字面量interned 字符串进入字符串常量池。

    唯一的例外是像String这样的类:

    public String toString() {
        return this;
    }
    

    它只返回当前字符串(如果它在堆上,它从堆中返回/如果它在字符串常量池中,它从字符串常量池返回)

    注意:如果toString() 未被覆盖以显式返回字符串文字,则字符串表示(ClassName@hexValueOfHashCode)总是在堆上创建。 p>

    【讨论】:

    • 还有例如Boolean 返回一个文字。
    • 这是关于实例toString() 方法,而不是静态方法Integer.toString()。还是您的意思是两者都适用?
    • @DanGetz - 一般而言...toString() 不保证实习字符串并返回它们..
    【解决方案2】:

    这取决于您调用的toString() 方法的实现。

    toString() 方法创建 String 对象,因此取决于它如何执行此操作(以及它是否实习字符串),返回的 String 将在字符串池中与否。

    请注意,toString() 只是一个与任何其他方法一样的方法。没有任何魔法可以以任何特殊方式处理toString() 方法的返回值。 (例如,toString() 返回的字符串不会自动实习)。它的工作方式与任何其他返回 String 的方法完全相同。

    【讨论】:

    • 嗨,jesper,我理解您对 tostring 方法的看法。我这里的问题与Integer的class to string方法有关...代码sn-p如下:for(int i=10;i>=0;i--){ String a = Integer.toString(1); System.out.println(tmp);我必须找出有多少字符串对象可用于垃圾收集..当代码无法到达时..即在循环之外
    【解决方案3】:

    这是一个奇怪的(我会说很糟糕)面试问题,因为这个问题的答案取决于 JDK 的实现。这个实现是可以改变的,不是普通人应该知道的,因为要确定,你需要询问实现者或阅读他们的源代码。此外,从 Java 7 开始,字符串池(我假设这是“常量池”的意思)在堆上。所以即使它在字符串池中,它仍然在堆上。

    目前(OpenJDK 8u40-b25),String 将是在堆内存中创建的新对象:

    public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
    }
    

    因为您传递的是10,而不是Integer.MIN_VALUE,所以会创建一个新的String 对象。这个字符串的内容是一个char 数组,它是用new 创建的,所以存在于堆上。 intern() 没有被调用,所以它没有被放入字符串池中。


    您可以通过使用 ==Strings 测试它们是否是同一个对象而不是同一个值这一事实来测试您得到的行为。以下将评估为False,因为静态Integer.toString() 方法在每次调用时都会在堆上创建一个新的String 对象:

    Integer.toString(10) == Integer.toString(10)
    

    如果它们被实习到字符串池中,它们将是同一个对象(因为这就是实习的重点——每个字符串只有一个对象)。以下计算结果为True

    Integer.toString(10).intern() == Integer.toString(10).intern()
    

    请注意,这是专门针对静态 Integer.toString() 方法的答案,而不是针对所有 toString() 方法的答案。例如Boolean.toString(),从字符串池中返回字符串。

    【讨论】:

    • 嗨,丹,对不起,如果没有正确询问问题.. 但我确实打算说两者都在堆上......你的 to string 建议在内部用于整数的 tostring 方法,字符串对象像 new 运算符一样创建,因此默认情况下不调用实习生...所以我可以得出结论,如果字符串 a = Integer.toString(1) 保持在 10 个循环中,则创建 10 个不同的字符串对象.. .
    • 嗨,丹,对不起,如果没有正确询问问题.. 但我确实打算说两者都在堆上......你的 to string 建议在内部用于整数的 tostring 方法,字符串对象像 new 运算符一样创建,因此默认情况下不调用实习生...所以我可以得出结论,如果字符串 a = Integer.toString(1) 保持在 10 个循环中,则创建 10 个不同的字符串对象.. .
    • 是的,这就是我的理解。我不是有意批评你的问题。这是一个有趣的问题。我批评这是你收到的作业的问题,因为这不是使用或学习 Java 的人应该知道的。
    • 作业我不是指大学作业..我是一名java专业人士...这是我被问到的面试问题之一(我收集了近10次面试中问我的一系列问题和开始解决它们...只是知道有很多东西要学)...我最后说只有一个对象被垃圾收集...
    • 正是,我的意思是,我认为这是一个不好的面试问题,因为我在回答中给出了原因。 @Boann 的回答对此进行了更详细的说明。
    【解决方案4】:

    给你的问题是有缺陷的。

    首先,两个选项:(1)返回一个常量池中的字符串,和(2)返回一个新创建的字符串,不是互斥的,因为一个方法可以创建一个新的字符串,但是@ 987654321@将其放入常量池中。

    其次,对于toString() 方法如何提供它们的字符串,没有特殊要求。这完全取决于特定 toString() 方法的特定实现。你不能一概而论地说他们都做某件事。

    • 某些方法可能会在每次调用时返回 new String(...)(例如,StringBuilder)。

    • 有些方法可能总是从一小组固定的可能性中返回一个字符串实例,而这些固定的可能性可能也在常量池中(适用于Boolean或对于枚举常量)。

    • 某些方法可能会混合使用。 Integer.toString(int) 可能会使用一组预定义的常量来表示小的常见值,但会返回新的堆字符串以获得更模糊的值。或者它可能会按需生成和缓存字符串,这样Integer.toString(10) 可以第一次返回一个新的堆字符串,但在后续调用时返回 same 堆字符串;在这种情况下,如果 Integer.toString 某个地方的调用者 .intern()s 的返回值,那么 Integer 类的按需缓存实例也可以进入常量池。

    行为也可能因 JVM 风格或 JVM 版本或传递给它的运行时选项而异。

    关于Integer.toString(int) 目前在 OpenJDK 8 中做了什么的具体问题,check the source

    public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
    }
    

    事实证明,它为Integer.MIN_VALUE 返回一个字符串文字(将在常量池中),但为每个其他值返回一个新的堆字符串。 (这可能看起来很奇怪,但在许多编程语言中,数字到字符串的函数通常会以这种方式对最小值进行例外处理,因为对于整数的two's complement 表示,最大值比绝对值小 1最小值(例如,-2,147,483,648 到 +2,147,483,647 的范围),这意味着最小值是唯一不能翻转为正值然后使用与正值相同的共享循环代码进行处理的负值。)

    问题的第三个问题是答案几乎不重要。只有当您发现 toString() 方法的实现不起作用或性能不令人满意时,您才能调查原因,否则,只要它有效且有效地工作,为什么有人要关心它如何缓存字符串或是否是否缓存它们? Java 新手过于担心愚蠢的字符串常量池的细节,这是 Java 决定不为对象使用 == 调用 .equals() 的一个不幸的副作用,这使得常量的行为池更明显。

    当您需要一个肯定在常量池中的字符串时,您可以自己实习,或者将其写为源中的字符串文字。否则,你无法保证它是否在常量池中,也不需要这样的保证。

    基于Integer.toString(int) 的实现,他们可能正在寻找答案2(“在堆上”),但这是对一个有缺陷的问题的有缺陷的答案。

    【讨论】:

      【解决方案5】:

      我认为这应该取决于实施。但是对于来自java.lang.Object 的默认 toString 和许多其他对象的toString 实现仅返回字符串文字。所以我可以告诉你,它们在字符串常量池中,因为默认情况下它们是interned,并且所有内部字符串都将存储在字符串常量池中。

      示例 1

      public String toString() {
         return "my test string";  // will be in string constant pool
      }
      

      示例 2

      public String toString() {
         String s1 = new String("my test string");
         return s1;  // s1 will be in heap
      }
      

      当然我不必告诉你s1.intern() 会将示例字符串放入字符串常量池中(如果该字符串尚不可用)

      【讨论】:

      • 对象的 toString() 不是interned。为什么toString() 只返回文字?。
      • Object.toString() 返回文字显然是错误的。试试new Object().toString(),你会看到的。
      • 你怎么说 object.toString() 不返回文字? Object o = new Object();String s1 = o.toString();String s2 = s1.intern();System.out.println(s1 == s2);这返回真。你这是什么意思?
      • 你是怎么知道的?我很抱歉问。但是无论如何,实习字符串是常量池的一部分的答案是正确的。
      • @DanGetz 即使s == s.intern(),也不一定意味着s 在调用intern() 之前已经被拘留。
      【解决方案6】:

      在 Integer 包装类中,一旦给定的 int 被解析为 char,它将返回一个新的 String 对象。

      return new String(buf, true);
      

      这将在堆内存中创建 1 个字符串对象。

      引用变量“a”将指向堆(普通内存)中的对象。

      但请记住,

      String str = new String("abc"); 
      

      将创建 2 个对象,一个在堆中,另一个在字符串池中。

      编译时常量 String 类型的表达式总是被保留的。

      【讨论】:

        【解决方案7】:

        依赖于实现。但是默认情况下,字符串会转到字符串池。

        案例一:

        public String toString() {
           return "Constant Pool";  // String constant pool
        }
        

        案例 2:

        public String toString() {
           String string2 = new String("Heap String");
           return string2;  // in heap
        }
        

        两个表达式都给你String 对象,但它们之间存在细微差别。

        • 当您使用new() 运算符创建String 时,它总是会在heap memory 中创建一个新对象。
        • 如果您使用字符串字面量语法创建对象,它可能会从 字符串池 中返回一个现有对象(如果该对象已存在)。否则它将创建一个新的字符串对象并放入字符串池中以供将来重复使用。

        当您使用 String literal 表示法创建字符串时,它会自动调用 intern() 方法将该对象放入 String pool(如果它不在池中)。如果是 new,它不会自动发生,直到对该对象调用 intern() 方法。

        【讨论】:

        • 这两个例子都与问题无关,是关于静态方法Integer.toString()
        • 为什么选择@DanGetz?回答者触及了实现依赖的确切点以及它与具体实现的关系。
        猜你喜欢
        • 1970-01-01
        • 2014-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多