为何String对象是immutable或final的?

1. 问题引入

本文内容主要参考自此处,感兴趣者可以自行移步查看。
另,StackOverflow上也有关于该问题的回答,这里贴出部分答案。

String is immutable for several reasons, here is a summary:

  1. Security: parameters are typically represented as String in network connections, database connection urls, usernames/passwords etc. If it were mutable, these parameters could be easily changed.
  2. Synchronization and concurrency: making String immutable automatically makes them thread safe thereby solving the synchronization issues.
  3. Caching: when compiler optimizes your String objects, it sees that if two objects have same value (a=”test”, and b=”test”) and thus you need only one string object (for both a and b, these two will point to the same object).
  4. Class loading: String is used as arguments for class loading. If mutable, it could result in wrong class being loaded (because mutable objects change their state).

That being said, immutability of String only means you cannot change it using its public API. You can in fact bypass the normal API using reflection. See the answer here.
大概有四个方面内容(出于安全和效率考虑):
1. 安全性:网络通信、数据库链接url、用户名密码等涉及安全的参数都是以String类型的数据。如果String是可改变的,那么这些参数可以很容易被修改,导致系统容易被攻击。
2. 多线程安全:同步和并发问题上,String不可改变的特性使它本身就是线程安全的,因而解决了同步的问题。
3. 缓存问题:当编译器优化我们的String对象时,如果检测到2个或多个对象有相同的值(a=”test”, b=”test’, c=”test”) 因而可以优化为一个string对象。
4. 类加载: 比如”Class.forName(com.fqyuan.string.TestStr)”. String作为类加载的参数。如果String可变的话,那么可能导致加载错误的类, 如本意要加载的是”java.io.Writer”, 结果可能加载成了”mil.vogon.DiskErasingWriter”。

2. 关于String Literal pool 及相关String相等的讨论

部分内容参考自此,感兴趣者可自行查看,这里做了总结。
1. 字符串常量池机制如果String对象是mutable的话,则根本无法正常工作,因为一个引用的改变将使得所有其他的引用的值也会改变。

String A = "Test";
String B = "Test";
/*
 * If calling B.toUppercase(), it create   a brand new string representing the value.
 * /

这里写图片描述
2. 关于如下代码的思考。
以下的内容参考自stackoverflow, 可以通过点击如下链接

package com.fqyuan.String;

public class TestStr {
    public static void main(String[] args) {
        String str1 = "someThing";
        String str2 = "someThing";
        String str3 = new String("someThing");
        String str4 = new String("someThing");
        /*
         * 简言之,equal方法如果没有被重写,那么其效用和'=='相同。 如果重写了equals()方法,那么记得重写hashCode方法。
         * 可以理解为:==比较的是引用的位置,而equal()比较的是引用的内容。
         * 即:equal()如果返回为true,则其hashCode()必然相同。而反之,并不一定成立。
         */
        if (str1 == str2)
            System.out.println("str1 == str2");
        else
            System.out.println("str1 != str2");
        if (str1 == str3)
            System.out.println("str1 == str3");
        else
            System.out.println("str1 != str3");
        if (str3 == str4)
            System.out.println("str3 == str4");
        else
            System.out.println("str3 !=str4");

        if (str1 == str3.intern())
            System.out.println("str1 == str3.intern()");
        else
            System.out.println("str1 != str3.intern()");

        if (str4.intern() == str3.intern())
            System.out.println("str4() == str3.intern()");
        else
            System.out.println("str4.intern() != str3.intern()");

        System.out.println("str1.equals(str3): " + str1.equals(str3));

    }
}
//Running result:
str1 == str2
str1 != str3
str3 !=str4
str1 == str3.intern()
str4() == str3.intern()
str1.equals(str3): true

3. String 不可改变特性,真的不可改变么?

该问题的讨论部分参见此处,感兴趣者可自行查看。
There is no absolute truth in the universe. 哈哈,同样,String也不是绝对不可改变的。

String is immutable, but this only means you can’t change it using public API.
We can do all kinds of tricks with reflection. But we’re basically breaking the “warranty void if remove” sticker on the class the instant you do it.
翻译过来就是:我们常说的String是不可改变的,但这是在一般意义上讲的,是指使用public API我们无法改变String对象的值。但是我们可以使用一些奇淫巧技,Reflection来对其做修改,但是这样就改变了该类设计的初衷,就像商品上“标签撕毁无效”的约定。

package com.fqyuan.String;

import java.lang.reflect.Field;

public class StrMut {

    public static void main(String[] args) throws Exception {
        String str1 = "stack";
        String str2 = "stack";
        String str3 = str1.substring(2);
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);

        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);
        char[] value = (char[]) field.get(str1);
        value[2] = 'u';
        value[3] = 'd';
        value[4] = 'y';
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
    }

}
//Runnign result:
stack
stack
ack
study
study
ack
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值