《Effective Java》读后感(一)

来源:互联网 发布:vscode typings文件 编辑:程序博客网 时间:2024/06/01 09:34

最近在看《Effective Java》,我觉得这本书其实不适合入门的小白看,如果有一些开发java的经验之后,再看这本书,才会受益匪浅,这里简单总结一下第一章《创建和销毁对象》的内容。
第一条:考虑用静态工厂方法代替构造器
静态工厂方法还是很常见的,比如hashMap的newInstance(),他比起常规的构造器生成实例有几个好处
1.静态工厂方法有名称,更容易区别出不同的返回对象。虽然构造器可以通过重载生成不同的对象,但是仅参数不同这一点区别显然不够清晰的区别不同的返回对象。比如BigInteger可以用probablePrime来返回一个素数,显然比用new BigInteger()构造器更容易区分。
2.静态工厂方法可以不必创建多余的对象。每次调用构造器都会创建一个新的对象,当有很多重复对象的时候,这种方式显然不够智能。静态工厂方法相当于封装了一下创建对象的过程,可以避免创建重复对象,甚至控制该类是一个singleton。
3.静态工厂方法可以返回原返回类型的任意子类型对象。这样做的好处一是可以隐藏子类实现类,我可以根据你的参数给你返回不同的子类实现,这对客户端完全是透明的。通过这种方式还可以实现服务提供者框架,只要我定义一个注册服务api,和一个访问服务的api即可让服务的提供者随意定制自己的实现类,我把所有提供者的实例都放在一个map里管理,客户端再按需求调用不同的实现类即可。
4.静态工厂方法可以让构造参数更加简洁,因为某些参数是可以推导出来的。

第二条:遇到多个构造器参数时要考虑用构建器
构造器参数过多的问题我在小米实习的时候就遇到过很多次了,当时我的解决方法是讲所有参数打包成一个bean,这样不仅会增加一个类,而且也不利于后面的人维护,javaBean还会在构造过程中处于不一致的状态。这一节讲到的构建器就是应对构造器参数过多的时候的一个很好的解决方案。
构建器是一个内部的静态类,这个类里面有很多参数的设置方法,类似于下面这种:

public class Box {    private int height;    private int width;    private int weight;    private String color;    public static class Builder{        private int height;        private int width;        private int weight;        private String color;        public Builder(int height, int width){            this.height = height;            this.width = width;        }        public Builder color(String color){            this.color = color;            return this;        }        public Builder weight(int weight){            this.weight = weight;            return this;        }        public Box build(){            return new Box(this);        }    }    private Box(Builder builder){        height = builder.height;        weight = builder.weight;        width = builder.width;        color = builder.color;    }}

因为每个参数的set方法都是返回Builder对象本身的,所以在构造对象的时候可以这么干。

Box box = new Box.Builder(30, 30).color("red").weight(20).build();

Builder模式比重载构造方法更容易阅读和编写,又比javaBean更安全,并且他很容易的就实现了参数的灵活可选。

第三条:用private构造器或者枚举类型强化单例模式
这个比较简单,讲了三种构造单例模式的,第一种是利用private构造方法在声明单例对象的时候就进行构造,因为外部不能调用他的构造方法,因此只会有一个实例。第二种和第一种类似,区别就是将单例的对象设为private,改为静态工厂方法返回这个单例对象。前两种都可能被别人利用反射进行攻击,第三种是设一个只有一个对象的枚举类,不仅可以防止反射攻击,还顺带实现了序列化,也绝对防止了多次实例化。是现在的最佳单例实现方法。

第四条:通过私有构造器强化不可实例化的能力
这个也比较简单,因为如果不写构造器,java会提供一个默认构造器,还是可以进行实例化的,所以为了保证某各类绝对不会被实例化,可以写一个private的构造器进去,外部无法访问这个构造器,也就无法进行实例了。

第五条:避免创建不必要的对象
主要通过一个例子String s = new String(“meituan”)和String s = “meituan”来说明,前者执行一次就会创建一个对象,后者无论执行多少次,都只有一个对象,不同的变量,只是指向同一个String对象的不同引用罢了,这在面试中也经常考察,除了这个以外,Integer,Long这些可以装箱拆箱的也有这个问题。

第六条:消除过期对象的引用
主要是举了一个用数组实现栈的例子,想象一下这样一种场景,刚开始的时候大量入栈,导致数组不断扩张,然后大量出栈,如果仅仅是把数组的指针往前挪的话,就会有大量过期对象(指针后面的已出栈对象),因为这些对象还存在着引用,所以不能被GC回收掉,有内存泄漏的风险。正确的方法是指针往前挪之后,把过期元素设为null。

第七条:避免使用终结方法
终结方法finalize()的特点是不一定会被执行,他经常延迟执行甚至压根就不执行,所以在终结方法中进行重要资源的回收是错误的,并且使用终结方法的代价还不低。如果确实有重要的资源需要终结,正确的方法是提供一个显示的终止方法,想FileInputStream,java.sql.Connection都有提供显示的close()方法,并且这种方法通常和try-catch结合使用。终结方法的作用要么是终止非关键资源,是在客户端忘记显示调用终止方法回收关键资源的时候,提供外层的安全网,毕竟延迟回收总比不回收要好。