黑马程序员技术交流社区

标题: 关于String,大牛们抽点时间看下 [打印本页]

作者: 阿磊    时间: 2014-8-5 00:06
标题: 关于String,大牛们抽点时间看下
本帖最后由 阿磊 于 2014-8-16 00:56 编辑

class  Test
{
        public static void main(String[] args)
        {
                String str1 = "abcde";
                String str2 = "abc";        
                swap(str1,str2);
                System.out.println("str1="+str1+"::"+"str2="+str2);//输出结果    str1=abcde::str2=abc
                if(str1.length()>str2.length()){
                        String temp = str1;
                        str1 = str2;
                        str2 =  temp;
                }        
                System.out.println("str1="+str1+"::"+"str2="+str2);输出结果  str1=abc::str2=abcde
        
        }
        public static void swap(String str1,String str2){
               
                if(str1.length()>str2.length()){
                        String temp = str1;
                        str1 = str2;
                        str2 =  temp;
                }
        }

}
为啥两次的输出结果不一样,求解答!!!!

作者: fxwb2005    时间: 2014-8-5 00:37
本帖最后由 fxwb2005 于 2014-8-5 00:39 编辑

swap方法里面交换的两个变量根本不是上面main方法里面的,你交换的只是两个参数的值而已,所以swap方法没有起到作用。
作者: 阿磊    时间: 2014-8-5 00:58
fxwb2005 发表于 2014-8-5 00:37
swap方法里面交换的两个变量根本不是上面main方法里面的,你交换的只是两个参数的值而已,所以swap方法没 ...

我调用了方法,传入了参数,怎么会不是操作的这个数据呢,那别的的方法都能用吗?你在看看这个图片能说明点问题。
作者: fantacyleo    时间: 2014-8-5 01:22
阿磊 发表于 2014-8-5 00:58
我调用了方法,传入了参数,怎么会不是操作的这个数据呢,那别的的方法都能用吗?你在看看这个图片能说明 ...

对,你操作的是数据,也就是str1和str2两个变量所指向的对象。但是,你没有也不可能操作str1和str2两个变量本身。因为Java方法的参数传递是值传递,你传入的是str1和str2的值,也就是str1和str2指向的对象的地址值,你只能操作对象,而不能操作指向对象的变量
作者: 阿磊    时间: 2014-8-5 01:45
fantacyleo 发表于 2014-8-5 01:22
对,你操作的是数据,也就是str1和str2两个变量所指向的对象。但是,你没有也不可能操作str1和str2两个变 ...

那为什么调用方法不能换位,而直接在主函数中写代码就可以换位呢?并且调用方法的时候str1和str2的值确实有传进去啊,都打印出了结果了。那入股说String是一个对象int型数据呢 ? 如下代码:
class  Test2
{
        public static void main(String[] args)
        {
                int str1 = 3;
                int str2 = 1;       
                swap(str1,str2);
                System.out.println("3str1="+str1+"::"+"3str2="+str2);//输出 3str1=3::3str2=1
                if(str1>str2){
                        int temp = str1;
                        str1 = str2;
                        str2 =  temp;
                }       
                System.out.println("4str1="+str1+"::"+"4str2="+str2);//输出 4str1=1::4str2=3
       
        }
        public static void swap(int str1,int str2){
        System.out.println("1str1="+str1+"::"+"1str2="+str2);//输出  1str1=3::1str2=1
                if(str1>str2){
                        int temp = str1;
                        str1 = str2;
                        str2 =  temp;
                }
        System.out.println("2str1="+str1+"::"+"2str2="+str2);//输出 2str1=1::2str2=3
        }

}



作者: AEhaojiu    时间: 2014-8-5 01:58
swap()函数里面是值传递 就是str1和str2复制一份拷贝给swap()函数进行操作 没有改变它们自身的值
在main函数中 操作的str1和str2是地址,所以它们自身的值改变了 所以两次输出的结果不一样。
作者: fantacyleo    时间: 2014-8-5 02:09
本帖最后由 fantacyleo 于 2014-8-5 02:21 编辑
阿磊 发表于 2014-8-5 01:45
那为什么调用方法不能换位,而直接在主函数中写代码就可以换位呢?并且调用方法的时候str1和str2的值确实 ...

