您的位置:首页 > 职场人生

一个java面试题引发的思考

2016-02-24 14:52 585 查看
先看看这个题目


public class Example{

  String str="good";
  char[]ch={'a','b','c'};

  public static void main(String args[]){
    Example ex=new Example();
    ex.change(ex.str,ex.ch);
    System.out.print(ex.str+" and ");
    System.out.print(ex.ch);
  }

  public void change(String str,char ch[]){
    str="test ok";
    ch[0]='g';
  }
}


输出结果:good and gbc

大体上会有两种困惑:

为什么不是 test ok and gbc ;

为什么 ch 可以改变;

说下我的理解过程:

1.为什么不是 test ok and gbc ?

网上有很多个解释,例如:引用传递和值传递,局部变量,String是个特殊的什么,等等。 觉得解释的有点泛(一听好像解释的通),还有的我个人认为就是错误的。

首先,有的人认为这是引用传递和值传递的问题,可能是每个人的理解方式有差异,java中的传递方式是只有值传递的,不过这个“值”可能并不是那么简单,就拿上面的栗子来说:

change()方法接收的值,并不是 ex.str = “good” 中的 “good”,是ex.str 在内存中的地址(或者说指向)如果非要解释一下的话,那就要在java的堆栈上说了,如果对这概念不是很清晰可以看看这篇文章:http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html

JVM在执行到 change(String str,char ch[]) 这个方法的时候,是新生成了一个栈空间,并且根据方法的定义,在新的栈中声明了自身的 String str 变量。

之后 str 被赋予 ex.str 在堆中对象的地址(指向),那么下面就很好解释了,str 在被赋值之后又经行了一个改变的操作,即将 str 的地址由原来 ex.str 的改变到 “test ok” 的地址(这里解释一下JVM中 str = “test ok”流程,即JVM先在堆的常量池中new了一个“test ok”,之后将这个“test ok”在堆中的地址(指向)赋给 str)。

JVM中change()方法的这个栈在方法结束后就销毁了,和主方法没有多大的联系,而主方法需要打印的是 ex.str 地址的内容,从上面可以看出,change方法改变的只是自身栈中 str 地址,而不是 ex.str 地址所在堆中的内容,所以 “good” 没有变成 “test ok”,当然这有另一个说法就是局部变量,只是觉得单单用“局部变量”四个字去解释,实在不够完善,所以唠叨唠叨。

还有的解释说String这特殊,确实String是特殊的,但是用于解释这个题目是 完全不搭的 。先说个反例辩驳一下,如果说是因为String,所以“good”没有变成“test ok”,那么你们可以试一下:

public class Example{

  int str=10;
  char[]ch={'a','b','c'};

  public static void main(String args[]){
    Example ex=new Example();
    ex.change(ex.str,ex.ch);
    System.out.print(ex.str+" and ");
    System.out.print(ex.ch);
  }

  public void change(int str,char ch[]){
    str=20;
    ch[0]='g';
  }
}


结果也是不变,由此可以看出,并不是因为String特殊的原因。

在JVM中的实现,也是有所区别的,在change方法中 str所赋的值 > 127的时候, JVM不会在堆的常量池中new了一个值,之后将这个值在堆中的地址(指向)赋给 str ;而是直接在栈中将 str 赋值(常量池对于基本数据类型的一些概念,对常量池不太清晰的可以看下这篇文章:http://blog.csdn.net/olanlanxiari/article/details/8104505

2.为什么 ch 可以改变?

在上面的基础上,这个就很容易解释了。执行到change方法时,change用自身在栈中声明的 ch 接收 ex.ch 的地址,即此时 change 中的 ch 是指向 ex.ch 所在堆中的内容。

那么对象已经指定了,此时 ch[0] = ‘g’ 所改变的就是堆中地址的内容。注意!!这里是 ch[0],不是 ch,这就相当于你在对象中修改属性(ch[0]是属性,ch是对象)类似于:

Example ex=new Example();       //  ex 类比 ch
ex.str = "haha";        //  ex.str 类比 ch[0]


这时候内容是改变的!

可以看看这些文章:

java中的基本数据类型一定存储在栈中吗? http://www.cnblogs.com/xiohao/p/4296059.html

java中只有按值传递,没有按引用传递! http://guhanjie.iteye.com/blog/1683637

对堆栈的理解可能有更好的把握。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: