我是Java的新手(多年来一直在写其他东西),除非我遗漏了一些东西(我很高兴在这里错了)以下是一个致命的缺陷......
String foo = new String(); thisDoesntWork(foo); System.out.println(foo);//this prints nothing public static void thisDoesntWork(String foo){ foo = "howdy"; }
现在,我很清楚(相当差的措辞)概念,在java中,一切都是通过"值"而不是"引用"传递的,但String是一个对象,有各种各样的花里胡哨,所以,人们会期待与int不同,用户可以对传递给方法的东西进行操作(并且不会被overloaded =设置的值所困).
有人可以向我解释这个设计选择背后的原因是什么?正如我所说,我不是想在这里,也许我错过了一些明显的东西?
这个咆哮解释得比我甚至尝试过:
在Java中,原语按值传递.但是,对象不通过引用传递.正确的语句是对象引用按值传递.
当你传递"foo"时,你将对"foo" 的引用作为值传递给ThisDoesntWork().这意味着当您在方法内部执行"foo"赋值时,您只需将局部变量(foo)的引用设置为对新字符串的引用.
在考虑字符串在Java中的行为方式时要记住的另一件事是字符串是不可变的.它在C#中的工作方式相同,并且有一些很好的理由:
安全性:如果没有人可以修改数据,没有人可以将数据插入到您的字符串中并导致缓冲区溢出错误!
速度:如果您可以确定您的字符串是不可变的,那么您知道它的大小始终是相同的,并且您在操作它时不必在内存中移动数据结构.您(语言设计者)也不必担心将String实现为慢速链接列表.但这会削减两种方式.仅使用+运算符追加字符串可能是内存昂贵的,并且您必须使用StringBuilder对象以高性能,内存有效的方式执行此操作.
现在谈谈你更大的问题.为什么对象以这种方式传递?好吧,如果Java传递你的字符串就像你传统上所说的"按值",它必须实际复制整个字符串,然后再传递给你的函数.那很慢.如果它通过引用传递字符串并让你改变它(就像C一样),你就会遇到我刚刚列出的问题.
由于我的原始答案是"为什么会发生",而不是"为什么设计的语言如此发生",我会再给它一个.
为简化起见,我将摆脱方法调用并以另一种方式显示正在发生的事情.
String a = "hello"; String b = a; String b = "howdy" System.out.print(a) //prints hello
为了得到最后的语句来输出"hello",b必须指向内存中的同一个"洞"是一个点(指针).当你想通过引用传递时,这就是你想要的.Java决定不去这个方向有几个原因:
指针令人困惑 Java的设计者试图删除一些关于其他语言的更令人困惑的事情.指针是C/C++最容易被误解和使用不当的结构之一,还有运算符重载.
指针是安全风险指针在误用时会导致许多安全问题.恶意程序会将内容分配给内存的那一部分,然后您认为您的对象实际上是其他人的内容.(Java已经摆脱了最大的安全问题,缓冲区溢出,带有已检查的数组)
抽象泄漏当你开始处理"内存中的内容和位置"时,你的抽象变得不那么抽象了.虽然抽象泄漏几乎肯定会侵入一种语言,但设计师并不想直接烘焙它.
对象都是你关心的 在Java中,一切都是对象,而不是对象所占据的空间.添加指针会使空间占据重要的对象,尽管.......
您可以通过创建"Hole"对象来模拟您想要的内容.您甚至可以使用泛型使其类型安全.例如:
public class Hole{ private T objectInHole; public void putInHole(T object) { this.objectInHole = object; } public T getOutOfHole() { return objectInHole; } public String toString() { return objectInHole.toString(); } .....equals, hashCode, etc. } Hole foo = new Hole foo){ foo.putInHole("howdy"); }