不可变类,即实例不能被修改的类,实例中包含的所有信息在对象的生命周期内固定不变。
常见的比如String、基本类型的封装类、BigDecimal、BigInteger。

相对与可变类,不可变更易于设计、实现、使用,且更稳定(less prone to error)更安全。
比如,不可变类本质上就是线程安全的,不需要做同步处理,使用起来也非常方便。

设计不可变类,我们有5个原则

  • 不提供任何可修改对象状态的方法。(这种方法也成为mutator,相应词汇"可变性(mutability)")
  • 保证类不会被扩展(extends),以防止通过子类修改对象状态。防止扩展的方式通常是用final修饰。
  • 尽量使所有的field加上final修饰。
  • 尽量使所有的field都是private。虽然给一个基本类型或者指向不可变引用的field加上final可以防止直接修改field,但我们还需要考虑在以后的版本中变更field的表示法。
  • Ensure exclusive access to any mutable components.
    即,如果一个field指向可变对象,则需要防止用户直接获得该可变对象。可以通过在构造器、getter、readObject中返回对象的拷贝的方式解决这一问题。

以书中代码为例,这是一种典型的不可变类:

public final class Complex {
private final double re;
private final double im; public Complex(double re, double im) {
this.re = re;
this.im = im;
} // 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;
// See page 43 to find out why we use compare instead of ==
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)";
}
}

鉴于不可变对象本质上就是线程安全的,不可变对象可以自由地进行传递,根本不用考虑在构造器或者getter中进行实例拷贝。
但正如上面的代码,使用不可变类的方法改变其自身状态时都要返回一个单独的对象。
如果创建对象的开销较大,这将有可能导致性能问题。

站在类提供者的立场上,我们有两种方法可以解决这一问题:

  • 在类内部使用的操作尽量使用基本类型。
  • 如果可以预想到那些状态经常会改变,则相应提供一个包级私有的配套类,比如,StringBuilder相对于String。

另外,如果因为某种原因不能使用final修饰不可变类,但又不希望被扩展,除了使用final是否还有其他方法。
方法就是,使用public static工厂方法取代所有public构造器。
比如:

public static Complex valueOf(double re, double im) {
return new Complex(re, im);
} public static Complex valueOfPolar(double r, double theta) {
return new Complex(r * Math.cos(theta), r * Math.sin(theta));
}

Java - 将可变性最小化的更多相关文章

  1. Effective Java -- 使可变性最小化

    为了使类成为不可变的,应该遵循以下五条原则: 1. 不要提供任何会下盖对象状态的方法 2. 保证类不会被扩展 3. 使所有的域都是final的 4. 使所有的域都成为私有的 5. 确保对于任何可变组件 ...

  2. 大神为你分析 Go、Java、C 等主流编程语言(Go可以替代Java,而且最小化程序员的工作量,学习比较容易)

    本文主要分析 C.C++98.C++11.Java 与 Go,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...

  3. Effective Java 第三版——17. 最小化可变性

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. 九度OJ 1502 最大值最小化(JAVA)

    题目1502:最大值最小化(二分答案) 九度OJ Java import java.util.Scanner; public class Main { public static int max(in ...

  5. 《Effective Java》笔记 使类和成员的可访问性最小化

    类和接口 第13条 使类和成员的可访问性最小化 1.设计良好的模块会隐藏所有的实现细节,把它的API与实现清晰的隔离开来,模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况: ...

  6. Java之路(六) 局部变量作用域最小化

    将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 将局部变量的作用域最小化的方法有: 方法1:在第一次使用某个局部变量的地方进行声明. a.Java可以在任何可以出现语句 ...

  7. Effective Java 第三版——15. 使类和成员的可访问性最小化

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. Effective Java 第三版——57. 最小化局部变量的作用域

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  9. 最小化 Java 镜像的常用技巧

    背景 随着容器技术的普及,越来越多的应用被容器化.人们使用容器的频率越来越高,但常常忽略一个基本但又非常重要的问题 - 容器镜像的体积.本文将介绍精简容器镜像的必要性并以基于 spring boot ...

随机推荐

  1. Dapper.Contrib.Extensions问题

    Dapper.Contrib.Extensions问题 Dapper.Extension.1.0.0.1\lib\net45\Dapper.Extension.dll Dapper.Contrib.1 ...

  2. Android TextView 嵌套图片及其点击,TextView 部分文字点击,文字多颜色

    1. TextView 中嵌套图片的方法 TextView textView... textView.setText("..."); textView.append(Html.fr ...

  3. Sphinx全文检索

    全文检索 一.生活中的数据总体分为: 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等. 非结构化数据:指没有固定格式或不定长的数据,如邮件,word文档等. 非结构化数据还有一种叫法: ...

  4. “全栈2019”Java第一百零三章:匿名内部类详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. 【Map,HashMap,Vector,List】资料汇总

    深入学习HashMap实现原理 http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532832.html 深入学习Vector原理 http:/ ...

  6. MFC Windows程序开发究竟是什么?

    MFC Windows程序开发究竟是什么? MFC是一个Application Framework.他是一个完整的程序模型. 什么是MFC?MFC是微软公司出品一个Application Framew ...

  7. 简单创建一个完整的struts2框架小程序

    要完成一个struts2框架的搭建, 1.首先应该从官网上下载最新的jar包,网络连接:http://struts.apache.org/download.cgi#struts2514.1,选择下载F ...

  8. leetcode-896-单调数列

    题目描述: 如果数组是单调递增或单调递减的,那么它是单调的. 如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的. 如果对于所有 i <= j,A[i] ...

  9. XMPPFramework核心类介绍

    XMPPFramework结构 在进入下一步之前,先给大家讲讲XMPPFramework的目录结构,以便新手们更容易读懂文章.我们来看看下图: 虽然这里有很多个目录,但是我们在开发中基本只关心Core ...

  10. 2016级算法期末上机-A.简单·Bamboo's Fight with DDLs I

    简单·Bamboo's Fight with DDLs I 分析 一句话:要装满的完全背包问题. 对比完全背包只有一点要改变:初始化为负无穷 传送门: https://buaacoding.cn/pr ...