【问题标题】:Strings [= new String vs = ""]字符串 [= 新字符串 vs = ""]
【发布时间】:2013-10-11 16:07:09
【问题描述】:

所以我的问题是关于声明和分配字符串。

我通常声明字符串的方式是执行以下操作:

String s1 = "Stackoverflow";

然后,如果我需要更改 s1 的值,我会执行以下操作:

s1 = "new value";

今天我发现了另一种方法,声明一个字符串会是这样的:

String s2 = new String("Stackoverflow");

然后更改值将是:

s2 = new String("new value");

我的问题是这两者有什么区别还是仅仅是优惠。从第四行看代码

s2 = new String ("new value"); 

我假设这样做会创建一个新的内存位置,然后 s2 会指向它,所以我怀疑它会用于更改值,但我可以看到它在声明字符串时被使用。

【问题讨论】:

  • 你也可以s2 = new String(new String("new value"));
  • new String("") 是缓慢的方式。阅读 [这篇文章 Java 中表达式“new String(…)”的目的是什么?][1] [1]:stackoverflow.com/questions/390703/…
  • @MemLeak 在 cmets 中发布链接使用 [description](link) 格式
  • 尝试阅读 this topic 关于字符串文字池。我认为它会很有用
  • 使用 new String(..) 创建一个新对象,而不是使用您拥有的“文字”。除非你真的理解这一点并且出于某种(奇怪的)原因需要它,否则你不应该这样做new String(..),因为它只会不必要地弄乱堆。

标签: java string


【解决方案1】:

来自the javadoc

初始化一个新创建的 String 对象,使其代表 与参数相同的字符序列;换句话说,新 创建的字符串是参数字符串的副本。 除非明确 需要原件的副本,不需要使用此构造函数 因为字符串是不可变的。

所以不,你没有理由不使用简单的文字。

简单地做

String s1 = "Stackoverflow";

从历史上看,此构造函数主要用于获取通过拆分较大字符串获得的字符串的较轻副本(请参阅this question)。现在,没有正常的理由使用它。

【讨论】:

  • 你能告诉我在什么情况下需要明确复制他的原件吗?
  • Java 7 改变了 String.substring() 的工作方式。以前的版本保留对相同支持 char[] 的引用,因此如果您有一个巨大的字符串,然后将该大字符串创建一个非常小的子字符串,那么大字符串仍将保留在内存中并且无法被垃圾收集(可能是内存泄漏)。通过使用new String(String),您将创建一个新的较小的支持 char[],并且如果您不再引用它,则能够对大字符串进行垃圾收集。
  • 实习怎么样?这两种方法是否表现出相同的行为? (我认为new 必须始终返回一个唯一的对象,但我可能错了。)
  • @TheodorosChatzigiannakis 您什么时候需要获取不同的实例?如果需要,您的代码中可能存在错误。
  • @TheodorosChatzigiannakis 我认为在 new 之后调用 intern() 会修改内存,因此两个副本将再次共享相同的引用。
【解决方案2】:
String s1 = "Stackoverflow"; // declaring & initializing s1

String s2 = new String("Stackoverflow"); // declaring & initializing s2

在上述情况下,您正在声明和初始化 String 对象。

两者的区别

//first case

String s2 = new String("new String"); // declaring & initializing s2 with new memory

// second case

s2 = "new String" // overwriting previous value of s2

在第一种情况下,您正在 创建一个新对象,即;为将由 s2 引用的新对象分配内存。 s2 指向/引用的先前地址尚未释放内存,当程序结束或系统需要时,该内存将被 gc。

良好的编程习惯(第二种情况)是初始化一个对象一次,如果你想改变它的值,给它赋空那么为它分配新内存,或者如果是字符串,你可以这样做

s2= "new String";