对啊,你往swap中传的是str1和str2的值,那么str1和str2的值是什么?是对象"abcde"和对象"abc"的内存地址,所以你在swap中只能改变"abcde"和"abc"(实际上由于字符串是不可变对象,你也改变不了),不可能改变str1和str2。这就好比,你(str1)的考卷("abcde")做好了交给老师批改(swap),老师可以在你的考卷上进行评分、打钩或画叉(swap中的代码),从而改变你考卷的卷面,但不可能改变这张考卷属于你(str1始终指向"abcde")这个事实。
作者: fantacyleo    时间: 2014-8-5 02:20
阿磊 发表于 2014-8-5 01:45
那为什么调用方法不能换位,而直接在主函数中写代码就可以换位呢?并且调用方法的时候str1和str2的值确实 ...

再打个比方吧:你这次考试得了80分,回到家你爸问你成绩。你拿出一张纸,写上80。你爸很不满意,拿起笔把80划掉,写上90,说”你下次必须考到90以上“。好,你写在纸上的80分,和你的考试成绩80分,只不过值相等而已,但它们是两件完全不同的东西,所以不管你爸怎么涂改你写在纸上的分数,都不可能改变你这次考试的分数是80这件事,对吧?方法的参数传递也一样,swap方法只是接收到str1和str2的值,就像在纸上写上了80分,无论你在swap中怎么折腾(涂改写在纸上的80分),都不影响str1和str2
作者: 阿磊    时间: 2014-8-5 08:18
fantacyleo 发表于 2014-8-5 02:20
再打个比方吧:你这次考试得了80分,回到家你爸问你成绩。你拿出一张纸,写上80。你爸很不满意,拿起笔把 ...

那是不是每个方法都不管用了呢?方法不都是这么调用的吗?如果说因为他们是字符串的原因,那要是int型的不是对象了吧,但为什么也是不行呢。。。。。和字符串一样一样的。
作者: fantacyleo    时间: 2014-8-5 09:09
阿磊 发表于 2014-8-5 08:18
那是不是每个方法都不管用了呢?方法不都是这么调用的吗?如果说因为他们是字符串的原因,那要是int型的 ...

跟是不是对象没关系,参数传递都是值传递。String str1的值是"abcde"的内存地址,int str1的值是3,这两个值不可能通过参数传递而在另一个方法中被改变
作者: 叶飞翔    时间: 2014-8-5 09:17
fantacyleo 发表于 2014-8-5 01:22
对,你操作的是数据,也就是str1和str2两个变量所指向的对象。但是,你没有也不可能操作str1和str2两个变 ...

这位大神已经分析的很详细了,很赞同。
作者: 200米王朝    时间: 2014-8-5 09:32
本帖最后由 200米王朝 于 2014-8-5 09:33 编辑

class  Test
{
         public static void main(String[] args)
        {
                 String str1 = "abcde";
                 String str2 = "abc";        
                swap(str1,str2);  //该函数没有返回值,也没有system.out.println(),所以调用该函数没有任何结果。
                 System.out.println("str1="+str1+"::"+"str2="+str2);//输出结果    str1=abcde::str2=abc
                 if(str1.length()>str2.length()){
                         String temp = str1;
                         str1 = str2;
                         str2 =  temp;  //这里说的是如果前前者字符串长度大于后者就换位,因为str1长度大于str2,所以换位。
                 }        
                System.out.println("str1="+str1+"::"+"str2="+str2);输出结果  str1=abc::str2=abcde
         
        }
         public static void swap(String str1,String str2){
                 
                if(str1.length()>str2.length()){
                         String temp = str1;
                         str1 = str2;
                         str2 =  temp;    //这里str1和str2和上面main函数中的str1,str2是2个完全不同的概念,虽然他们名字碰巧相同。
                 }
         }

}
作者: LFW    时间: 2014-8-5 09:38
我也发帖了,swap(String str1,String str2)的确换到了,但并没有将结果传回给调用者,原因是什么我不知道,所以发帖了
作者: 阿磊    时间: 2014-8-5 09:40
LFW 发表于 2014-8-5 09:38
我也发帖了,swap(String str1,String str2)的确换到了,但并没有将结果传回给调用者,原因是什么我不知道 ...

