黑马程序员技术交流社区

标题: String的小问题…… [打印本页]

作者: 迷失的小Z    时间: 2014-11-20 21:23
标题: String的小问题……
有个面试题是String s1 = "abc";
String s2 = new String("abc");说s1和s2的不同,地址值得问题我明白,但是我不太明白的是为什么常量池中的"abc"为毛也是一个对象……而且究竟是常量池中所有的常量都是对象还是只有字符串常量比较特别才是对象……
还有,引用数据类型形参的改变会影响到实参的数据,为何单单String不可以。求解释原理……
作者: 王小忠    时间: 2014-11-20 21:34
先来说说为什么引用数据类型形参的改变会影响到实参的数据,为何单单String不可以的问题吧.
  我们都知道"引用数据类型形参的改变会影响到实参的数据"这句话是从C语言里面的指针里引出来的,引用变量形参改变时实参也会跟着改变,但是,我们有知道,方法在调用时只是单纯的copy了实参的值然后传给形参,如果是地址的话,形参拿到地址便可以操作地址指向的内容,但是请注意,String 有一个缺点就是一旦被赋值,其长度和内容便不能被改变.所以当方法在操作String 形参指向的内容是,会另外开辟一个空间来存贮操作后的结果,而原来的实参指向的数据不会被改变,这也就是你的问题的又来了.建议你可以对比String  和StringBuffer来测试一下
作者: 迷失的小Z    时间: 2014-11-20 22:04
王小忠 发表于 2014-11-20 21:34
先来说说为什么引用数据类型形参的改变会影响到实参的数据,为何单单String不可以的问题吧.
  我们都知道" ...

哦,thanks……那么第一个问题呢?
作者: 奋斗的蜗牛ksd    时间: 2014-11-20 22:33
本帖最后由 奋斗的蜗牛ksd 于 2014-11-20 22:44 编辑

回答你第一个问题:
在Java程序中,有很多的东西是永恒的,不会在运行过程中变化。比如一个类的名字,一个类字段的名字/所属类型,一个类方法的名字/返回类型/参数名与所属类型,一个常量,还有在程序中出现的大量的字面值。

  比如下面小段源码中粗体代码显示的部分:

  public class ClassTest {

           private String itemS ="我们 ";

           private final int itemI =100 ;

           public void setItemS (String para ){...}

  }

  而这些在JVM解释执行程序的时候是非常重要的。那么编译器将源程序编译成class文件后,会用一部分字节分类存储这些粗体代码。而这些字节我们就称为常量池。

详细分析:
(1)String s=new String("Hello world");       编译成class文件后的指令(在myeclipse中查看):

  事实上,在运行这段指令之前,JVM就已经为"Hello world"在堆中创建了一个拘留字符串( 值得注意的是:如果源程序中还有一个"Hello world"字符串常量,那么他们都对应了同一个堆中的拘留字符串)。然后用这个拘留字符串的值来初始化堆中用new指令创建出来的新的String对象,局部变量s实际上存储的是new出来的堆对象地址。

(2)String s="Hello world";

  这跟(1)中创建指令有很大的不同,此时局部变量s存储的是早已创建好的拘留字符串的堆地址。

补充一下 常量池的概念-----我是查的资料, 分享一下----
常量池   在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式。
常量池其实也就是一个内存空间,不同于使用new关键字创建的对象所在的堆空间。

再补充一个自己昨天思考String 特性时候的测试: 目的是为了测试一下 new 和常量池的区别,结果如下
String类也是java中用得多的类,同样为了创建String对象的方便,也实现了常量池的技术。
  测试代码如下:
  public class Test{
  public static void main(String[] args){
  //s1,s2分别位于堆中不同空间
  String s1=new String("hello"); //new出来的是堆  而“hello”是存放在常量池里的对象,String 申请出来的数据是对象,s1只是引用而已!
  String s2=new String("hello");
  System.out.println(s1==s2);//输出false
  //s3,s4位于池中同一空间
  String s3="hello" String s4="hello";
  System.out.println(s3==s4);//输出true
  }
  }
  用new String()创建的字符串不是常量,不能在编译期就确定,所以new String()创建的字符串不放入常量池中,他们有自己的地址空间。

作者: 迷失的小Z    时间: 2014-11-20 22:44
奋斗的蜗牛ksd 发表于 2014-11-20 22:33
回答你第一个问题:
在Java程序中,有很多的东西是永恒的,不会在运行过程中变化。比如一个类的名字,一个 ...

首先感谢你的回答,其实我要问的是为什么在常量池中的字符串常量是对象……
作者: 奋斗的蜗牛ksd    时间: 2014-11-20 22:45
迷失的小Z 发表于 2014-11-20 22:44
首先感谢你的回答,其实我要问的是为什么在常量池中的字符串常量是对象…… ...

那你还是没有认真看我写的哦-------字符串常量和常量 不是一个概念的。
作者: 杨佳名    时间: 2014-11-20 22:45
刚刚给一个基础班朋友画了一个图,还好没有删。用到了


作者: 奋斗的蜗牛ksd    时间: 2014-11-20 22:55
本帖最后由 奋斗的蜗牛ksd 于 2014-11-20 22:58 编辑
杨佳名 发表于 2014-11-20 22:45
刚刚给一个基础班朋友画了一个图,还好没有删。用到了

来而不往非礼也哈,不过不确定 常量池是否在方法区中。
常量池中存放的对象  都是 编译 时期存放的吧?  

查资料 补充最后一个练习佐证  加深理解----我只是搬运工
常考的一道面试题:
  
  String s1 = new String("s1") ;
  String s2 = new String("s1") ;
  上面创建了几个String对象?
  答案:3个 ,编译期Constant Pool中创建1个,运行期heap中创建2个.(用new创建的每new一次就在堆上创建一个对象,用引号创建的如果在常量池中已有就直接指向,不用创建

作者: 奋斗的蜗牛ksd    时间: 2014-11-20 23:02
奋斗的蜗牛ksd 发表于 2014-11-20 22:45
那你还是没有认真看我写的哦-------字符串常量和常量 不是一个概念的。

这样解释可能更容易理解:
String是一个类, String  s1=“abc”;  类名 引用名=对象
对象只是类的一个实例化后的数据而已,对象不只是new出来的哦!这样理解简单。
作者: 迷失的小Z    时间: 2014-11-20 23:08
奋斗的蜗牛ksd 发表于 2014-11-20 22:45
那你还是没有认真看我写的哦-------字符串常量和常量 不是一个概念的。

还是有点晕……,换种说法吧,为什么"hello"是对象 ……我纠结这个……
作者: 迷失的小Z    时间: 2014-11-20 23:15
奋斗的蜗牛ksd 发表于 2014-11-20 23:02
这样解释可能更容易理解:
String是一个类, String  s1=“abc”;  类名 引用名=对象
对象只是类的一个 ...

哦 好吧,这样理解也通……
作者: 杨佳名    时间: 2014-11-20 23:18
本帖最后由 杨佳名 于 2014-11-20 23:19 编辑
奋斗的蜗牛ksd 发表于 2014-11-20 22:55
来而不往非礼也哈,不过不确定 常量池是否在方法区中。
常量池中存放的对象  都是 编译 时期存放的吧?  
...

很肯定是存放在方法区中。我理解的字符串对象,跟其他对象一样,只不过是存放的位置不一样。




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