zoukankan      html  css  js  c++  java
  • 如何写一个不可变类

    写在前面

    开局是这样的, 在看到springmvc Controller默认是单例的时候, 会引发成员变量竞争访问的问题, 于是有了解决方案123, 其中有一个方案是在Controller中使用threadLocal变量, 如何使用threadLocal变量? 查资料的过程中有一篇这样的文章:不要使用全局变量, ThreadLocal也不行 于是有了这篇东拼西凑的博文.看到最后发现貌似在Controller上添加@Scope("prototype")注解是最简单粗暴的方案.

    什么是不可变类

    不可变类的意思是创建该类的实例后,该实例的属性是不可改变的;所以不可变类并不是指该类是被final修饰的,而是指该类的属性是被final修饰的。

    自定义不可变类遵守如下原则:

    1> 使用private和final修饰符来修饰该类的属性。

    2> 提供带参数的构造器,用于根据传入的参数来初始化属性。

    3> 仅为该类属性提供getter方法,不要提供setter方法。并且使用copy-on-write原则,创建私有的copy

    4> 如果有必要,重写hashCode和equals方法,同时应保证两个用equals方法判断为相等的对象,其hashCode也应相等

    5> 通常构造对象时,成员变量使用深度拷贝来初始化,而不是直接赋值,这是一种防御措施,因为你无法确定输入变量不被其他人修改

    Ps:

    List<String> immutableList = List.of(“不可变的list”);  immutableList.add(“allen”);会抛异常

    最近看到一个不错的

    https://blog.csdn.net/Ithinkthereforiam/article/details/60781771

    https://www.cnblogs.com/Shevo/p/8414649.html

    https://www.jianshu.com/p/07955d0e4a1c

    不可变类有什么优点

    • 方便构造、测试和使用
    • 线程安全,没有同步问题
    • 不需要拷贝构造方法
    • 不需要实现Clone方法
    • 可以缓存类的返回值,允许hashCode使用惰性初始化方式
    • 不需要防御式复制
    • 适合用作Map的key和Set的元素(因为集合里这些对象的状态不能改变)
    • 类一旦构造完成就是不变式,不需要再次检查
    • 总是“failure atomicity”(原子性失败):如果一个不可变对象抛出异常,它从不会保留一个烦人的或者不确定的状态
    • 易于构造,测试和使用
    • 天然线程安全,没有同步问题
    • 不需要实现clone方法
    • 引用不可变类的实例时,不需要考虑实例的值发生变化的情况

    如何创建不可变类

    要创建不可变类,要实现下面几个步骤:

    1. 将类声明为final,所以它不能被继承
    2. 将所有的成员声明为私有的,这样就不允许直接访问这些成员
    3. 对变量不要提供setter方法
    4. 将所有可变的成员声明为final,这样只能对它们赋值一次
    5. 通过构造器初始化所有成员,进行深拷贝(deep copy)
    6. 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝

    为了理解第5和第6条,我将使用FinalClassExample来阐明。

    FinalClassExample.java

    package com.journaldev.java;
     
    import java.util.HashMap;
    import java.util.Iterator;
     
    public final class FinalClassExample {
     
        private final int id;
     
        private final String name;
     
        private final HashMap testMap;
     
        public int getId() {
            return id;
        }
     
        public String getName() {
            return name;
        }
     
        /**
         * 可变对象的访问方法
         */
        public HashMap getTestMap() {
            //return testMap;
            return (HashMap) testMap.clone();
        }
     
        /**
         * 实现深拷贝(deep copy)的构造器
         * @param i
         * @param n
         * @param hm
         */
     
        public FinalClassExample(int i, String n, HashMap hm){
            System.out.println("Performing Deep Copy for Object initialization");
            this.id=i;
            this.name=n;
            HashMap tempMap=new HashMap();
            String key;
            Iterator it = hm.keySet().iterator();
            while(it.hasNext()){
                key=it.next();
                tempMap.put(key, hm.get(key));
            }
            this.testMap=tempMap;
        }
     
        /**
         * 实现浅拷贝(shallow copy)的构造器
         * @param i
         * @param n
         * @param hm
         */
        /**
        public FinalClassExample(int i, String n, HashMap hm){
            System.out.println("Performing Shallow Copy for Object initialization");
            this.id=i;
            this.name=n;
            this.testMap=hm;
        }
        */
     
        /**
         * 测试浅拷贝的结果
         * 为了创建不可变类,要使用深拷贝
         * @param args
         */
        public static void main(String[] args) {
            HashMap h1 = new HashMap();
            h1.put("1", "first");
            h1.put("2", "second");
     
            String s = "original";
     
            int i=10;
     
            FinalClassExample ce = new FinalClassExample(i,s,h1);
     
            //Lets see whether its copy by field or reference
            System.out.println(s==ce.getName());
            System.out.println(h1 == ce.getTestMap());
            //print the ce values
            System.out.println("ce id:"+ce.getId());
            System.out.println("ce name:"+ce.getName());
            System.out.println("ce testMap:"+ce.getTestMap());
            //change the local variable values
            i=20;
            s="modified";
            h1.put("3", "third");
            //print the values again
            System.out.println("ce id after local variable change:"+ce.getId());
            System.out.println("ce name after local variable change:"+ce.getName());
            System.out.println("ce testMap after local variable change:"+ce.getTestMap());
     
            HashMap hmTest = ce.getTestMap();
            hmTest.put("4", "new");
     
            System.out.println("ce testMap after changing variable from accessor methods:"+ce.getTestMap());
     
        }
     
    }

    输出

    Performing Deep Copy for Object initialization
    true
    false
    ce id:10
    ce name:original
    ce testMap:{2=second, 1=first}
    ce id after local variable change:10
    ce name after local variable change:original
    ce testMap after local variable change:{2=second, 1=first}
    ce testMap after changing variable from accessor methods:{2=second, 1=first}

    现在我们注释掉深拷贝的构造器,取消对浅拷贝构造器的注释。也对getTestMap()方法中的返回语句取消注释,返回实际的对象引用。然后再一次执行代码。

    Performing Shallow Copy for Object initialization
    true
    true
    ce id:10
    ce name:original
    ce testMap:{2=second, 1=first}
    ce id after local variable change:10
    ce name after local variable change:original
    ce testMap after local variable change:{3=third, 2=second, 1=first}
    ce testMap after changing variable from accessor methods:{3=third, 2=second, 1=first, 4=new}

    从输出可以看出,HashMap的值被更改了,因为构造器实现的是浅拷贝,而且在getter方法中返回的是原本对象的引用。

    感谢

  • 相关阅读:
    JavaScript 深度克隆 JSON 对象
    NetBeans IDE 6.7.1 with JavaFX Now Available for Download!
    NetBeans 时事通讯(刊号 # 65 Jul 21, 2009)
    来自雨林木风的Linux发行版: Ylmf Linux
    JavaScript 深度克隆 JSON 对象
    STL vector vs list function comparison:
    手把手教你把Vim改装成一个IDE编程环境(图文)
    Using Visual Leak Detector
    疯狂的编程世界_IT新闻_博客园
    分享好段子:
  • 原文地址:https://www.cnblogs.com/yadongliang/p/13050293.html
Copyright © 2011-2022 走看看