是啊 上面讲的我都晕晕的。。。还真没懂。。。。郁闷了!
作者: a6511631    时间: 2014-8-5 09:47
fantacyleo 发表于 2014-8-5 02:20
再打个比方吧:你这次考试得了80分,回到家你爸问你成绩。你拿出一张纸,写上80。你爸很不满意,拿起笔把 ...

非常形象!
作者: 阿磊    时间: 2014-8-5 09:47
fantacyleo 发表于 2014-8-5 09:09
跟是不是对象没关系,参数传递都是值传递。String str1的值是"abcde"的内存地址,int str1的值是3,这两 ...

感觉还是晕晕的,能不能帮忙简单的画个图助于理解理解呢?还有数组里面换位,用的也是viod无返回值的,但是那个就可以成功。。。费解,拜托了,真心想弄明白!
作者: 阿磊    时间: 2014-8-5 09:51
阿磊 发表于 2014-8-5 09:47
感觉还是晕晕的,能不能帮忙简单的画个图助于理解理解呢?还有数组里面换位,用的也是viod无返回值的,但 ...

哦哦  好的  感谢啊  坐等你的好消息
作者: LFW    时间: 2014-8-5 09:54
fantacyleo 发表于 2014-8-5 09:09
跟是不是对象没关系,参数传递都是值传递。String str1的值是"abcde"的内存地址,int str1的值是3,这两 ...

那数组的互换swap又怎么实现的?又或者说,我就是希望能抽取出一个将长str2短str1子串,换成短str2长str1的子串    的功能。那怎么做。我就是没明白,为什么数组的互换可以调用方法实现而这个不行。
作者: fantacyleo    时间: 2014-8-5 10:15
LFW 发表于 2014-8-5 09:54
那数组的互换swap又怎么实现的?又或者说,我就是希望能抽取出一个将长str2短str1子串,换成短str2长str1 ...

还是我打的那个比方:假定有个数组int[] a = {1,2,3}  你(a)的考卷({1,2,3})做好了交给老师批改(swap),老师可以在你的考卷上进行评分、打钩或画叉,从而改变你考卷的卷面({1,2,3}变成了{2,1,3}),但不可能改变这张考卷属于你(a始终指向{1,2,3},或者说后来的{2,1,3}的内存地址)这个事实。你所谓的数组互换,要求的是改变卷面,这是可以的。楼主要求的互换是说把自己的考卷换成别人的,这件事在Java中无法通过参数传递来实现
作者: 如果有梦    时间: 2014-8-5 10:21
调用交换函数是复制是传递值,交换函数里交换成功,但是只仅限也函数内,和原来的值没关系
作者: LFW    时间: 2014-8-5 10:51
fantacyleo 发表于 2014-8-5 10:15
还是我打的那个比方:假定有个数组int[] a = {1,2,3}  你(a)的考卷({1,2,3})做好了交给老师批改(swap) ...

能改变对象内存里边的东西,却不能改变引用所指的内存地址?例如a = {1,2,3},调用swap可以让a ={2,1,3},其实是操作了a引用所指的内存地址(如0x1122)中的元素,但是a引用所指的地址值只能是0x1122?
作者: fantacyleo    时间: 2014-8-5 10:59
LFW 发表于 2014-8-5 10:51
能改变对象内存里边的东西,却不能改变引用所指的内存地址?例如a = {1,2,3},调用swap可以让a ={2,1,3}, ...

