首页 文章

在Java中通过引用传递字符串?

提问于
浏览
134

我习惯在 C 做以下事情:

void main() {
    String zText = "";
    fillString(zText);
    printf(zText);
}

void fillString(String zText) {
    zText += "foo";
}

输出是:

foo

但是,在Java中,这似乎不起作用 . 我假设因为 String 对象被复制而不是被引用传递 . 我认为字符串是对象,它总是通过引用传递 .

这里发生了什么?

14 回答

  • 17

    到目前为止,Aaron Digulla有最好的答案 . 他的第二个选项的变体是使用commons lang库版本3的包装器或容器类MutableObject:

    void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }
    

    您保存容器类的声明 . 缺点是依赖于公共资源 . 但是lib有很多有用的功能,几乎任何我工作过的大项目都使用它 .

  • 0

    对于在java中通过引用传递对象(包括String),您可以将其作为周围适配器的成员传递 . 具有通用的解决方案在这里:

    import java.io.Serializable;
    
    public class ByRef<T extends Object> implements Serializable
    {
        private static final long serialVersionUID = 6310102145974374589L;
    
        T v;
    
        public ByRef(T v)
        {
            this.v = v;
        }
    
        public ByRef()
        {
            v = null;
        }
    
        public void set(T nv)
        {
            v = nv;
        }
    
        public T get()
        {
            return v;
        }
    
    // ------------------------------------------------------------------
    
        static void fillString(ByRef<String> zText)
        {
            zText.set(zText.get() + "foo");
        }
    
        public static void main(String args[])
        {
            final ByRef<String> zText = new ByRef<String>(new String(""));
            fillString(zText);
            System.out.println(zText.get());
        }
    }
    
  • 45

    你有三个选择:

    • 使用StringBuilder:
    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
    
    • 创建容器类并将容器的实例传递给您的方法:
    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
    
    • 创建一个数组:
    new String[] zText = new String[1];
    zText[0] = "";
    
    void fillString(String[] zText) { zText[0] += "foo"; }
    

    从性能的角度来看,StringBuilder通常是最佳选择 .

  • 1

    在Java中没有任何内容通过引用传递 . 一切都是 passed by value . 对象引用按值传递 . 另外字符串是 immutable . 因此,当您附加到传递的String时,您只需获得一个新的String . 您可以使用返回值,或者传递StringBuffer .

  • 2

    发生的事情是引用按值传递,即传递引用的副本 . java中没有任何内容通过引用传递,并且由于字符串是不可变的,因此该赋值创建了一个新的字符串对象,该引用的副本现在指向该对象 . 原始引用仍指向空字符串 .

    这对任何对象都是相同的,即在方法中将其设置为新值 . 下面的例子只是让事情变得更加明显,但连接一个字符串实际上是一回事 .

    void foo( object o )
    {
        o = new Object( );  // original reference still points to old value on the heap
    }
    
  • 1

    java.lang.String是不可变的 .

    我讨厌粘贴URL,但如果你在java-land中,https://docs.oracle.com/javase/10/docs/api/java/lang/String.html对于你阅读和理解是必不可少的 .

  • 5

    对象通过引用传递,基元通过值传递 .

    String不是原始的,它是一个对象,它是object的特例 .

    这是为了节省内存 . 在JVM中,有一个字符串池 . 对于创建的每个字符串,JVM将尝试查看字符串池中是否存在相同的字符串,如果已存在,则指向它 .

    public class TestString
    {
        private static String a = "hello world";
        private static String b = "hello world";
        private static String c = "hello " + "world";
        private static String d = new String("hello world");
    
        private static Object o1 = new Object();
        private static Object o2 = new Object();
    
        public static void main(String[] args)
        {
            System.out.println("a==b:"+(a == b));
            System.out.println("a==c:"+(a == c));
            System.out.println("a==d:"+(a == d));
            System.out.println("a.equals(d):"+(a.equals(d)));
            System.out.println("o1==o2:"+(o1 == o2));
    
            passString(a);
            passString(d);
        }
    
        public static void passString(String s)
        {
            System.out.println("passString:"+(a == s));
        }
    }
    

    / * OUTPUT * /

    a==b:true
    a==c:true
    a==d:false
    a.equals(d):true
    o1==o2:false
    passString:true
    passString:false
    

    ==正在检查内存地址(引用),.equals正在检查内容(值)

  • 0

    String是Java中的不可变对象 . 您可以使用StringBuilder类来完成您要完成的工作,如下所示:

    public static void main(String[] args)
    {
        StringBuilder sb = new StringBuilder("hello, world!");
        System.out.println(sb);
        foo(sb);
        System.out.println(sb);
    
    }
    
    public static void foo(StringBuilder str)
    {
        str.delete(0, str.length());
        str.append("String has been modified");
    }
    

    另一种选择是使用String作为范围变量创建一个类(非常不鼓励),如下所示:

    class MyString
    {
        public String value;
    }
    
    public static void main(String[] args)
    {
        MyString ms = new MyString();
        ms.value = "Hello, World!";
    
    }
    
    public static void foo(MyString str)
    {
        str.value = "String has been modified";
    }
    
  • 5

    Java中的所有参数都是按值传递的 . 将 String 传递给函数时,传递的值是对String对象的引用,但您无法修改该引用,并且基础String对象是不可变的 .

    分配

    zText += foo;
    

    相当于:

    zText = new String(zText + "foo");
    

    也就是说,它(本地)将参数 zText 重新指定为新引用,该引用指向新的内存位置,其中包含 zText 的原始内容,其中附加了 "foo" .

    原始对象未被修改, main() 方法的局部变量 zText 仍指向原始(空)字符串 .

    class StringFiller {
      static void fillString(String zText) {
        zText += "foo";
        System.out.println("Local value: " + zText);
      }
    
      public static void main(String[] args) {
        String zText = "";
        System.out.println("Original value: " + zText);
        fillString(zText);
        System.out.println("Final value: " + zText);
      }
    }
    

    打印:

    Original value:
    Local value: foo
    Final value:
    

    如果要修改字符串,可以使用 StringBuilder 或者使用一些容器(数组或 AtomicReference 或自定义容器类)来为您提供额外级别的指针间接 . 或者,只需返回新值并指定它:

    class StringFiller2 {
      static String fillString(String zText) {
        zText += "foo";
        System.out.println("Local value: " + zText);
        return zText;
      }
    
      public static void main(String[] args) {
        String zText = "";
        System.out.println("Original value: " + zText);
        zText = fillString(zText);
        System.out.println("Final value: " + zText);
      }
    }
    

    打印:

    Original value:
    Local value: foo
    Final value: foo
    

    这可能是一般情况下最类似Java的解决方案 - 请参阅Effective Java item "Favor immutability."

    如上所述, StringBuilder 通常会为您提供更好的性能 - 如果您有很多要做的事情,特别是在循环内,请使用 StringBuilder .

    但是如果可以的话,尝试传递不可变的 Strings 而不是可变的 StringBuilders - 您的代码将更容易阅读和更易于维护 . 考虑使用参数 final ,并配置IDE以在将方法参数重新分配给新值时发出警告 .

  • 2

    String是Java中的一个特殊类 . 它是线程保存,意思是“一旦创建了一个String实例,String实例的内容将永远不会改变” .

    这是正在发生的事情

    zText += "foo";
    

    首先,Java编译器将获取zText String实例的值,然后创建一个新的String实例,其值为zText,附加“foo” . 所以你知道为什么zText指向的实例没有改变 . 这完全是一个新的例子 . 实际上,甚至String“foo”也是一个新的String实例 . 因此,对于这个语句,Java将创建两个String实例,一个是“foo”,另一个是zText的值附加“foo” . 规则很简单:String实例的值永远不会改变 .

    对于方法fillString,您可以使用StringBuffer作为参数,或者您可以像这样更改它:

    String fillString(String zText) {
        return zText += "foo";
    }
    
  • 174

    答案很简单 . 在java中,字符串是不可变的 . 因此它就像使用'final'修饰符(或C / C中的'const') . 所以,一旦分配,你就不能像你那样改变它 .

    您可以更改字符串指向的值,但不能更改此字符串所在的实际值目前指点 .

    IE浏览器 . String s1 = "hey" . 您可以使用 s1 = "woah" ,并且's totally ok, but you can'实际上将字符串的基础值(在这种情况下:"hey")更改为使用plusEquals等分配的其他内容(即 s1 += " whatup != "hey whatup" ) .

    为此,使用StringBuilder或StringBuffer类或其他可变容器,然后只需调用.toString()将对象转换回字符串 .

    注意:字符串通常用作哈希键,因此这是它们不可变的部分原因 .

  • 1

    Java中的字符串是immutable .

  • 74

    这适用于StringBuffer

    public class test {
     public static void main(String[] args) {
     StringBuffer zText = new StringBuffer("");
        fillString(zText);
        System.out.println(zText.toString());
     }
      static void fillString(StringBuffer zText) {
        zText .append("foo");
    }
    }
    

    甚至更好地使用StringBuilder

    public class test {
     public static void main(String[] args) {
     StringBuilder zText = new StringBuilder("");
        fillString(zText);
        System.out.println(zText.toString());
     }
      static void fillString(StringBuilder zText) {
        zText .append("foo");
    }
    }
    
  • 7

    字符串在java中是不可变的 . 您无法修改/更改现有的字符串文字/对象 .

    String s =“Hello”; s = s“hi”;

    这里先前的引用s被指向值“HelloHi”的新引用替换 .

    但是,为了带来可变性,我们有StringBuilder和StringBuffer .

    StringBuilder s = new StringBuilder(); s.append( “你好”);

    这会将新值“Hi”附加到相同的引用 . //

相关问题