public class test22 {
private String s1=new String("初始值"); //主要是观察s1和i的值变化
private int i=0; public void print()
{
modify(s1,i);

System.out.println("s1的值:"+s1);
System.out.println("i的值:"+i);

} private void modify(String s,int i)
{
System.out.println("*******************");
if(s==s1)
System.out.println("传递的是引用");
else
if(s.equals(s1))
System.out.println("传递的是值");
System.out.println("*******************");
i=2;
s=new String("改变值");
}
}
输出结果:
*******************
传递的是引用
*******************
s1的值:初始值
i的值:0i值没有发生变化,是因为内部是值传递。
困惑:s1的值为什么没有改变?传的明明是引用啊,我尝试其他引用(非字符串引用)值是会发生变化的,难道字符串有什么特殊吗?
请各位不吝赐教!!~~~~谢谢~~~~~

解决方案 »

  1.   

    哈哈……
    这个问题很基础,但是很蒙人。注意你的最后一句:s=new String("改变值");
    你在这里把s的引用目标改变了。传递引用会改变内容的原因是,通过传递的引用改变了引用目标的内容。你这里把引用目标改变了,外面的当然不会跟着变了。图解如下
    调用函数前:
    s1 -> "初始值"
    函数初始:
    s1 -> "初始值" <- s    两个都引用一个
    执行最后一句:s=new String("改变值");之后:
    s1 -> "初始值" 
    s -> "改变值"
    函数结束时,s因为失去作用域而消失。只剩下:
    s1 -> "初始值"
    根调用函数前没啥区别。String, Integer, Boolean...这些类都是没有改变内容方法的,所以永远不要妄想通过传递引用来改变它们的内容。
      

  2.   

    api 里 是 这 样 描 述 的:字符串是常量;它们的值在创建之后不能改变。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享它们。例如:      String str = "abc";
    应该怎么理解 “是常量” 三个字,是个问题。
      

  3.   

    另外,初始化String变量和给String变量赋常数值时,不要用new的方法,而要直接用常数String赋值。例如:
    String s1 = "初始值";
    你的new的方法会浪费系统资源的。具体原因如下:
    第一种写法: p = "csdn"; 
    第二种写法: p = new String("csdn"); // <- 枪毙枪毙 x_x原因:
    第二种写法严重浪费系统资源。
    第一种写法会在内存中保存一个String类型的常量。无法被垃圾回收机制回收。
    第二种写法会在heap中动态开辟一块内存保存一个String类型的对象,可以被垃圾回收。但是在产生这个动态内存的同时,因为使用常量来定义(注意new String之后括号里面的东西),所以,与此同时同样会在内存中保存一个永不被回收的Strign类型常量。造成内存的重复浪费,同时new一个对象时的那不小的CPU开销和随后垃圾回收所用的资源也是白白浪费。综上,忘掉p = new String("csdn");这种垃圾写法吧。永远不要让他出现在我们的代码中。除非你是要写恶意占用资源的软件。http://community.csdn.net/Expert/topic/4854/4854358.xml?temp=.8900263
      

  4.   

    不要太偏激,有的时候new还是有用的
    如果我要把两个相同的字符串放入到IdentityHashMap中就需要使用new了
      

  5.   

    ****************************************************************************
    ****************************************************************************
    *******************************************************************************
    感谢各位给予快速解答!!
    我上面的代码也是为了说明问题,所以还没有考虑资源问题,呵呵,受教了,谢谢~~~我的理解是:
    我是用指针来理解引用的,
    if(s==s1)
    System.out.println("传递的是引用");
    这个条件为真,说明s1和s2是同一个指针,所以这时候,对他们中的任一个进行操作,就相当于对另一个操作。而如果仅仅是s.equals(s1)条件为真,则说明是两个指针,只不过是指向内容相同。
    当然字符串还有特殊之处,但这里我感觉没有用到。我现在不明白的是明明是同一个引用,对其中一个修改,另一个为什么不发生改变?????
    谢谢各位!!!!
      

  6.   

    虚心的问:楼上说的那种情况在什么时候有实际应用价值呢?为什么不用两个不同的常量字符串?如果为了在某些情况下相同,可以保持前面部分相同,然后用startsWith()判断吧。
      

  7.   

    lz看过我给你的图解了么?
    因为即便是所谓的相同的引用,其实也是两份引用。
    举个例子吧:
    有一张纸,这张纸就是一个对象。初始化的时候,上面写着"初始值"。
    你,一个引用,一手抓着这张纸,一手拿着笔准备改。
    调用函数,你把引用传给了我。
    我也伸出一只手,抓着这张纸,一手拿着笔准备改。
    也就是,我们俩都抓着这张纸。现在,如果在函数里通过我这个引用调用了修改对象的方法,那么我就会拿起笔,在纸上写写画画。
    这时,通过你这个引用找到那张纸时,纸上也将是我改过的样子。但是,在你贴子里面的程序,没有调用这种方法,(事实上String根本不给我们写写画画的机会,)而是给我这个引用赋了新值。这时,我就放开了你抓着的这张纸,转去抓着另一张纸(纸上写着"改变值")。
    注意了,这个时候你手上抓着的还是原来那张纸,因为通过我这个引用做的赋值显然不可能让你也去抓别的纸张。
    现在函数结束了,我可以消失了。(可怜我年轻的一生……)
    程序通过你这个引用,看你手上抓的纸,自然还是原来那个写着"初始值"的纸了。这就是为什么没有改变的原因。
    还不明白,就找个身边的人,照着我写得做一遍。
      

  8.   

    s和s1中保存的都是 "初始值" 这个字符串的地址~~,所以s和s1的值肯定是一样的了.
    记住java中都是值传递的,这点和C及C++不同.
      

  9.   

    JAVA中都是传值,不存在传引用,简单类型传的是本身的值,对象传的是地址值,总之都是传值。
      

  10.   

    String还是比较特殊,对象不可变给人的迷惑很多。
      

  11.   

    我正看Java编程思想,不知道是不是: 别名问题 引起的.
      

  12.   

    非常同意类似Dan1980等人的说法。这样想不会头疼,而且很多看似复杂的问题也会迎刃而解。不愧是括号里面有一堆绿馒头的人。