首页 > Java >  Java 对象不可变是什么意思?

 Java 对象不可变是什么意思?

上一篇 下一篇

不可变到底是什么意思——也就是说,对象可变或不可变的后果是什么?特别是,为什么Java是不可变的?String

我的理解是,该类型类似于 的可变等价物。我什么时候会使用而不是 ,反之亦然?StringBuilderStringStringBuilderString

分割线

网友回答:

不可变意味着一旦对象的构造函数完成执行,就无法更改该实例。

这很有用,因为这意味着您可以传递对对象的引用,而不必担心其他人会更改其内容。特别是在处理并发时,永不更改的对象不存在锁定问题

例如

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foo不必担心调用方可能会更改字符串中的文本。getValue()

如果您想象一个与 类似的类,但使用 a 而不是 a 作为成员,您可以看到调用 to 将能够更改实例的属性。FooStringBuilderStringgetValue()StringBuilderFoo

还要注意你可能会发现的不同类型的不变性:Eric Lippert写了一篇关于这一点的博客文章。基本上,您可以拥有接口不可变但在幕后实际可变私有状态的对象(因此无法在线程之间安全共享)。

分割线

网友回答:

不可变对象是内部字段(或至少影响其外部行为的所有内部字段)无法更改的对象。

不可变字符串有很多优点:

性能:执行以下操作:

String substring = fullstring.substring(x,y);

substring() 方法的底层 C 可能是这样的:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

请注意,无需复制任何字符!如果 String 对象是可变的(字符稍后可能会更改),则必须复制所有字符,否则对子字符串中字符的更改稍后将反映在其他字符串中。

并发:如果不可变对象的内部结构有效,它将始终有效。不同的线程不可能在该对象中创建无效状态。因此,不可变对象是线程安全的

垃圾回收:垃圾回收器更容易对不可变对象做出逻辑决策。

但是,不变性也有缺点:

性能:等等,我以为你说性能是不变性的好处!嗯,有时是这样,但并非总是如此。采用以下代码:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

这两行都用字母“a”替换第四个字符。第二段代码不仅更具可读性,而且速度更快。看看你必须如何为 foo 做底层代码。子字符串很容易,但现在因为空格 5 处已经有一个字符,并且其他东西可能引用了 foo,所以你不能只是更改它;你必须复制整个字符串(当然,其中一些功能被抽象为真正的底层 C 中的函数,但这里的重点是显示在一个地方执行的代码)。

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);
    
    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

请注意,连接被调用两次,这意味着必须循环遍历整个字符串!将此与操作的 C 代码进行比较:bar

bar->characters[4] = 'a';

可变字符串操作显然要快得多。

结语:在大多数情况下,您需要一个不可变的字符串。但是,如果您需要在字符串中进行大量追加和插入,则需要速度的可变性。如果您希望并发安全和垃圾回收具有优势,关键是将可变对象保留在方法的本地:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

由于对象是本地引用,因此不必担心并发安全性(只有一个线程接触它)。而且由于它没有在其他任何地方被引用,它只在堆栈上分配,所以一旦函数调用完成,它就会被解除分配(你不必担心垃圾回收)。您可以获得可变性和不变性的所有性能优势。mutable

分割线

网友回答:

实际上,如果您使用上面建议的维基百科定义,字符串并不是不可变的。

字符串的状态确实会更改构造后的情况。看看hashcode()方法。字符串将哈希代码值缓存在本地字段中,但在第一次调用 hashcode() 之前不会计算它。这种对哈希码的惰性计算将 String 置于一个有趣的位置,作为一个不可变的对象,其状态发生了变化,但在不使用反射的情况下无法观察到它已经发生了变化。

因此,也许不可变的定义应该是一个无法观察到已经改变的对象。

如果一个不可变对象在创建后的状态发生了变化,但没有人可以看到它(没有反射),那么该对象是否仍然不可变?

模板简介:该模板名称为【 Java 对象不可变是什么意思?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Java】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 292次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-16
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
单页式简历模板 微信素材 微信文章 微信模板 微信图片 html5 自适应 企业网站 微信公众平台 响应式
您可能会喜欢的其他模板