String是值类型,还是引用类型

        一直记得,JAVA语言除了8大基本类型(byte,short,char,int,long,float,double,boolean),其他类型皆为引用类型。但是,今天有人告诉我String为值类型,顿时迷惑了。回来百度一通,有人说String是值类型,也有人说String是引用类型,大家都有自己的说法。

        好吧,求人不如求己,实践出真知。


        首先申明下结论:Java中的String是正宗的引用类型,但是,一定条件下,String会表现出一定的值特性。贴上代码,开工:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toast.makeText(this, "test1() = " + test1(), Toast.LENGTH_LONG).show();
        Toast.makeText(this, "test2() = " + test2(), Toast.LENGTH_LONG).show();
        Toast.makeText(this, "test3() = " + test3(), Toast.LENGTH_LONG).show();
    }


    private boolean test1() {
        String a = "123";
        String b = "123";
        return a == b;
    }

    private boolean test2() {
        String a = new String("123");
        String b = new String("123");
        return a == b;
    }

    private boolean test3() {
        String a = "123";
        doTest3(a);
        return TextUtils.equals("123", a);
    }

    private void doTest3(String str) {
        str = str + "123";
    }
}

        从test1开始分析:

        test1()返回值为true,看起来有点像值类型。但是,一切都是可以解释的:

        首先分析:


        String a = "123";

       和


        String a = new String("123");
       的不同之处:

  • 前者的String对象分配在堆中,但在常量池中保存了指向String对象的指针,而a为String型指针,指针的内容和常量之中“123”对应的指针相同。具体来说,执行过程如下:
    • 首先,常量池中查找“123”的指针
    • 如果在常量池中未能找到“123”的指针,则在堆中分配“123”的内存空间,把地址保存到常量池中,并把这个地址赋值给String型指针a
    • 如果在常量池中找到“123”的指针,说明堆中已经存在“123”的实体,因为常量表示一个不可变的对象,所以,没有必要再创建新的实例,直接把常量池中的指针内容赋值给String型指针a
  • 而后者,实际上涉及到两个String实体,“123”在堆中存在一个实体,并且在常量池中存在一个指向“123”的指针,而new String()会在堆中创建一个新的String实体,并深度拷贝“123”的内容,并返回新的String实体的地址,赋值给指针a

         所以,test1中,a和b两个指针都指向常量“123”的实体,他们的值相等,故a==b为true。

         而test2中,a和b两个指针都指向各自的String实体,虽然两个String实体都深度拷贝自常量“123”,所以内容相同,但是,因为不是同一个String实体,故而内存地址不同,a==b为false。

         说到这里,顺便说明一个C#和Java不同之处:C#对于所有的String对象都做了常量化处理,所以,内容确定的String实体在堆上只会有一个实例。故,如果test2函数以C#语言实现,则仅会有一个实例,a==b为true,这也是为什么C#中,String虽然是引用类型,却可以使用“==”操作符判断两个字符串相等的原因。


         再来说下test3

         粗看时,string类型作为引用传递,似乎应该被doTest3函数所改变,故TextUtils.equal("123","123123")应该为false才对,而实际测试结果则是:test3()返回值为true。

         这里,让我通过调试来分析原因:

  • 首先,创建a,a的实体地址为“830042018440”

  • 接着,进入到doTest3函数中,str的实体地址也为“830042018440”,这时,内存中存在a和str两个指针,他们都指向同一个String实体

  • 然后,我们发现str指针指向了一个新的实体“123123”,地址为“830042152712”,但是这并不影响a指针,a指针仍旧指向之前的实体“123”

  • 最后,函数返回,a指针果然还指向“123”,所以TextUtils.equal("123","123")返回true


        总结:

        我想前面的解释已经很清楚了,String类型的确是引用类型,虽然某些情况下,看起来有些像值类型。

        另外,相信有些细心的朋友已经发现了,String的实现实际上就是char[]加变量count(先忽略offset和hash code),的确从本质上来说,String可以归结于char[],既然char[]是引用类型的,那String怎么可能是值类型呢?


  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值