对于引用类型,我们往往出现理解上的错误,甚至某些教材编写者也是一样,往往片面的将引用类型与同类型的两个变量引用同一个实例等同起来,最近我看了本书,是清华大学出版社出版的Java从入门到精通,其中有关于String 类型一段的描述如下:
String str1,str2;
str1="We are students";
str2="We are students";
同时内存示意图上显示 str1与str2指向了同一个字符串"We are students",
此处出现的明显错误对于老C系列程序员而言,可以轻易纠正,但对于初学者而言,这无疑是误人子弟,我们在定义引用类型与值类型的时候,基本上是继承了原来的c系列语言中对于内存寻址的概念,如果该地址存放的是地址,则为引用,而如果该地址存放的是操作数,则为值类型。而String 类型比较特殊,它在每次进行构造的时候都会产生一个新的字符数组,并且自动丢弃原来的字符数组,因此,str1与str2实际上并非同一个实例,他们是两个不同的实例,改变其中一个,并不会联动使得另外一个发生改变。
而如果我们采用
String str1,str2;
str1=str2="We are students";
这种方式赋值,我们同样会得到一个表面的结果——str1与str2指向的不是同一个实例,其实这是一种误区,我们采用这种方式赋值的时候,其实两个字符串变量指向的确实是同一个字符数组地址,但是为什么会在改变其中一个的时候,另外一个不会发生改变呢?这还是因为字符串的特殊性,他在构造的时候总是产生一个新的字符数组,我们如果改变了其中一个字符串变量的值,其实他的地址已经与另外一个不一样了,他会指向一个新的字符数组地址,而原来的那个却没有发生改变,所以,会有这种表面现象使得我们误以为字符串类型是属于值类型。总的来说,String类型是一个具有值类型特性的引用数据类型。

解决方案 »

  1.   

    至于为什么说字符串是每次构建一个新字符数组,大家可以去看看java.lang包中的String类型源码,可以发现其具体的成员是final char[]数组,而我们知道数组与泛型集合不一样,是一次性构建的,所以字符串也一定是一次性构建。
      

  2.   

    纠正一点,一开始我说了str1与str2指向的不是同一个实例是错误的,实际上为了节约存储资源,对于字符串我们总是进行重复利用的。可以利用比较运算符“==”的比较特性(直接比较两变量或操作数的存储值,不论该变量存储的是地址还是值)编写如下测试代码:
        public static void main(String[] args){   
           String a,b;
           a="cc";
           String d="dd";
           b="cc";
           System.out.println(a==b);
        }
    运行输出结果应该为true.而如果
    public static void main(String[] args){   
           String a,b;
           a=b="cc";
           b="dd";
           System.out.println(a==b);
        }
    运行输出结果为false。
      

  3.   

    1:
    public static void main(String[] args){   
      String a,b;
      a="cc";
      String d="dd";
      b="cc";
      System.out.println(a==b);
      }
       
      变量a和b的值相同都是"cc"肯定相等啦!2:
    public static void main(String[] args){   
      String a,b;
      a=b="cc";
      b="dd";
      System.out.println(a==b);
      }
     
    变量a值是"cc"; b是"d" 这怎么可能是true呢??
      

  4.   

    楼上第一个问题的理解方式是错误的,字符串变量的赋值方式不一样,结果也会不一样,在第一个问题中,由于在内存中已经存在一个"cc"字符串的实体,在第二次赋值给b时,同样是将这个"cc"的地址赋给了b,这是对同一个字符串的重复引用,而如果使用这种方式:
    String a=new String ("cc");
    String b=new String ("cc");
    那么a==b只能返回false,而不是true。
    第二个问题是你眼花了呵呵,最后一句话是false.
      

  5.   

    基本数据类型,都有一个封装的类,如int  Integer,boolean Boolean...
    而String没有封装类,却可以使用对象操作方法,length(),substring()...
    String却又总给人基本类型的假象,就像楼主说的具有基本类型特性的引用类型
    测试一个应用类型数据很容易
    int[] a={1,2,3};
    int[] b=a;
    a[2]=110;
    System.out.println(b[2]);
    输出结构:110
    但是一个String类型怎么明显的测试特使引用类型,暂时还没有方法
    楼主有什么思想吗,讨论下
      

  6.   

    此处需要注意new关键字是用于创建实例的,两个不同的new 则产生两个不在一个位置的实例,而java中的值类型指针的赋值与c#不一样,为了避免概念上的混淆,是不需要使用new的,可以利用这个规则来进行推论:可以使用new关键字赋值的指针就是引用类型,其他的则为值类型。欢迎拍砖
      

  7.   

    str1与str2实际上并非同一个实例,他们是两个不同的实例,改变其中一个,并不会联动使得另外一个发生改变。觉得这两个只是指向不指向字符串池里的一个个字符串的程度。两个字符串之间没有什么必然的联系。
      

  8.   

    我到现在还在怀疑字符串池的实际存在,我觉得最简便的方法还是在操作系统初始化的同时建立字符影射表,然后字符串中的每一个字符数组指向字符影射表中的具体值比较简便,也不容易犯错,如果是每次都给与一个新的字符值储存在字符串中,当切换字符值时(比如在从0x16ff切换到0x0084),并不能保证所有字符串的影射同步切换,也就是说,可能会局部改变了某一个字符值,而全局的字符并没有得到切换,这样就会产生两个问题,第一,字符串在不同标准的解释下会有不同的结果,第二,字符串转译的时候,会出现错误,比如中文英文转换时(指gb2312同ascii或者unicode之间的转换),就会出现个别的字符出错,这对于一句完整的语句来说可能会是致命的。所以我觉得字符串池只是个抽象的概念,而实际应用的还是字符影射表,不知道这样理解对不对。