在多线程学习系列 - 2 - Immutable Pattern
中,作者告诉我们不可变类的好处以及如何设计不可变类
下面的内容是Effective Java(v2)中的第15条--使4可变性最小化
作者给出了设计不可变类的一些建议,多线程学习系列 - 2 - Immutable Pattern
中一些建议过于强硬,比如类要是final,域要是final等等
1.不要提供任何会修改对象状态的方法
2.保证类不会被扩展(final类)
可以防止粗心或恶意的子类假装对象的状态已经改变,从而破坏该类的不可变行为
3.使所有的域都是final的
4.使所有的域都是私有的
防止客户端直接修改这些对象
5.确保对于任何可变组件的互斥访问
如果类具有指向可变对象的域,则必须确保该类的客户端无法获得这些对象的引用
永远不要用客户端提供的对象引用来初始化这样的域
也不要从任何访问方法中返回该对象引用
在构造器,访问方法和readObject方法中使用保护性拷贝
effective java中给了一个例子
Complex为复数,具有实部和虚部
public final class Complex {
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public static final Complex ZERO = new Complex(0, 0);
public static final Complex ONE = new Complex(1, 0);
public static final Complex I = new Complex(0, 1);
// Accessors with no corresponding mutators
public double realPart() { return re; }
public double imaginaryPart() { return im; }
public Complex add(Complex c) {
return new Complex(re + c.re, im + c.im);
}
public Complex subtract(Complex c) {
return new Complex(re - c.re, im - c.im);
}
public Complex multiply(Complex c) {
return new Complex(re * c.re - im * c.im,
re * c.im + im * c.re);
}
public Complex divide(Complex c) {
double tmp = c.re * c.re + c.im * c.im;
return new Complex((re * c.re + im * c.im) / tmp,
(im * c.re - re * c.im) / tmp);
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Complex))
return false;
Complex c = (Complex) o;
return Double.compare(re, c.re) == 0 &&
Double.compare(im, c.im) == 0;
}
@Override public int hashCode() {
int result = 17 + hashDouble(re);
result = 31 * result + hashDouble(im);
return result;
}
private int hashDouble(double val) {
long longBits = Double.doubleToLongBits(re);
return (int) (longBits ^ (longBits >>> 32));
}
@Override public String toString() {
return "(" + re + " + " + im + "i)";
}
}
下面分析下复数这个类
它是final的,成员变量均为private final
除了构造函数,没有任何能够改变自己状态的方法
构造函数的参数为double,也是不可变的
有几个方法是返回Complex,注意这些方法,他们无一不是new一个新Complex,并没有修改自身
所以Complex是不可变的
不可变对象可以自由的被共享
我们应该尽量的重用现有实例,所以对于频繁使用的值应该提供public final的常量
例如:
public static final Complex ZERO = new Complex(0,0);
public static final Complex ONE = new Complex(1,0);
public static final Complex I = new Complex(0,1);
我们还可以进一步扩展一下想法,给它提供一个静态工厂方法
,这样可以更灵活的控制缓存,自己选择是否创建新的实例
静态工厂有许多优势,举例来说:假如你想以极坐标的方式创建一个复数,那么构造函数可能会是下面这样
public Complex(double re, double im){...}
可是已经有了一个相同签名的构造函数了,所以这样是不行的
而使用静态工厂,你就可以更改签名了,例如下面这样
public static Complex valueOfPolar(double r, double theta) {
return new Complex(r * Math.cos(theta),
r * Math.sin(theta));
}
不可变对象无须任何拷贝,所以也不用提供clone或者拷贝构造器
ps:听说这点在java平台的早期不好理解,所以String还保留了拷贝构造器
不可变对象还可以共用内部信息
BigInteger中的一个例子
在它的negate方法中新建另一个对象,数值是一样的,符号是相反的,并没有拷贝数组
final int[] mag;
public BigInteger negate() {
return new BigInteger(this.mag, -this.signum);
}
对于第二条保证类不会被扩展(final类)
,可以不用那么严格,不一定非要是final类才可以
我们把所有的构造函数设置为private,通过静态工厂创建实例,那么也一样可以达到final类的效果
添加如下方法即可
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
对于第三条使所有的域都是final的
,也可以不用那么严格
只要没有一个方法能够对对像的状态产生外部可见的改变就可以
这些非final域在第一次被请求执行计算这些计算结果的时候把一些开销昂贵的计算结果缓存在这些域中达到复用,因为对象是不可变的,所以每次请求返回的结果一致
转贴请保留以下链接
本人blog地址
http://su1216.iteye.com/
http://blog.csdn.net/su1216/
分享到:
相关推荐
《Java多线程编程模式实战指南》系列文章配套源码。这些文章已扩充为一本书:《Java多线程编程实战指南(设计模式篇)》,由电子工业出版社出版,当当、亚马逊、京东、互动出版网、淘宝等各大书店有售。 【样章】 ...
优点:不需使用synchronized保护类的属性,适用于类被多个线程共享,且有可能被频繁访问。 标准类中mutable类:StringBuffer 标准类中Immutable类:String、BigInteger、BigDecimal、Pattern、基本类型的包装类 java...
在Java编程中,**多线程安全**是确保程序在多线程环境中能够正确运行的重要方面。下面是一些可以增加Java SE程序线程安全性的技术: - **b. 写类使它们不可变(Immutable)**:不可变对象是指一旦创建后就不能改变...
Guava提供了原子类的扩展,如AtomicDoubleArray、AtomicLongMap等,以及Future和ListenableFuture,它们在多线程环境下提供了高效的同步机制。此外,CountDownLatch、CyclicBarrier、Semaphore等并发工具类也使得...
- **并行性**:Rust内置了高级的并发模型,支持安全的多线程编程,有助于编写可扩展的应用程序。 - **跨平台**:Rust支持多种操作系统,如Windows、macOS和Linux等。 #### 2. Rust与C++相比有哪些优势和不同之处? ...
一、不可变模式(Immutable Pattern) 在并行软件开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步。而同步操作对系统性能是...
一、不可变模式(Immutable Pattern) 在并行软件开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步。而同步操作对系统性能是...
8. **JUnit and TestNG**: 测试是软件开发中的重要环节,JUnit是Java单元测试的标准库,TestNG则提供了更丰富的功能,如支持多线程测试、参数化测试等。 9. **StringTokenizer 和 Pattern/Matcher**: `...
此外,它还包含了一些便捷的集合操作方法,如Joiner和Splitter用于字符串处理,以及Immutable集合类,确保集合在创建后无法被修改,这对于线程安全和防止意外修改非常有用。 2. **缓存机制**:Guava的Cache模块允许...
对于并发编程,Rust提供了一种名为“共享所有权”的模型,其中数据可以由多个线程同时读取,但写入操作必须互斥。这得益于其所有权和借用检查机制,Rust在编写线程安全的代码时无需依赖复杂的同步原语。 除此之外,...
5. **接口的优先级高于抽象类(Prefer Interfaces to Abstract Classes)**:Java的多继承特性限制了抽象类的使用,接口则允许类实现多个行为。IntelliJ IDEA的"Extract Interface"功能可以帮助我们快速创建接口。 ...
以上只是《Effective Java》中的一部分重要概念,实际书籍中还涉及了很多其他主题,包括集合、多线程、序列化、类设计等方面的最佳实践。通过对这些原则的深入理解和应用,Java开发者可以提升代码质量,编写出更专业...
开关语句(Switch)根据表达式的值执行相应的代码块,同步(Synchronization)用于控制多线程的访问。语法(Syntax)是编程语言的规则,线程(Thread)是并发执行的程序部分。变量(Variable)存储数据,可见性...