引用和变量传递。解释这个简单的java程序的输出?

时间:2010-03-07 01:16:20

标签: java

我需要帮助的快速摘要:

  • 什么是不可改变的类?
  • 使用Java的String类,为什么不能像在其他类中一样将一个String变量“指向”另一个(使用C术语)?例如,String x=(String)y;将y的值复制到x。为什么?
    • 例如,使用我自己的类('Test'),Test x=(Test)y;在y处正确地指向x。
  • String的行为是否与其不变性有关?

我有这个Java程序......有人可以解释引用的内容吗?

public class Main {

    public static void foo(String a){
        a="2";
    }

    public static void main(String[] args) {
        String x="1";
        foo(x);
        System.out.println("x="+x);

    }

}

输出:

x=1

预期产出:

x=2

发生了什么事?我认为无论我对afoo所做的事情也会影响x,因为a只是x的别名 - 不是吗?

这里有一个类似的问题:

public class Main {
    public static void main(String[] args) {
        String x=new String("1");
        String y=new String("2");
        y=x;
        x="3";
        System.out.println("x="+x);
        System.out.println("y="+y);
    }

}

输出:

x=3
y=1

预期产出:

x=3
y=3

有人可以解释一下吗?


编辑:

为什么这些例子会产生预期的结果,而上面的例子中没有String?

1:

public class Main {

    private static class Test{
        public String val;
        public Test(String set){val=set;}
        public Test(){}
    }
    public static void foo(Test a){
        a.val="2";
    }
    public static void main(String[] args) {
        Test x=new Test("1");
        foo(x);
        System.out.println("x="+x.val);
    }

}

输出(和预期输出):

x=2

2:

public class Main {

    private static class Test{
        public String val;
        public Test(String set){val=set;}
        public Test(){}
    }
    public static void main(String[] args) {
        Test x=new Test("1");
        Test y=new Test("2");
        x=y;
        x.val="3";
        System.out.println("x="+x.val);
        System.out.println("y="+y.val);
    }

}

输出(和预期输出):

x=3
y=3

5 个答案:

答案 0 :(得分:4)

Java始终是值传递的。

传递的东西是对象的引用,而不是对象本身。

在String的情况下,它是一个不可变的类。因此,您无法在foo()方法中更改String引用所指向的内容。

但是,你可以这样做:

package cruft;

public class Main
{
    private String value;

    public Main(String s)
    {
        this.value = s;
    }

    public String getValue()
    {
        return value;
    }

    public void setValue(String value)
    {
        this.value = value;
    }

    public static void foo(Main main)
    {
        main.setValue("2");
    }

    @Override
    public String toString()
    {
        return "Main{" +
            "value='" + value + '\'' +
            '}';
    }

    public static void main(String[] args)
    {
        Main main = new Main("1");
        System.out.println("before: " + main);
        foo(main);
        System.out.println("after: " + main);

    }

}

看到区别?我没有在foo()方法中更改对Main的引用;我只是改变了部分状态。

答案 1 :(得分:3)

在第一种情况下

a != xa是引用x的副本,请考虑指向公共String的指针的副本。因此,a = "2"说make a等于对“2”的引用, not 使得指向的值a等于“2”。 / p>

第二种情况x& y是您要更新的不同参考,以指向不同的值。

基本上,所有Java值(不包括基元)都是引用类型(C ++表示法中的some_class*),并且所有函数都作为pass-by-value调用。因此,调用一些引用类型作为参数的方法会复制这些参数。

答案 2 :(得分:1)

将对象视为在内存中漂浮的数据blob。您定义的变量(如String s)指向这些blob:它们不代表blob本身。

相反,int直接引用值。

答案 3 :(得分:1)

  

为什么我使用自己的类而不是使用String获得预期的结果?

简单说明,因为你正在比较苹果和橘子。您的String示例正在执行a = "2";,但您后面的示例正在执行a.val = "2";。第一个是尝试更新调用者中的引用...这不起作用。第二个是成功的,因为你告诉它(使用“。”运算符)来改变引用所表示的对象的状态。

如果您更改后面的示例以执行String示例尝试执行的操作,您会发现它们也不起作用。

第二点是您无法更改String的状态,因为String API旨在使其无法实现。相比之下,您的课程旨在允许更改其状态。但这并不能解释您所看到的行为,因为您的String示例甚至不是尝试来更改String的状态。

答案 4 :(得分:1)

首先:我知道这是虚伪的,但我认为把这个想法牢牢掌握在脑海中的最佳位置是获取Java教科书(或者可能是在线教程)。我不认为你能通过我们的答案对这些概念有充分的了解。

也许尝试(我没有读过这些,只是略读):


我将尝试以不同的方式回答这个问题。正确的答案已经存在,但是如果你不与这个人面对面,这种事情很难解释IMO。

这是你的第一段代码:

public class Main {

    public static void foo(String a){
        a="2";
    }

    public static void main(String[] args) {
        String x="1";
        foo(x);
        System.out.println("x="+x);

    }

}

问题是行a = "2"。简单来说,它意味着:“使变量'a'指向一个值为'2'的String实例”。这意味着:“忘记当前指向的'a'字符串,以便它可以指向这个新值”。因此,您要告诉它忘记它指向您传递的String(作为方法的参数),因此它可以指向新的。

作为参数传递的String仍然存在,变量“x”仍然指向它。您没有更改变量'x'指向的String,只更改了变量'a'指向的String。

如上所述,这是因为Java使用pass-by-value而不是pass-by-reference。