Java 是一种“按值传递”语言,这意味着将变量发送到方法中,将变量指向新对象,不会影响外部变量。
public void one() {
String s = "one";
two(s);
System.out.println(s);
}
public void two( String s ) {
s = "two";
}
会写“一”。
有没有办法防止这种情况?或者在方法内部实际更改s
为“二”的最常见解决方案或模式是什么?
Java 是一种“按值传递”语言,这意味着将变量发送到方法中,将变量指向新对象,不会影响外部变量。
public void one() {
String s = "one";
two(s);
System.out.println(s);
}
public void two( String s ) {
s = "two";
}
会写“一”。
有没有办法防止这种情况?或者在方法内部实际更改s
为“二”的最常见解决方案或模式是什么?
不可能阻止它。
您可以使用这样的通用包装器来模拟它:
class _<T>{
public T _;
public _(T t ) {
_ = t;
}
public String toString(){ return _.toString(); }
}
然后按照您的意图使用它。
class GeneriWrapperDemo {
public static void main(String [] args ) {
_<String> one = new _<String>("One");
two( one );
System.out.println( one );
}
public static void two( _<String> s ) {
s._ = "two";
}
}
但是长得丑。我认为最好的办法是自行更改参考:
public String two( String a ) {
return "two";
}
并使用它
String one = "one";
one = two( one );
:)
创建一个包含字符串的对象,然后将其传递给方法。
public class StringHolder {
public String s;
public StringHolder(String s) {
this.s = s;
}
}
然后代码将如下所示:
public void two(StringHolder sh) {
sh.s = "two";
}
StringHolder sh = new StringHolder("one");
two(sh);
System.out.println(sh.s);
虽然,对于上面的例子,你可以只返回你想要的值:
public String two(String s) {
return "two";
}
String s = "one";
s = two(s);
System.out.println(s);
而对于String
s,你总是可以使用StringBuffer
,它是可变的:
public void two(StringBuffer buf) {
buf.setLength(0);
buf.append("two");
}
如果s
是一个可变对象,您可以更改其值(即其数据成员的值)。并且成员也可以是String
。这不适用于String
直接使用参数,因为它是不可变的,因此“更改”它的唯一方法是将引用指向不同的对象。
您不能通过引用传递 - 至少不是变量本身。所有参数都按值传递。但是,对象包含引用 - 并表示为引用本身。您可以随时更改对象的内部,并坚持更改。因此,发送一个数组,或者创建一个包装类,或者创建你自己的引用对象:
class Ref<T> {
T obj;
public Ref(T value) {
this.obj = value;
}
public void set(T value) {
obj = value;
}
public T get() {
return obj;
}
}
正如其他人所说,String
无论如何都不是可变的,所以你实际上并没有在这里改变字符串,而是让变量指向另一个方向,所以不简单地返回新字符串并没有多大意义。
你不能阻止Java 按值传递;这就是语言语义。
您可以通过一种或另一种方式绕过它,具体取决于您想要做什么。
您可以return
根据传递的参数创建新值:
static String scramble(String s) {
return s.replaceAll("(.*) (.*)", "$2, $1");
}
// then later...
String s = "james bond";
s = scramble(s);
System.out.println(s); // prints "bond, james"
你也可以传递一些可变的东西:
static void scramble(StringBuilder sb) {
int p = sb.indexOf(" ");
sb.append(", ").append(sb.substring(0, p)).delete(0, p+1);
}
// then later...
StringBuilder sb = new StringBuilder("james bond");
scramble(sb);
System.out.println(sb); // prints "bond, james"
字符串是不可变的......否则,您所要做的就是调用一个方法来操作同一个对象并以某种方式更改字符串值。
我确信有许多 Java 类可以为您完成这项工作,但您也可以通过创建一个带有公共字段或 setter/getter 的封装类来创建自己的类。前者的一个例子是这样的:
public class EncapsulatedString
{
public String str;
public EncapsulatedString(String s)
{
str = s;
}
}
1-length 数组有点难看,但完美的解决方案。无需创建多余的包装类:
public void one() {
String[] s = new String[]{"one"};
two(s);
System.out.println(s[0]);
}
public void two(String[] s) {
s[0] = "two";
}
此模式在翻译已广泛应用引用传递的旧(例如 C)代码时特别有用。
也就是说,返回一个新的、新鲜的值总是比改变“输入”值更容易混淆。
创建一个包含您的对象的包装器并更改包装器的内容:
public class StringWrapper {
private String str;
public String getString() {
return str;
}
public String setString(String str){
this.str = str;
}
public String toString() {
return str;
}
}
public void one() {
StringWrapper s = new StringWrapper();
s.setString("one");
two(w);
// This will print "two"
System.out.println(s);
}
public void two( StringWrapper s ) {
s.setString("two");
}
我想到的一个丑陋的解决方案是传递一个长度为 1 的字符串数组。这不是我会鼓励的,但如果你觉得你想这样做......
1 public class One {
2
3 public static void main( String[] _ ) {
4 String[] s = {"one"};
5 two(s);
6 System.out.println(s[0]);
7 }
8
9 public static void two( String[] s ) {
10 s[0] = "two";
11 }
12 }
使用可变的 StringBuffer 或 StringBuilder。
Java不是按值传递的语言。相反 - 它是一种传递参考语言。(通过引用传递并不意味着您可以将原始“指针”更改为指向其他地方,如 C++ 允许的那样)。唯一通过值传递的是原语(int
,long
等char
)。
对象引用总是通过引用传递——所以如果你的对象能够支持改变它的内容(例如通过getter和setter方法)——它可以被改变。
String
特别是不可变的——这意味着它的内容可能永远不会改变。因此,对于您的具体问题,如果您希望局部变量 's' 引用的字符串发生更改,您需要为其提供对String
对象新实例的引用。
例子:
public void one()
{
String s = "one";
s = two(); // Here your local variable will point to a new instance of a String with the value "two"
System.out.println(s);
}
public String two()
{
return "two";
}
如果您使用的对象不是String
- 您可以在它们上定义 setter 方法,这些方法将为您更新它们的内容。