是的。还有疑问吗?
作者: 亦金亦水    时间: 2014-8-5 11:06
楼上们的都是详解
作者: Bule丶    时间: 2014-8-5 11:28
如果你两个String在main里new出来的呢
作者: fxwb2005    时间: 2014-8-5 11:29
用数组就可以传了,String比较特殊,操作参数不能改变变量本体的值,因为你把参数传过去的时候,方法里的参数会重新划分一块内存出来储存传来的值,你改变的也只是新划分的内存中储存的值,而不是原来变量指向的内存中的值。下面是用数组传参交换数据。
  1. class  Test
  2. {
  3.         public static void main(String[] args)
  4.        {
  5.                     String [] str={"abcde","abc"};      
  6.                swap(str);
  7.                 System.out.println("str1="+str[0]+"::"+"str2="+str[1]);//输出结果    str1=abcde::str2=abc
  8.                 if(str[0].length()>str[1].length()){
  9.                         String temp = str[0];
  10.                         str[0] = str[1];
  11.                         str[1] =  temp;
  12.                 }        
  13.                System.out.println("str1="+str[0]+"::"+"str2="+str[1]);
  14.         
  15.        }
  16.         public static void swap(String [] str){
  17.                
  18.                  if(str[0].length()>str[1].length()){
  19.                  String temp = str[0];
  20.                  str[0] = str[1];
  21.                  str[1] =  temp;
  22.          }      
  23.         }

  24. }
复制代码

作者: LFW    时间: 2014-8-5 11:35
fantacyleo 发表于 2014-8-5 10:59
是的。还有疑问吗?

我把if定义在getMaxSubString函数时,能调换的原因是?
作者: fantacyleo    时间: 2014-8-5 11:49
LFW 发表于 2014-8-5 11:35
我把if定义在getMaxSubString函数时,能调换的原因是?

原因是你只互换了getMaxSubString函数中的两个string,就像你在swap中一样能看到效果,但对getMaxSubString无效。同样,你在getMaxSubString中的互换,对getMaxSubString的调用者无效。
作者: 到处玩的    时间: 2014-8-5 12:04
看到前面的回答,总结一下:
首先,Java中引用数据类型的变量被传进到方法中,并且被做了改动之后结果会被保存下来。而基本数据类型(数字、字符、布尔值)是 按值传递的,String是引用类型。
其次,关于string类型是不可改变的问题:  string类型确实是是不可改变的,我感觉这也不是String不能改变的原因,也不是申明方式不同的原因,因为在main函数和调用swap函数之后,在swap函数里面打印的时候也是换了的,代码和运行结果如下:
public class  Demo {
    public static void main(String[] args) {
        String str1 = new String("abcde");
        String str2 = new String("abc");
        swap(str1,str2);
        System.out.println("--调用swap之后---str1="+str1+"::"+"str2="+str2);
        //输出结果    str1=abcde::str2=abc
        if(str1.length()>str2.length()) {
            String temp = str1;
            str1 = str2;
            str2 =  temp;
        }
        System.out.println("main函数中---str1="+str1+"::"+"str2="+str2);
        //输出结果  str1=abc::str2=abcde

    }
    public static void swap(String str1,String str2) {
        System.out.println("wap函数中,还没变化---str1="+str1+"::"+"str2="+str2);
        if(str1.length()>str2.length()) {
            String temp = str1;
            str1 = str2;
            str2 =  temp;
        }
        System.out.println("wap函数中变化之后---str1="+str1+"::"+"str2="+str2);
    }

}
结果:
wap函数中,还没变化---str1=abcde::str2=abc
wap函数中变化之后---str1=abc::str2=abcde
--调用swap之后---str1=abcde::str2=abc
main函数中---str1=abc::str2=abcde

所以,真正的原因到底是什么呢?
作者: 怀念黑海岸    时间: 2014-8-5 12:05
本帖最后由 怀念黑海岸 于 2014-8-5 12:07 编辑

