本帖最后由 tuteng181 于 2009-09-28 13:32:39 编辑

解决方案 »

  1.   

     public static void test(StringBuffer str)
        {
            StringBuffer tempStr = new StringBuffer(); //构造一个tempStr的StringBuffer对象        System.out.println("first : " + str);//输出str,没有问题,肯定是hello
            str = str.append(" world");//在hello后面连接" world",并重新赋值给str,所以str现在是"hello world"
            System.out.println("second: " + str); //输出"hello world"
            str = tempStr; //将str指向tempStr,注意这里的str直接赋值和前面append的区别,append
    //能改变这个str对象,而str这个形式参数只是指向了tempStr空间,一旦方法结束,str还是原来的str,
    //也就是append之后的那个str "hello world" 
            System.out.println("third : " + str);
        }于String的对比代码,请看:
    public static void test(String str)
        {
    String tempStr = "";         System.out.println("first : " + str);
            str = str +" world";
            System.out.println("second: " + str);
            str = tempStr;
            System.out.println("third : " + str);
        }
     public static void main(String args[]) {
            String str1 = "hello";
            test(str1);
            System.out.println("main  : " + str1);    }
    上面的代码输出和你的代码不一样  因为String是不可变的(immutable),而StringBuffer是可变的(mutable)。再来看下面这段代码:
    public static void test(StringBuffer str)
        {
            StringBuffer tempStr = new StringBuffer();         System.out.println("first : " + str);
            str.append(" world"); //把str = str.append(" world")改成这样  对结果没有任何影响 
    //更说明了append方法对于StringBuffer对象的改变 (具体参见源码),而不在于你赋值与否;也说明了
    //str只是个形式参数
            System.out.println("second: " + str);
            str = tempStr;
            System.out.println("third : " + str);
        }
      

  2.   

    test里的 tempStr 是个空对象!打印出来当然是空了!!!静态方法 和静态变量的 一些知识 去网上看看  
    http://zhidao.baidu.com/question/5912766.html
    看看这个对你有帮助
      

  3.   

    test算法中的tempStr 只是声明了,没有赋值,所以str=tempStr;时str的值是空的, str = str.append(" world");这句话把world的值赋给了str,所以在main中的输出会有world
      

  4.   


    public static void main(String[] args)
        {
            StringBuffer  str1 = new StringBuffer("hello");
            test(str1);
            System.out.println("main  : " + str1);
        }
        public static void test(StringBuffer str)
        {
            StringBuffer tempStr = new StringBuffer();         System.out.println("first : " + str);
            str = str.append(" world");
            System.out.println("second: " + str);
            str = tempStr;
            System.out.println("third : " + str);
        }请记住Java中函数永远是值传递的。
    StringBuffer  str1 = new StringBuffer("hello");
    在堆内存声明了一个StringBuffer实例,传入参数“hello”。test()方法调用时,str1在栈内存中的对象引用被赋值给局部变量str,此时str和str1指向堆内存中的同一个StringBuffer对象。
    接着输出str(同str1),结果是:hello;
    append()将world追加到str(str1)变量所指向的StringBuffer中。注意由于str1和str指向同一个Stringbuffer对象,所以str1的值被改变,结果是“hello world”。
    System.out.println("second: " + str)的打印结果当然是:hello world。
    str = tempStr;这句将使str变量指向tempStr在堆内存中申请的内容为空字符串的StringBuffer对象,其实就是一个tempstr的别名。此时System.out.println("third : " + str);当然是空了。
    退出test()方法后,由于str1的内容已经改变为:"hello world"
    所以main方法中的System.out.println("third : " + str);打印输出为:hello world。
      

  5.   

    请问高人:
    Java中函数永远是值传递的。如何理解? 传简单类型(int,short,Integer)的时候,就是值的拷贝!
    传引用类型(StringBuffer.....)的时候,传的就是引用的拷贝,这里说的值就是指"对象的引用"吗?
    而不是对象本身???????????????????????????
      

  6.   

    是这样的,str 是参数 他开始指向的是 str1所指向的地址 ,所以  first : hello (第一个 sysout)
              str.append 就是  str1.append 所以  second: hello world (第二个 sysout)
              str = tempStr  就是把 str指向tempStr所指向的地址。而是str1所指向的地址没有变(不要把参数str当做str1)  third : (第三个 sysout)
    (第四个 sysout)因为str1 所指向的地址一直没有变,他只append了world 所以 main  : hello world 
      

  7.   

    是的,像这样(StringBuffer.....)给函数传引用类型的变量时,传递的是这个引用的拷贝。话句话说,其实就是在栈内存中又申请了一个指向引用类型变量的引用,然后让它指向引用参数所指向的堆内存地址。这样,他们其实引用的是堆内存中的同一个对象。那么对函数中局部变量的改变也会影响到原来的变量。
       对象和数组就属于引用类型。
      

  8.   

    其实java里根本没有引用传递的说法
    c++里才有
      

  9.   

    大哥,String在java中是被特殊处理的,对它的所有申请都是在栈内存中,String不属于引用类型。请不要混淆
      

  10.   

    String怎么就被特殊处理了?String不是对象类型么?
    是你把它特殊化了吧?你去看看这篇blog:对于String的认识
      

  11.   

    函数传参,无论是java还是C++传递的都是值。java的对象全都是引用(相当于C指向对象的指针), 这点没有疑问,所以传递的是对象指针(java中当作是引用或许更通俗一点)。下面是分析。
      

  12.   

    对于字符串对象来说,虽然在参数传递的时候也是引用传递,但是java虚拟机在函数内部对字符串对象进行了特殊处理--视String对象为常量(final)   所以对传进来的引用地址所引用的string对象比能直接进行修改,而是产生一个副本对其进行操作,对其进行的操作不会影响原来的值。 下面我们看一例子(sun认证试题)来证明这一点: 1.   public   class   X   { 
    2.   public   static   void   main   (String[]args)   { 
    3.   string   s   =   new   string   (“Hello”); 
    4.   modify(s); 
    5.   System.out.printIn(s); 
    6.   } 
    7. 
    8.   public   static   void   modify   (String   s)   { 
    9.   s   +=   “world!”; 
    10.   } 
    11.   } 你说它会打印出什么结果?是Helloworld?可能大部分人会说是这个答案吧,哈哈结果却是Hello,为什么呢?再仔细分析一下上边所说的吧-----“视String对象为常量(final)”   这是问题的关键!在modify()方法中传进了string对象,虽然是引用传递,上边我们说了视String对象为常量(final),它不会修改原来地址所引用的值,而是生成一个副本对其进行操作,所以打印结果应该为Hello!下面再讲一讲java中对常量(final)是怎样处理优化的,以便更深入的理解上面的问题: java中的final变量,java编译器是进行了优化的。每个使用了final修饰的变量的地方都不会通过连接而进行访问。比如说Test类中使用了Data类中一个final的int数字fNumber=77,这时候,java编译器会将77这个常数编译到Test类的指令码或者常量池中。这样,每次Test类用到fNumber的时候,不会通过引用连接到Data类中进行读取,而是直接使 我们再来举个例子说明这一点: public   class   TestFinal(){ public   static   void   main(string   s[]){ System.out.println(FinalData.date); } public   class   FinalData(){ public   static   final   data=8; } } 运行上面的TestFinal类结果为:8 但是你把FinalData类中的data=88,编译后再运行TestFinal类结果还是8,这是因为编译器把data的副本保存TestFinal类中,所以在重新编译TestFinal类的前,TestFinal类一直把data认为是8而不是88,是不是证明了上面所讲述的每个使用了final修饰的变量的地方都不会通过连接而进行访问!结合这个例子,再深入的想一想第一个问题是不是就理解的更加清晰了 
      

  13.   

    顶7楼,并且严重的告诉楼主:Java只有值传递,没有引用传递!对于对象引用,传递的是引用的拷贝!在方法中你可以改变引用拷贝所指的对象内容,但是改变引用拷贝的引用不会影响外部的引用!
      

  14.   

    我并不是说你的解释不对,而是,就引用类型的参数传递而言,String并不适用于我说的原则。
      

  15.   

    我想问你 下面的这些话 是你自己写的 还是你复制的?你怎么还给我来一句:可能大部分人会说是这个答案吧,哈哈结果却是Hello,为什么呢?呵呵~~
    你去看下我的blog和里面链接的几篇文章 然后看看别人的回复 就知道了 我在二楼也说了  String是immutable的 然后我2#里的几个代码的逻辑关系不知道你看明白没
      

  16.   

    举一个例子,有人从邮局给你送了一个包裹(对象new StringBuffer("hello")), 然后通过一个凭据(StringBuffer  str1)写明包裹所在的邮局地址(包裹的引用地址),同时将这个凭据复印了一下(StringBuffer str),  , 让你操作一下(test函数),撕开包裹,取出邮件。重点讲一下test函数,即你拿到凭据复印件str之后,怎么操作的,你通过str到地址邮局中找到了包裹, 打开它,在里面塞了一些废纸,
    然后,你自己又弄了一个新包裹, 在里面塞了一只烟灰缸。问题: 原来的包裹有什么东东? 不知道你是否明白了这个问题的答案? 请结合我上面的答复再看看,
    表述不好,见谅!
      

  17.   

    这不是我总结的,来自于别人的Blog,但是我觉得对于String类型的参数传递,就是如此。
      

  18.   

    要说多少遍呢,java中将对象传入某个方法时,在内部实现时只是拷贝了该对象的指针,而不是整个对象。也就是说java值传递对于对象来说,对象的指针值不会改变,而对象的属性值是可以改变的(只要该对象属性值允许改变)
      

  19.   

    算了 不争了 估计争到这里 楼主也差不多明白了不过话说回来 你复制的那个解释是正确的 但是你前后表述的有矛盾的地方  比如你说对象和数组时引用类型(这话没错) 但你又说String不属于引用类型。这么说是觉得不正确的。
      

  20.   

    在java中基本类型是按值传递的,但对象类型是按引用传递的
    public static void main(String[] args)
        {
            StringBuffer  str1 = new StringBuffer("hello");
            test(str1);
            System.out.println("main  : " + str1);
        }
        public static void test(StringBuffer str)
        {
            StringBuffer tempStr = new StringBuffer();         System.out.println("first : " + str);//此时str是main方法中的str1的引用
            str = str.append(" world");
            System.out.println("second: " + str);
            str = tempStr;                      //此时str的引用改变了,改为对tempStr的引用,但main方法中的str1的内容没有改变哦。
            System.out.println("third : " + str);
        }
      

  21.   

    Java里都是按值传递  没有按引用传递
      

  22.   

    这里我是想说对于不可变的String进行参数传递时,不是按照:传递引用的拷贝这个原则进行的。没说清楚还望见谅。最终的目的是传播正确的知识,只要不误传知识就好。
      

  23.   


    C++/C 也一样, 都是值传递,就是被一群傻13 搞得那么复杂的规则!Thinking in java, 里面已经明确的指出, 不要区分传啥, 最终是传值的
      

  24.   

    2楼:
     str = tempStr; //将str指向tempStr,注意这里的str直接赋值和前面append的区别,append
    //能改变这个str对象,而str这个形式参数只是指向了tempStr空间,一旦方法结束,str还是原来的str,
    //也就是append之后的那个str "hello world" 这个怎么可能呢??
    str之前指向的是传入的参数str1所指向的内存空间,所以之前的操作会影响外部的变量str1的值的
    而经过这条语句后,str已经指向了新new出来的内存空间,也就是tempStr所指向的内存空间
    执行完了,str也不会再重新指向原有的内存空间呀,
    System.out.println("third : " + str);
    这条执行后,就会等待时机进行垃圾回收了呀~~~~
    之所以最后输出main  : hello world 只是由于之前的影响造成的呀
      

  25.   

    应该这么说吧,java对于传参来说,只有拷贝,没有传递???对不?
      

  26.   

    wang12同学,java没有对String作特殊处理.
      

  27.   


    public static void test(String str)
        {
            String tempStr = "";         System.out.println("first : " + str);
            str = str +" world";//这里产生了新对象
            System.out.println("second: " + str);
            str = tempStr;
            System.out.println("third : " + str);
        }
     public static void main(String args[]) {
            String str1 = "hello";
            test(str1);
            System.out.println("main  : " + str1);    }
    其实10楼说的也没错 
    public static void test(String str) 这里的str是一个新的引用 即形参。它和str1 同时指向了"hello".
    只不过String是个特别的,确如你所说它是不可变的。
    所以
    str = str +" world"; 又生成了一个新的String "hello world",然后str重新指向hello world"。而这个时候str1仍然是指向"helle"的。故最后在main中输出时 :
    first : hello
    second: hello world
    third : 
    main  : hello。另外谈一下我自己对java值传递的理解。
    如:public static void test(String str)
    这里传过来的str 明明就是传的"hello"这个字符串的引用,为什么说是值传递呢??因为这里的值是指引用的值!我们知道引用其实也是一个对象,而这个对象的内容包含了它所引用对象的地址罢了。所谓值传递应该理解为值的复制。所以这里说的值传递是指引用的值传递。
    归纳下来 ,对于基本类型 int long 等:值传递传的就是这个对象本身。即传递时将这个基本类型拷贝一份,传的是副本,而本体不会发生任何改变。public static void main(String[] args)
        {
            int i = 3;
            test(i);
            System.out.println("main:"+i);
        }
        public static void test(int i)
        {
        
            i=i+100;
            System.out.println("first : " + i);
            
        }
    结果
    first : 103
    main:3
    对于对象类型,String StringBuffer都属于对象类型。值传递时传的是对象引用的值,即将对像的引用拷贝一份,将副本传给方法。test方法中的str 与str1为两个完全不一样的东东。(即便我们有时候把两个参数写为一样也不影响,因为一个是局部变量)。他们同时指向了"hello"对象。对他们的操作实质都是通过地址找到"hello"对象进行操作。但是当:str = tempStr;这个操作并不能对str指向的对象进行操作。而是引用的赋值运算,即将tempStr的内容(引用对象的地址)赋给了str.
    我说明白了么?
      

  28.   

    确切的说是jvm对String进行了特殊处理,jvm中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。
      

  29.   

    wang12。String类型是引用类型。只是由于它的不可变所以像做s=S+"a" 这样的操作时新生成了对象。而new String("a");时才有可能是直接在栈池里面拿。
    我想说的观点s=s+"a";永远都是新生成了对象。个人猜测,欢迎拍砖
      

  30.   

    刚开会去了 我忘记说一句话了 那个str是拷贝 
      

  31.   

    new String("a");时才有可能是直接在栈池里面拿。我对String共享池的理解也仅限于此。
    至于s=s+"a";永远都是新生成了对象。如果能给于确切答案的话,还望不吝赐教。
      

  32.   

    我的那个blog里有啊 因为s是变量 和s = "ab" + "c"不一样  一个是编译时确定 一个是运行时确定String str1 = "abc";
    String str2 = "a" + "bc";
    String str3 = str2 + "";//你去判断str1,str2,str3是否 “==”
      

  33.   


    抱歉抱歉,我说错了。自己实践了下爆汗!
    看代码
    public static void main(String[] args)
        {
         String s1 = "test";
         String s2 = "test";
         String s3 = "te"+"st";
         String s4 = new String("test");
         System.out.println("s1=s2?:"+(s1==s2));
         System.out.println("s1=s3?:"+(s1==s3));
         System.out.println("s1=s4?:"+(s1==s4));
         
        }结果:
    s1=s2?:true
    s1=s3?:true
    s1=s4?:false结论:
    对象String s = "test";这样的应该是从String池里取得。
    对于String s = "te"+"st";这样的 ,如果池子里有肯定也是从池子里取。
    而对于 new String("test").则永远是新生成对象。
    我居然忘了老师说的 new 永远是 new! 哎 抱歉啊!
    至于对sting 池 我刚刚看来一些资料。因为之前也对这个有所疑惑。这个池子里到底会放哪些String对象呢。原来啊 ,String 池应该叫常量池,它里面不管有String对象,还有一些基本数据类型。常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
    用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
      

  34.   

    在看测试 public static void main(String[] args)
        {
         String s1 = "test";
         String s2 = "test";
         String s3 = "te";
         String s4 = "st";
         String s5 = s3+"st";
         String s6 = s3+s4;
         String s7 = "te"+"st";
         String s8 = new String("test");
         System.out.println("s1=s2?:"+(s1==s2));
         System.out.println("s1=s5?:"+(s1==s5));
         System.out.println("s1=s6?:"+(s1==s6));
         System.out.println("s1=s7?:"+(s1==s7));
         System.out.println("s1=s8?:"+(s1==s8));
         
        }
    结果
    s1=s2?:true
    s1=s5?:false
    s1=s6?:false
    s1=s7?:true
    s1=s8?:false
    有什么想法呢??
      

  35.   

    justinavril:
    我的那个blog里有啊 因为s是变量 和s = "ab" + "c"不一样  一个是编译时确定 一个是运行时确定 正解啊!佩服!
      

  36.   

    看了你提供的帖子,对String的认识又进了一步,受益匪浅,谢了。
      

  37.   

    大家不要把这个问题复杂化了,我来总结一下楼主的程序吧(其他楼层兄弟的代码没全看,可能已经有相同的解释了):public static void main(String[] args)
        {
            StringBuffer  str1 = new StringBuffer("hello");//声明一个引用str1,创建一个StringBuffer对象(叫对象A吧),里面放的是"hello"这个字符串.
            test(str1); //调用test方法,详细看下面的注释.
            System.out.println("main  : " + str1);//str1所指向的对象A在test方法中被追加了" world"字符串,所以打印结果为"main  : hello world".
        }
        public static void test(StringBuffer str)//在test方法参数栈(姑且这么叫吧)上声明一个引用str,指向上面存有"hello"字符串的StringBuffer对象(对象A).
        {
            StringBuffer tempStr = new StringBuffer(); //声明一个引用tempStr,创建一个StringBuffer对象(叫对象B吧),里面放的是空字符串.        System.out.println("first : " + str);//由于指向的的StringBuffer对象(对象A)存的是"hello",所以打印"firs : hello"
            str = str.append(" world");//调用append方法,对象A追加字符串" world",现在对象A的内容为"hello world".
            System.out.println("second: " + str);//打印对象A,结果"second: hello world".
            str = tempStr;//引用str指向引用tempStr所指向的对象B,此时str所指向的对象内容为空字符串.
            System.out.println("third : " + str);//因此打印结果"third : "
        }
      

  38.   

    是引用传递,1、2、3都好理解,4是因为tmpstr的作用域问题。
      

  39.   


    这个家伙很嚣张,丢我个板砖,我的这句话是从《JAVA大学教程》第四版看到的,其中基本类型有int,char,boolean,String ,对象类型有Integer,StringBuffer。
      

  40.   

    JAVA里面既有值传递,也有引用传递;
    对于基本数据类型,如int,float,double等为值传递,
    而针对对象,都是引用传递,
      

  41.   

    public static void main(String[] args)
        {
            StringBuffer  str1 = new StringBuffer("hello");
            test(str1);
            System.out.println("main  : " + str1);
        }
        public static void test(StringBuffer str)
        {
            StringBuffer tempStr = new StringBuffer();         System.out.println("first : " + str);
            str = str.append(" world");
            System.out.println("second: " + str);
            str = tempStr;   A        
            System.out.println("third : " + str);
        }
         (A处)通过 str = tempStr (把 tempStr 的引用传给了 str,导致了 str 
          
          指向了 对象 tempStr ,因此 third 的值为:null(空)  
          
           Java 中的函数 传过的参数 为: 单项值传递 
           
           即 把 str1 传给 函数 test ,test 可以用 str1 进行操作
          
              而 test 中对 str1 的改变 ,不会传递到 main 函数中
      

  42.   

    单项值传递  yingWei  单向值传递
      

  43.   

    这个我懂了90%,剩下的是因为我还没看到什么StringBusher,这个很好理解啊,就跟C里的指针一样
      

  44.   

    public static void main(String[] args)
        {
            StringBuffer  str1 = new StringBuffer("hello");  //str1->"hello"
            test(str1);                                      
            System.out.println("main  : " + str1);
        }
        public static void test(StringBuffer str)            
        {
            /*注意这里产生了一个新的指针和
             *str1指向同一个字符串str->"hello"
             */
            StringBuffer tempStr = new StringBuffer();         //一个新的对象        System.out.println("first : " + str);
            str = str.append(" world"); //这里"hello"变成了"hello world"会影响到str和str1
            System.out.println("second: " + str);
            str = tempStr;     //这里把str指向了一个新对象,主意str1的指向并没有发生变化
            //到此为止str指向tempStr 而str1指向"hello world" 两个指针分别指向了不同对象
            System.out.println("third : " + str);
        }