zoukankan      html  css  js  c++  java
  • 《Effective Java》阅读笔记之对象创建及销毁

    写在前面

    本文及后面的几篇笔记都是读《Effective Java》的随笔。内容里面有书里面重点的部分,所以大家别见怪就好。如果各位发现啥问题,希望能不吝赐教,虽然是自己的笔记,也希望能得到大家的指教。

    关于对象的创建,《Design patterns》里面有五种设计模式支撑,不能说设计模式就是最终导向,只能说是特定情况下的全局考量。那么在《Effective Java》(以下用EJ表示)里面的处理方式,可以跟这些设计模式相得益彰,因为GOF讲的是大局,而Bloch讲的是实践。必要时,两个方面都会谈谈。

    一、静态工厂方法

    优点:

    1.可以有更能说明具体作用的方法名(与构造函数相比)

       从维护代码的角度讲,这的确是个优点。至少可以提供更加简洁明了的api支持。但是,如果一个类已经能够说明清楚用途,大可不必因为这个原因来做此重构。

    2.不必每次都创建一个新对象(与构造函数相比)

       从接触java开始,我们就不停地在利用构造函数创建对象,new来new去,再加上jvm的垃圾回收机制,使得我们java团员们根本不会考虑一味的对象创建会对内存和效率产生多大影响。通过静态工厂方法,我们可以重复使用一些对象。这些对象的特点:常用属性不变(field或method)。

       在java的api里面,就有:

       public static Boolean valueOf(boolean b) {

            return (b ? TRUE : FALSE);

        }

       这就可以重复使用public static final Boolean TRUE = new Boolean(true);或者public static final Boolean FALSE = new Boolean(false);

       在这种情况下,我们的代码里面应该都是领域(用有业务含义的词来说明)的语言表达。结合第一点,我们可以让我们的代码变得更加简洁有效。

    3.可以返回子类的对象(这点明显优于构造函数,但是否有这个必要,还需考虑)

       一般情况下,我想可以不考虑这个实现。但是考虑到EJ提到的服务提供框架,这的确是非常高级的一种处理。

      在此也大体说明一下这种service provider framework!在EJ中对这种框架的组成部分有比较详细的说明(不禁感叹学习的重要性了)。这种框架主要包括三个接口:服务接口(各种服务提供类必须实现的统一服务接口);服务注册接口(便于各个具体服务提供实现,注册到框架中);服务访问接口(这是服务使用端【或者称为客户端】需要的)。

      例子的话,也就是JDBC里面的:Connection是服务接口,DriverManager.registerDriver是服务注册接口,DriverManager.getConnection是服务访问接口。

    4.降低冗长的泛型定义(在定义一些集合类时,的确比较繁琐,尤其是我们的泛型比较复杂时)

      对于这点,我也模仿EJ里面的方式,写了几个方法:

      public static final <T> ArrayList<T> newArrayList(int initialCapacity){

            return new ArrayList<T>(initialCapacity);

        }

      在使用的时候,真的少些很多type reference(就是泛型的类型定义)。

    缺点:

    1.如果提供了静态工厂方法,那么这个类就应该禁用构造函数创建,同时也就不能被继承。

    2.无法区分静态工厂方法跟其他静态方法。

    二、用构造器替换参数较多的构造函数

    这种情况时候存在,比方说:

    class Person {

     public Person(int id,int age,int weight,int hight){

      this.id = id;

      this.age = age;

      this.weight = weight;

      this.hight = hight;

     }

    //nothing more ....

    }

    在这个类里面我们需要四个变量,这还是少的。即便少,有时也会眼迷一下,搞错位置,就出问题了:Person p = new Person(123344,23,76,178);哪个可怜鬼要是碰到了这个类,算是比较幸运的。再来四个变量,就疯了。一般在这种情况下,考虑通过setter的方式来避免同一类型的参数错误:

    class Person {

      public Person(){}

      public void setId(int id){this.id = id;}

      //其他几个变量都有对应的setter方法

    }

    创建对象:

    Person p = new Person();

    p.setId(123344);

    p.setAge(23);

    ...

    尽管这种方式比构造函数的参数的情况好很多,但还是要写大片的代码。所以,就在此引入builder。

    接下来是EJ里面的实现方式:

    class Person{

        private int id;

        private int age;

        private int weight;

    ....

     private Persion(Builder builder){

        this.id = builder.id;

        this.age = builder.age;

        ......

     }  

     public static class Builder{

        private int id;

        private int age;

        private int weight;

    ....

      public Builder(int id){this.id = id;} 

      public Builder age(int age){this.age = age; return this;}//这个效果的确比较方便,能出现面条式的赋值方式

      public Builder weight(int weight){this.weight = weight;return this;}

      .......

      public Person build(){

          return new Person(this);

      }

     }

    }

    各位看到这里,可能也会跟我一样,会有中看到懒婆娘的裹脚啦。但是这种方式在创建对象时,效果还是相当可观的:Person p = new  Person.Builder(123344).age(23).weight(76).build();比前面好了很多。当然我也在想,EJ的作者为啥非要在Person里面再创建一个builder,而不是直接在Person里面用

    这种赋值方式呢?毕竟可以减少,变量的复制,因为在builder里面有跟Person一样的域列表。个人看法,EJ作者是想把责任明确,如果是Person里面就需要一个用于创建实例对象的方法,但是那样的话,责任就不是很明确了,就是对象自己创建自己,还不如使用构造函数算了。

    上面的这种方式,的确很值得借鉴,个人觉得适用的情况要明确。

    三、强制属性单例

    首先,单例在某些情景中是违反设计模式的,比如比较重要的资源:IO,数据库链接等。凡事不可一概而论,单例也有很多优点,可以重用对象。我们该如何加入单例属性呢?

    第一点要注意的就是构造函数应该是private的。当然,用单例属性时不一定非要使得宿主类是不可被构造的,这种情况仅仅是保证宿主类就要是单例的。这个问题在单例模式(GOF)中有详细说明。这个单例属性应该是静态的,而且最好是在定义时初始化,否则,就要考虑double-check问题。还是直接来一个类说明一下比较好:

    class Person{

      private static final Person INSTANCE = new Person();

      public static Person getInstance(){ return INSTANCE;}//静态工厂方法

    }

    这种方式中,通过静态的final变量定义方式,可以保证其永远都是同一个对象。

    在序列化的时候,因为static和transient变量不会被写到流中。所以,经过序列化之后,总是会使得静态变量再次创建。那么我们所谓的单例也就不能保证了。

    但是通过enum可以避免在序列化(从参考资料中,查看enum的序列化处理)中像static那样的负面影响,可以比较保险的实现单例。

    public enum PersonEnum{

       INSTANCE;

       public void doAction(){...}

    }

    四、如何实现非实例化的对象创建及避免多余的对象创建?

    通过private构造函数就可以避免不能实例化的对象创建了,当然EJ中也分析了抽象类的一些缺点(引导用户去继承呀等等)。

    而避免多余的对象创建,其例子是String s = new String("test");这里就创建了两个string对象。那么对于其他的一些情况,我们要再深入些:

    1.java的autobox功能。尽量避免这种转换,以减少对象创建的频率。

    2.仔细分析方法调用,区分变化和非变化部分。例如:一个方法调用中,总是会创建一些固定的对象,可以考虑让这些个对象的创建过程在实例或者类层次上完成。以避免在多次的方法调用过程中重复对象创建。

    五、避免过时的对象引用及finalizer

    一般的变量赋值可能不会出现过时对象引用问题。在用到引用数组或者缓存等数据管理时,要时刻关注这种引用,是否会造成对象引用过时。对于数组(集合也算在其中)情况,可以在使用完之后,赋值成null。而在缓存中,可以使用weakreference的方式来处理。

    对于finalize,个人觉得就是记住:一定不要相信它来做任何资源处理。当然,如果你用了,而且说这东西很好,我只能说,那是个奇迹。

    六、参考资料

    java序列化的高级认识 http://www.ibm.com/developerworks/cn/java/j-lo-serial/

    三种enum的序列化http://www.vineetmanohar.com/2010/01/3-ways-to-serialize-java-enums/

    weakreference相关的 http://www.cnblogs.com/ericchen/archive/2011/05/27/2059060.html

  • 相关阅读:
    实现 RSA 算法之改进和优化(第三章)(老物)
    实现 RSA 算法之 C 语言实现(第二章)(老物)
    实现 RSA 算法之基础公式证明(第一章)(老物)
    mapreducer计算原理
    Job的运行过程
    HDFS之JAVAAPI
    HDFS文件操作
    HdfS体系结构
    hdfs(分布式文件系统)优缺点
    hadoop的单机配置
  • 原文地址:https://www.cnblogs.com/ericchen/p/2133975.html
Copyright © 2011-2022 走看看