可以简而概之:当我们往一个方法里面传递参数时,如果这个参数是基本数据类型包括String,那么实际上传递进去的只是这个基本数据类型的一个复制品而已,因此在这个方法内对这个参数的操作其实都只是对这个复制品的操作,而这个参数如果是引用类型,即一个对象的话,那么传递进去的是这个对象的引用,那么在这个方法内对这个参数的操作都是操作这个对象本身。例如下面的例子:
class TestSwap{
                public static void main(String []args){
                        int a=1,b=3;
                        Person p1=new Person("Li");                                
                        Person p2=new Person("Du");
                        System.out.println("Before swap-->"+a+","+b);
                        swap(a,b);
                        System.out.println("After swap-->"+a+","+b);
                        System.out.println("---------------------------");
                        String str1="abc";
                        String str2="def";
                        System.out.println("Before swap-->"+str1+","+str2);
                        swap(str1,str2);
                        System.out.println("After swap-->"+str1+","+str2);
                        System.out.println("---------------------------");
                        System.out.println("Before swap-->"+p1.name+","+p2.name);
                        swap(p1,p2);
                        System.out.println("After swap-->"+p1.name+","+p2.name);
        }
        public static void swap(String str1,String str2){  
                System.out.println("Before swap-->"+str1+","+str2);
                String temp =str1;
                str1=str2;
                str2=temp;
                System.out.println("After swap-->"+str1+","+str2);
        }
        public static void swap(int a,int b){  //对于基本数据类型,传入a,b实际上是将a,b复制一份后对复制的a,b进行操作。
                System.out.println("Before swap-->"+a+","+b);
                int temp =a;
                a=b;
                b=temp;
                System.out.println("After swap-->"+a+","+b);
        }
        public static void swap(Person p1,Person p2){//对于引用数据类型,传入的引用地址,实际上的操作就是对对象本身的操作
                String temp=p1.name;
                p1.name=p2.name;
                p2.name=temp;
        }
}
class Person{
        String name;
        Person(String name){
                        this.name =name;
                }
        }
运行结果:
Before swap-->1,3
Before swap-->1,3
After swap-->3,1
After swap-->1,3
--------------------------
Before swap-->abc,def
Before swap-->abc,def
After swap-->def,abc
After swap-->abc,def
--------------------------
Before swap-->Li,Du
After swap-->Du,Li

作者: fxwb2005    时间: 2014-8-5 12:18
fantacyleo 发表于 2014-8-5 10:59
是的。还有疑问吗?

C#里面就可以用ref将值传递变为引用传递……
作者: fantacyleo    时间: 2014-8-5 12:36
fxwb2005 发表于 2014-8-5 12:18
C#里面就可以用ref将值传递变为引用传递……

我知道,ref就跟C中的&和pascal中的var一样,其实都是值传递,只不过传递的是变量的地址值而已。引用传递这个概念当然是可以定义的,我只是认为一个值传递概念就足以涵盖加与不加ref、&、var的情况,而且可以将Java和其他语言的参数传递进行统一解释。多一个概念对初学者来说可能会适得其反
作者: tianleboy    时间: 2014-8-5 12:54
这个问题你如果学过c的话就特别好理解。在子方法里所有变化的值只限于子方法里面。在主函数里还是原来的值。
在c中,如果你想在子方法里把主函数的值变化的话,就只能加指针,是传参数的时候,传的引用对象。
作者: 会飞De石头    时间: 2014-8-5 13:48
6楼正解。。。
作者: 唕    时间: 2014-8-5 13:50
各种分析各种回复各种比喻
作者: danmo    时间: 2014-8-5 13:54
使用swap函数,传入的值只是原来值的一个拷贝,在函数中并没有改变原来的值,如改变原来的值使用传地址值方式,
作者: 阿磊    时间: 2014-8-5 17:17
怀念黑海岸 发表于 2014-8-5 12:05
可以简而概之:当我们往一个方法里面传递参数时,如果这个参数是基本数据类型包括String,那么实际上传递进 ...

这个例子的话有点了解了,但是还不全懂  呵呵,就是对于数据类型和String都是不可能改变地址值的,但是对于一个对象的话就是可以的
作者: 怀念黑海岸    时间: 2014-8-5 17:23
本帖最后由 怀念黑海岸 于 2014-8-5 17:27 编辑
阿磊 发表于 2014-8-5 17:17
这个例子的话有点了解了,但是还不全懂  呵呵,就是对于数据类型和String都是不可能改变地址值的,但是对 ...

