查看原文
其他

面试题4解析

2017-04-07 Java面试那些事儿


题目:String类是线程安全,是不是不可变就意味着是线程安全的?

考点


这个题目主要考查不可变类与线程安全之间的关系。


这个题目的意思就是说一个对象是不可变的,那么对这个对象的操作就一定是线程安全的嘛?答案是否定的。


1不可变类与可变类的区别?

不可变类:这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:Interger、Long和String等。

可变类:相对于不可变类,可变类创建实例后可以改变其成员变量值,开发中创建的大部分类都属于可变类。

2怎么设计一个不可变类?

类添加final修饰符,保证类不被继承。

保证所有成员变量必须私有,并且加上final修饰;从而保证了成员变量不可改变。

不提供改变成员变量的方法,包括setter。避免通过其他接口改变成员变量的值,破坏不可变特性。

通过构造器初始化所有成员,进行深拷贝。如果构造器传入的对象直接赋值给成员变量,还是可以通过对传入对象的修改进而导致改变内部变量的值。为了保证内部的值不被修改,可以采用深度copy来创建一个新内存保存传入的值。

在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝。这种做法也是防止对象外泄,防止通过getter获得内部可变成员对象后对成员变量直接操作,导致成员变量发生改变。


3结合String源码来解释?


下面我们结合String的源码来解释,如下:

    public int hashCode() {

        int h = hash;

        if (h == 0 && value.length > 0) {

            char val[] = value;

    

            for (int i = 0; i < value.length; i++) {

                h = 31 * h + val[i];

            }

            hash = h;

        }

        return h;

    }

我们可以发现hashCode方法改变了内部状态值int hash;通过String类的hashCode方法实现,我们可以总结:不可变可以改变其内部状态,只要这种改变不要对外暴露。

如果我们重写hashCode方法,如下:

    public int hashCode() {

        if (hash == 0 && value.length > 0) {

            char val[] = value;

    

            for (int i = 0; i < value.length; i++) {

                hash = 31 * hash + val[i];

            }

        }

        return hash;

    }

这时,就不是线程安全的。我们移走了本地变量h,直接基于内部状态进行改变操作。如果同时几个线程调用这个方法,则返回值可能不同。

这个类因此是可变的。因为两个不同的线程可以看到不同的hash值。

总结:String是不可改变的,因为它是线程安全的,而不是相反。



Hello,伙伴们长按二维码关注我们吧!



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存