不变模式

不变模式就是为了尽可能的去除并行中的同步操作,提高并行程序的性能,可以使用一种不可改变的对象,依靠对象的不变性,可以确保其在没有同步操作的多线程环境中依然始终保持内部状态的一致性和正确性。并且,不变模式通过回避问题而不是解决问题的态度来处理多线程并发访问控制。

不变模式的主要使用场景

  • 当对象创建后,其内部状态和数据不再发生任何变化。
  • 对象需要被共享,被多线程频繁访问。

不变模式的实现(保证当对象被创建后,不发生任何改变)

  1. 去除setter方法以及所有修改自身属性的方法。
  2. 将所有属性设置为私有,并用final标记,确保不能被修改
  3. 确保没有子类可以重载
  4. 有一个可以创建完整对象的构造函数

对于第四点,在创建对象时,必须指定完整的数据,因为创建后,就无法进行修改了

eg:

public final class Book {
private final String name;
private final String ID; public Book(String name,String ID) {
this.name=name;
this.ID=ID;
}
public String getName() {
return name;
}
public String getID() {
return ID;
}
}

在不变模式中,final关键字起到了重要的作用。

  • 对属性的final定义确保所有数据只能在对象被构造时赋值一次,之后,就永远不发生改变。
  • 对class 的final确保了类不会有子类

注:根据里式原则,子类可以完全的替代父类。如果父类是不变的,那么子类也必须是不变的,但实际上为了防止子类做出一些意外的行为,这里干脆就把子类禁用了

关于String中的不变模式

在JDK中,不变模式有很多应用,如Integer,Long等所有元数据类的包装类,当然,最典型的就是String类了。

对于String类而言,内部是这样定义的:public final class String  是一个被final关键字修饰的类,我们来看看上面说到final关键字的作用,这就意味着:

  1. String中的数据只能在String对象被构建时赋值,之后就永远不能改变
  2. String类不会有子类重载它的方法,也确保了String类生成的对象就是String对象,而不可能是其他类的对象

为什么String要被设计成不变模式呢?

很明显,当然是为了安全

先看到String源码中的如何赋值的

    /** The value is used for character storage. */
private final char value[];

注释部分说的很明白,它是用一个final的字符数组来储存字符串的。

虽然value是不可变,但只是value这个引用地址不可变。但是Array数组是可变!!!

因为Array变量只是stack上的一个引用,数组的本体结构在堆。如图:

String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆里array本身数据不可变。

eg:

 final char value[]={'a','b','c'};
char value2[]={'d'};
//value=value2; //编译器报错,因为用了final修饰
value[0]='d';
for (char v : value) {
System.out.print(v);//输出 dbc
}

很明显,这个value虽然被声明成final,但是如果value溢出了,就很容被修改了。

幸好,在设计String 的时候,value不仅是final的,而且还是private的,并且在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。

再者,String基本约定中最重要的一条是immutable。String是几乎每个类都会使用的类,所以保护String的不可变很重要,所以整个String类被设成final,从而实现禁止继承,避免被其他人继承后破坏。

假如String没有声明为final, 那么String的子类就有可能是被复写为mutable的,这样就打破了成为共识的基本约定。并且会造成不可想象的后果,

如,Hashmap之类的集合的key值,mutable的String有非常大的风险,可能会破坏了Hashmap键值的唯一性等。

安全之外,还可以涉及到性能方面:

String在堆中维护着一个字符串常量池。如字符串one和two都赋值为"something"。它们其实都指向同一个内存地址。

String one = "someString";
String two = "someString";

这样在大量使用字符串的情况下,可以节省内存空间,提高效率。

More:

Why String is Immutable or Final in Java

 