【讨论】:

    【解决方案3】:
    String s1 = "Stackoverflow";
    

    如果字符串池尚不存在,此行将在字符串池中创建一个新对象。这意味着首先它将首先尝试在字符串池中搜索“Stackoverflow”,如果找到则 s1 将开始指向它,如果未找到则它将创建一个新对象并 s1 将引用它。

    String s2 = new String("Stackoverflow");
    

    将始终创建一个新的 String 对象,无论该值是否已存在于 String 池中。因此堆中的对象将具有值“Stackovrflow”,并且 s2 将开始指向它。

    s2 = new String("new value");
    

    这将再次创建一个新对象,并且 s2 将开始指向它。 s2 指向的较早对象未对垃圾回收 (gc) 打开。

    如果这有帮助,请告诉我。

    【讨论】:

      【解决方案4】:

      主要区别在于构造函数总是创建一个全新的 String 实例,其中包含与原始 String 相同的字符。

      String s1 = "Stackoverflow";
      String s2 = "Stackoverflow";
      

      那么s1 == s2会返回true

      String s1 = "Stackoverflow";
      String s2 = new String("Stackoverflow");
      

      那么s1 == s2会返回false

      通常只使用双引号会更好:

      • 使用构造函数,您可以创建两个 String 实例。
      • 更易于阅读且不易混淆

      【讨论】:

        【解决方案5】:

        @user2612619 在这里我想说的是.. 当你使用“new”运算符创建对象时,它总是落在堆内存中。所以无论你有相同的内容但不同的对象,它都会在堆上创建新的对象,这样你就无法节省内存......

        但是为了节省内存,java 人们提出了不可变的概念,我们可以在其中节省内存.. 如果您创建具有相同内容的不同对象.. 字符串将识别数据差异并仅创建一个具有相同内容的对象并将两者指向只引用一个对象..

        我可以从这张图解决你的疑惑..

        案例一:

        String s = new String("stackoverflow");
        String s1 = new String("stackoverflow");
        

        因为它们是堆内存上的两个不同对象,具有两个不同的哈希码值 .. 所以 s==s1 (是引用比较)它是错误的 .. 但 s.equals(s1) 是内容比较 .. 所以它是真的

        案例2:

        String s = "stackoverflow";
        String s1 = "stackoverflow";
        

        这里的对象落入 scp(字符串常量池内存) 两个不同引用的相同对象..所以哈希码也相同..相同的引用值..所以s == s1在这里是真的[你可以从图中清楚地观察] s.equals(s1) 作为内容比较是正确的..这是一个非常漂亮的概念..如果你解决了一些问题,你会喜欢它的......一切都是最好的

        【讨论】:

        • 你应该拼出你的(你的?)单词。
        • your = ur @scott_fakename .. 呵呵,我在写作时使用捷径.. 我总是使用完整的形式??
        【解决方案6】:

        直接创建字符串对象:

        String s1 = "Hip Hop" 
        

        将创建一个字符串对象,但首先 JVM 检查 字符串常量或文字 池,如果字符串不存在,它会创建一个新的字符串对象“Hip jop”并且在池中维护一个参考。变量s1 也指向同一个对象。现在,如果我们在此之后添加一个声明:

         String s2 = "Hip Hop"
        

        JVM 首先检查字符串常量池,因为字符串已经存在,所以对池实例的引用返回到s2

        System.out.println(s1==s2) // comparing reference and it will print true
        

        java 可以进行这种优化,因为字符串是不可变的,并且可以共享而不必担心数据损坏。

        使用 new 创建字符串对象

        String s3 = new String("Hip Hop")
        

        对于new关键字,在堆内存中创建了一个String对象,或者不相等的字符串对象已经存在于pool 和s3 将引用新创建的

        System.out.println(s3==s2) // two reference is different, will print false 
        

        使用 new 运算符创建的字符串对象不引用 字符串池 中的对象,但可以使用 String 的 intern() 方法。 java.lang.String.intern() 返回一个interned String,即在全局字符串字面量池中有一个条目。如果字符串不在全局字符串文字池中,那么它将被添加到池中。

        String s4 = s3.intern();
        Systen.out.println(s4 == s2); // will print `true` because s4 is interned, 
                       //it now have the same reference to "Hip Hop" as s2 or s1
        

        但是试试:

        Systen.out.println(s4 == s3) // it will be false,
        

        作为s4 的引用,s2s1 是对池化实例的引用,而s3 是在堆内存中引用创建的对象强>。

        使用 new 创建字符串:

        在 OpenJDK 7 更新 6 之前,Java String.susbtring 方法存在潜在的内存泄漏。 substring 方法将构建一个新的 String 对象,保留对整个 char 数组的引用,以避免复制它。因此,您可能会在不经意间保留对只有一个字符串的非常大的字符数组的引用。如果我们想在substring 之后有最少的字符串,我们使用构造函数来获取另一个字符串:

        String s2 = new String(s1.substring(0,1));
        

        这个问题在 JDK 7 更新 6 中得到了解决。因此不再需要使用 new 创建字符串来利用 字符串文字池 机制提供的优势。

        参考:

        1. String literal pool
        2. using new to prevent memory leak for using substring

        【讨论】:

          猜你喜欢
          • 2012-11-04
          • 2021-02-09
          • 2015-04-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-27
          相关资源
          最近更新 更多