你不管字符串数据是引用类型还是基本类型,原因主要在于他们都是存储于常量池中的,而对象都是存在于堆内存中的,用这个方法来区分对待这个问题,你就能轻松的理解了。
你只需记住,调用函数时往函数里面传参数,其实都传的是具体的数值,不同在于对于基本数据类型和字符串,是直接将传入的数据先复制一份,然后再将复制的这份传进去,你怎么修改穿进去的数据都是修改复制版本,他本身是不会改变的。而传递的是引用数据类型的话就是传递这个对象的地址,那么你方法里的任何对对象操作都会导致你会直接修改传进去的这个对象。

作者: Darkhorse′Xa    时间: 2014-8-5 17:25
本帖最后由 Darkhorse′Xa 于 2014-8-5 17:35 编辑

了解一下值传递和引用传递就懂了.
但是Java里头只有值传递

作者: 阿磊    时间: 2014-8-5 17:31
fantacyleo 发表于 2014-8-5 11:49
原因是你只互换了getMaxSubString函数中的两个string,就像你在swap中一样能看到效果,但对getMaxSubStri ...

貌似有点懂了,但是还是希望你给个图哈  晚上等你消息,让我更加明白:lol
作者: paozhuanyinyu    时间: 2014-8-5 18:50
swap方法中的str1和str2跟主方法中的两个字符串名字相同,但是它们不是同一个字符串。
作者: cs8630323    时间: 2014-8-5 20:44
去看看核心技术卷一 115页4.5方法参数,对java传递参数机制有详细说明
作者: fantacyleo    时间: 2014-8-5 21:22
画了两张内存图:
1. main函数调用swap函数,将参数传递完毕但swap函数中的代码尚未开始执行前

可以看到,swap函数中的str1和str2与main函数中的同名变量是两回事,只不过它们有相同的值——对象所在的堆地址值(这里就不考虑常量池了),这就是所谓的值传递。除了值相同以外,两个函数的同名变量之间是独立的,互不干扰

2. swap函数中的代码尚执行完毕后

尽管swap函数中的str1和str2交换了指向,但不影响main函数中的str1和str2的指向

作者: zhouqun    时间: 2014-8-5 21:32
swap方法进栈后,将地址值赋值了一份,也执行了方法内的换值,但是swap出栈后,打印的还是主函数里面的值,第二个确实是在主函数中互换了,所以打印的值变化了。如果你想swap方法也可以交换字符串的话,就需要将交换后的字符串返回给调用者,并打印,那么结果肯定是改变了。
作者: 阿磊    时间: 2014-8-5 22:44
fantacyleo 发表于 2014-8-5 21:22
画了两张内存图:
1. main函数调用swap函数,将参数传递完毕但swap函数中的代码尚未开始执行前

哦 哦 大概有点明白了,看的很清楚,现在如果换成其他的参数也是一样的吧?那数组的换位我能这样理解吗?就是方法和mian函数一样都指向了数组的地址值,但是方法操作的是数组内部的元素,换了位置,但是mian仍然指向它,所以在数组内部换位以后,主函数中要操作数组仍然是同一个数组,但是它已经换位了,这样理解可以吗?
作者: 郭旭辉    时间: 2014-8-5 22:46
我是来学习的。。。。
作者: fantacyleo    时间: 2014-8-5 22:54
阿磊 发表于 2014-8-5 22:44
哦 哦 大概有点明白了,看的很清楚,现在如果换成其他的参数也是一样的吧?那数组的换位我能这样理解吗? ...

嗯,正确
作者: 阿磊    时间: 2014-8-5 23:13
fantacyleo 发表于 2014-8-5 22:54
嗯,正确

多谢多谢。。。。:handshake
作者: 刘小印    时间: 2014-8-5 23:34
你应该没搞懂局部变量把,而且字符串是不能被修改的,
作者: 阿磊    时间: 2014-8-5 23:37
刘小印 发表于 2014-8-5 23:34
你应该没搞懂局部变量把,而且字符串是不能被修改的,

现在差不多懂了 在大牛们的帮助下  模模糊糊懂了  呵呵
作者: 威猛的小熊    时间: 2014-8-5 23:46
学习学习。。。
作者: dreamseekerkun    时间: 2014-8-6 01:09
我是来学习的




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2