`
JackyCheng2007
  • 浏览: 253919 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

不可变类(immutable class)

    博客分类:
  • Java
阅读更多
不可变类,顾名思义就是说类的实例是不可被修改的。实例的信息是在创建的时候提供,并且在整个生命周期中都不可改变。
大家都知道Java的String类是immutable。其实primary的包装类都是immutable的。那么如果让你设计和immutable的class要怎么做呢?
immutable 也就是不变的意思。不可改变的。怎么样让一个类的对象不变呢?
第一你肯定想到,这个类不能被继承,于是class 前面要加 final。这是最直接的办法,也是最简单的办法,但是当你将来想对它扩展的时候就做不到了。也许你想在他的基础上定义一个子类也是immutable的。这时候你可以不再class 前面加final,而是在所有的方法上加final,同样可以防止子类重写它的方法。其实不用继承一样可以扩展这个类,通过composite方法。所以不推荐允许继承immutable类。另外,还有一个办法让immutable类不能被继承,那就是将方法私有或者protected也即是保内私有,然后提供一个静态工厂方法来生产实例。这是最推荐的方法,因为这个方法允许包内扩展,对客户类来说是不可继承的。同时静态工厂方法还有许多其他比构造方法好的地方。

第二你也可能会想到属性不能被改变,那field前面也得加final。你还得把属性私有化,防止被直接访问修改。这样就够了吗?那如果有一个其他类的对象的引用属性呢?怎么保证它不被变?
首先你能做到的就是保证自己的方法里面不改变它的值。然后怎么保证别的类不直接改变它呢?这个引用如果是别的对象传给你的呢?怎么保证不变? 同样,如果是方法中要返回这个属性呢?想想看。
如果它也是immutable的自然也就省心了。但如果不是呢?

看来必须得clone一个新的对象了。关于clone,可以参考liuwang126的文章http://liuwang126.iteye.com/blog/272595
下面是完整的Sample。

package immutable;

public class Immutable {
    private final int a;
    private static final String b;
    private final ClassA oa;
    static{
    	b="";
    }
    public static Immutable getImmutable(int a, ClassA oa){
    	return new Immutable(a, oa);
    }
    protected Immutable(int a, ClassA oa){
    	this.a=a;
    	this.oa=oa.clone();
    }
    public ClassA getOA(){
    	return oa.clone();
    }
    
    public int getA() {
		return a;
	}
	public static String getB() {
		return b;
	}
	public static void main(String[] s){
    	ClassA a = new ClassA(2);
    	Immutable immutable=new Immutable(1, a);
    	ClassA a2 = immutable.getOA();
    	if(a==a2){
    		System.out.print("a==a2");
    	}else{
    		System.out.print("a2.a="+a2.getA());
    	}
    }
}




package immutable;

public class ClassA implements Cloneable{
	private int a;
	public ClassA(int i){
		a=i;
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	public ClassA clone(){
		ClassA o = null;
        try{
        	o = (ClassA)super.clone();
        }catch(CloneNotSupportedException e){
        	e.printStackTrace();
        }
        	return o;
        }
}


package immutable;

final class ChildImmutable extends Immutable{
	private final int c;
	public ChildImmutable(int a, ClassA oa, int c) {
		super(a, oa);
		this.c=c;
	}
	public int getC() {
		return c;
	}
}



这可能在一般的开发中不大会用到。可以被看成是一个设计游戏。不过如果我们自己设计API供别人调用,你不得不考虑规则的严谨和你的用意可以准确的表达和限制。

下面对final做个分析。
你有两个原因能够想到用final:design 和 efficiency。 你可以再方法的参数上加上final,保证传入的参数不被改变,这种设计是我们常常推崇的。
你也有两个原因在method前加上final。第一个是保证类的子类不会重写这个方法,这个方法的行为不被改变。这是设计方面的原因。另一个在低版本jdk中利用这点可以提高performance。但是也会有消耗内存的负面影响。在jdk5以后,jvm可以自动改善performance,我们反而不鼓励利用这点来优化性能。应该交给jvm去办。
当你在class前面加上final,说明你不准备也不允许这个类将来被继承。这可能是出于安全或者本身它就不会有子类。这是design方面的原因。final class的方法因为不可能被继承也就被final了,但是属性却没有被final。


和朋友交流了一下,又有了一点新的看法。immutable的本意是想让一个类的对象被创建以后就不要再被修改。有点类似常量。只想让别的程序只读。那就是说不提供写或者修改的方法。只是提供构造函数,一次生成,永不改变。从这个角度上看,也是简化了设计。对于这样的对象,你可以放心的使用,因为你知道他就是你生成时的对象,从来没有也不会被改变。

为什么JDK要设计这么多的immutable的类呢?
因为immutable类比mutable类更加容易设计,实现和使用。而且不易出错,更加安全。
简单:因为对象一旦生成就他的值就固定了,不变了。从对象状态角度上看,它只有一种状态,没有什么状态变化和迁移。相反,对于一个可以变对象,他的状态变化可就多了,你需要有个状态迁移图才能描述清楚他可能的变化,也就是说他的状态空间可能很大。
线程安全:因为没有修改方法,所以你不用考虑同步的问题,在并发环境中也可以放心的并发read。
节省内存:既然不变对象可以自由的共享。你可以让使用者尽量使用已经存在的实例,而不要重复的创建。你可以定义这些类的一些静态的final实例属性,就像定义一个常量一样。
package client;

import immutable.ClassA;
import immutable.Immutable;

class Client{
	public static final Immutable A = Immutable.getImmutable(1, new ClassA(1));
	public static final Immutable B = Immutable.getImmutable(2, new ClassA(2));
	
}



当然你也可以为Immutable 类提供一个静态工厂,由工厂来保证不会创建重复的对象实例。
这样也就节省了内存消耗和垃圾回收负担。如果你有这样的对象,在系统中会被大量的,频繁的,并发的读。不放考虑这种模式。其实String就是一个很好的例子。
很好的构建基石:你应该比较常用到Set和Map,尤其是map的key值,你是否经常需要考虑这些值会不会在你不经意间变了呢?从而使得影响你设定这个set或map的本意。如果用Immutable对象你就不用担心了吧?这样是为什么你总喜欢用String做key值。当然你也可能想尽量避免重写equals和hashCode方法。

不要滥用这种模式
凡是必然是有利有弊。你知道immutable的对象会因为值的不同而生产新的对象,所以你要谨慎设计你的对象和使用方法。比如String,当你在循环里面连接字符串的时候就不建议你用String的+号,而是用BufferString,StringBuffer类用来表示内容可变的字符串,并提供了修改底层字符串的方法。当我们进行字符拼接时,请使用StringBuffer类而非String类,因为前者将比后者快上百倍。这也许也可以知道我们平衡我自己的设计。没准你也可以设计一个immutable类的伙伴(companion)类。

在我看来,当你想设计一种有结构的常量的时候,你可以考虑设计出一个immutable的类。
分享到:
评论
7 楼 lliiqiang 2014-09-25  
属性变量被设定为不可更改的,外界传递的对象复制一份再保存到对象属性中,返回对象类型属性信息的时候也是复制一份返回.
6 楼 zhaspe 2011-02-27  
其实关于immutable的概念和要点,楼主基本都写出来了,只是感觉写的有些抽象,让人不想看,其实楼主把要点1234的列出来就明显的多了
5 楼 yeak2001 2011-02-26  
这样就够了吗?那如果有一个其他类的对象的引用属性呢?怎么保证它不被变?

对于这种情况你可以尝试change reference object to value object
4 楼 JackyCheng2007 2011-02-25  
superheizai 写道
貌似不用final类吧,私有构造就可以使它不能被继承。


因为不提供修改对象参数的方法,如果把构造私有了,怎么生成这样一个对象呢?还要提供一个代替构造函数的一个工厂方法?
感觉语义上没有final类来的直接。
3 楼 superheizai 2011-02-25  
貌似不用final类吧,私有构造就可以使它不能被继承。
2 楼 tsxm 2011-02-25  
可惜讲的太少了,就得calendar好像也是不可变类
1 楼 fastwei 2011-02-24  
都说的什么东东,标题党呀。

相关推荐

    kotlinx.collections.immutable, Kotlin的不可变集合 Prototype.zip

    kotlinx.collections.immutable, Kotlin的不可变集合 Prototype Kotlin的不可变集合库 Kotlin的不可变集合接口和实现 Prototype 。有关详细信息,请参阅建议列表。Prototype实现基于 pcollections ( 版权 2015的作者...

    ImmutableObjects

    Java标准库中包含了许多内置的不可变类,这些类提供了不可变性的保证,从而提高了代码的稳定性和安全性。以下是一些常见的不可变类: - **String**: `String` 类是不可变的,这意味着一旦一个字符串对象被创建,它...

    immutableclass:项目移至可编程元组

    不可变类用于使用户定义的类的实例不可变的 Python 元类 该模块提供了一个元类,用于使用户定义的类的实例不可变。 它的基本功能以为模型,但它提供了更多的面向对象和可编程性。 基本上,这里不可变类的实例一旦...

    immutable-angular:在 Angular 中迭代和观察 immutable-js 集合

    不可变的角度 支持在 Angular 1.x 中查看和枚举集合 入门 使用 npm 或 jspm 安装immutable-angular npm install immutable-angular jspm install npm:immutable-angular 包含'immutable'模块作为模块的依赖项 ...

    Java语言程序设计中文ppt第十章(对象详解)

    不可变对象(immutable object)是一种一旦创建就不能再改变它的内容的对象,而不可变类(immutable class)是指该类的所有数据都是私有的且没有修改器。例如,Circle 类如果删掉前面的 set 方法,那么该类就变成不...

    immutable-object:如何构建和使用不可变对象的示例

    在编程领域,不可变对象(Immutable Object)是一种特殊类型的对象,一旦创建,其状态就不能被修改。这种设计模式在Java等编程语言中广泛应用于提高并发性能、保证数据安全以及简化编程逻辑。本篇文章将深入探讨如何...

    java面向对象之final修饰符.docx

    这通常用于创建不可变对象,如Java集合框架中的ArrayList类的子类`Collections.unmodifiableList()`返回的列表就是final的,不能被修改。 ```java public final class ImmutableClass { private final String ...

    Java不可变对象整洁之道

    为了提高开发效率和代码质量,可以使用自动代码生成库,如Immutables和AutoValue,它们在编译期间自动生成不可变类的代码。对于Android开发,需要在Android Studio中配置APT(Annotation Processor Tool)。 ...

    python如何实现不可变字典inmutabledict

    在Python中实现不可变字典主要有四种方法:一是自定义类并重写`__setitem__`魔术方法,二是使用Python C API的`PyDictProxy_New`,三是利用`collections.frozendict`,四是通过`dataclasses.frozen()`装饰器创建一个...

    基于 Immutable.js 实现撤销重做功能的实例代码

    本文将探讨如何使用 Immutable.js 这个强大的数据不可变库来实现撤销重做功能。Immutable.js 使得状态管理更加简单,因为它确保了数据一旦创建就不会被改变,从而简化了撤销和重做的逻辑。 首先,我们需要确定哪些...

    Java String不可变性实现原理解析

    一、不可变模式(Immutable Pattern) 在并行软件开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步。而同步操作对系统性能是...

    python面试题100.docx

    对象有两种:可更改(mutable)和不可更改(immutable)。在 Python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list, dict 等则是能够修改的对象。 当一个引用传递给函数的时候,函数自动复制一份引用...

    详解Java编程中final,finalize,finally的区别

    总结起来,`final`用于声明不变量、方法和类,保证其不可变性或不可扩展性;`finalize`是为了在对象被回收前执行清理工作,但使用需谨慎;而`finally`则是异常处理的关键,确保了关键代码段的执行,特别是在资源管理...

    stati、thi、supe、final关键字详解

    final class ImmutableClass { // 不可继承的类 private int value; ImmutableClass(int value) { this.value = value; } } ``` 总结,static、this、super和final是Java编程中非常重要的关键字。static用于...

    CustomImmutableClassExample_2

    在Java编程中,"CustomImmutableClassExample_2"很可能是某个示例项目或者教程的名称,专注于自定义不可变类(Custom Immutable Class)的设计与实现。不可变类是一种一旦创建就不能改变其状态的类,它们提供了许多...

    react-reacthooksreduximmutablejs仿网易云音乐打造精美webApp

    Immutable.js是一个JavaScript库,提供不可变数据集合。在React和Redux的世界里,使用不可变数据可以帮助避免意外的状态改变,因为一旦创建,对象就不能被修改。这增强了代码的可预测性和调试性。在本项目中,...

    通过实例解析java String不可变性

    一、不可变模式(Immutable Pattern) 在并行软件开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步。而同步操作对系统性能是...

    关键字Final

    在Java编程语言中,`final`关键字扮演着非常重要的角色,它有多种用途,用于创建不可改变的对象、变量和类。下面将详细讲解`final`关键字的五种主要使用方式。 1. **常量(Final Variables)** `final`关键字可以...

    Java中static、this、super、final用法.doc

    `final`在设计模式中也常用于实现不可变对象,如Java的`String`类就是不可变的。例如: ```java final int MAX_VALUE = 100; // 定义一个不可更改的常量 final class Immutable { private final int value; ...

    数据类:JavaScript和TypeScript的不可变值对象

    语法糖利用了JavaScript和TypeScript中可用类型系统的功能,为定义不可变和持久性的域模型和数据传输对象的数据结构提供了轻松的方法。 现有技术 实施的概念在很大程度上受到了Scala和Kotlin的启发。 两种语言都将...

Global site tag (gtag.js) - Google Analytics