关于String中的不变模式的更多相关文章

  1. Java的String中的subString()方法

    方法如下: public String substring(int beginIndex, int endIndex) 第一个int为开始的索引,对应String数字中的开始位置, 第二个是截止的索引 ...

  2. Here String 中不该进行分词

    我们知道,在 Shell 中,一个变量在被展开后,如果它没有被双引号包围起来,那么它展开后的值还会进行一次分词(word splitting,或者叫拆词,分词这个术语已经被搜索引擎相关技术占用了)操作 ...

  3. C++string中有关字符串内容修改和替换的函数浅析

    1.assign() 原型: //string (1) basic_string& assign (const basic_string& str); //substring (2) ...

  4. 从源代码的角度聊聊java中StringBuffer、StringBuilder、String中的字符串拼接

    长久以来,我们被教导字符串的连接最好用StringBuffer.StringBuilder,但是我们却不知道这两者之间的区别.跟字符串相关的一些方法中总是有CharSequence.StringBuf ...

  5. Java-J2SE学习笔记-查找一个String中,subString的出现次数

    1.查找一个String中,subString的出现次数 2.代码 package Test; public class TestStringContain { public static void ...

  6. string中常用的函数

    string中常用的函数 发现在string在处理这符串是很好用,就找了一篇文章放在这里了.. 用 string来代替char * 数组,使用sort排序算法来排序,用unique 函数来去重1.De ...

  7. String 中的秘密

    Navigation:  数据类型相关 > Delphi 的字符及字符串 > [3] - String 中的秘密   //String 的指针地址及实际的内存地址 var str: str ...

  8. String中的==与Empty

    1.String中的==与Equals方法执行结果一样吗? 我们都知道对于引用类型"=="比较的是引用而不是具体的值,但c#中有一种神奇的叫做操作符重载的东西.官方对String类 ...

  9. Java 字符串比较,String 中的一些方法 == 和 equals 的详解

    "==" 是比较的是两个对象的内存地址,而equals方法默认情况下是比较两个对象的内存地址. 1.String str = "hello"  生成的字符串,首 ...

随机推荐

  1. ROS命令

    rospack find [package_name]功能:获取软件包的路径 例:运行 rospack find roscpp ,会返回 /opt/ros/indigo/share/roscppros ...

  2. CSS学习总结

    CSS基础 简介 什么是CSS? CSS如何创建? 选择器 通用选择器 标签选择器 类选择器 ID选择器 属性选择器 后代选择器 子选择器 相邻元素选择器 伪类选择器 CSS样式 背景 文本 字体 链 ...

  3. LINUX 笔记-ps命令

    使用该命令能确定有哪些进程正在运行和运行的状态.进程是否结束.进程有没有僵死.哪些进程占用了过多的资源等等 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME ...

  4. 关于Page_Load事件发生情况

    Page_Load事件会在第一次加载页面时发生和将该页面回发到服务器时发生 第一种情况Page.IsPostBack返回false,第二种返回True. 若在Page_Load事件中有一些对控件的操作 ...

  5. c#public、private、protected、internal、protected internal修饰符及访问权限

    c#public.private.protected.internal.protected internal修饰符及访问权限 public 公有访问.不受任何限制. private 私有访问.只限于本 ...

  6. RabbitMQ-客户端

    Install-Package RabbitMQ.Client 参考: http://www.rabbitmq.com/download.html https://www.nuget.org/pack ...

  7. open-falcon Agent配置文件修改hostname后,还是有其他名称的endpoint

    问题 open-falcon Agent在配置文件修改hostname后,log日志中还是发现其他名称的endpoint. 原因 Graph, Gateway组件会引用goperfcounter(gi ...

  8. Count the Colors

    Count the Colors Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Subm ...

  9. 事件 event

    添加移除函数(removeEventListener)时三个参数保持一致,否则会失败,而且不会有错误提示 var btn = document.getElementById(“myBtn”); btn ...

  10. 串口接收模块(verilog) 波特率115200

    我来分享一下uart协议之接收verilog代码 顶层实例化 `timecale 1ns / 1ps////////////////////////////////////////////////// ...