Java经典书籍《Effective Java(第二版)》相信大家都看过,此书共有78条关于编写高质量Java代码的建议,这里是通俗易懂地讲解,会持续全部更新完78条,希望大家每天读一篇,一起学完这78条,相信可以写出高质量代码。今天先看第一章的1到7条。

第1条:考虐用静态工厂方法代替构造器

用静态工厂方法就是类提供一个公有的静态共有方法,它就是一个返回类实例的方法;如下面所示:

public static People getInstance() {
        return new People();
    }

用静态工厂方法有如下的几个优点:

1、方法有名称,代码的阅读性更好

2、不需要每次在调用时都创建一个新的对象出来,创建新的对象需要的代价很高,这样极大的提高了性能。

3、可以返回原类型的任何子类型的对象

4、在创建参数化的对象实例时,它会使代码很简洁。用书中的例子;

Map<String,List<String>> map = new HashMap<String,List<String>>()//繁琐

给集合类提供静态工厂方法后:

  public static  <K,V>   HashMap<K,V> newInstance(){
          return new HashMap<K,V>();
    }
 HashMap<String,List<String>> map = HashMap.newInstance();

第2条:遇到多个构造器参数时要考虑用构建器

前面说的静态工厂和构造器都有一个共同的局限性:不能很好的扩展到大量的可选参数。

这时可以用到Builder模式:不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造函数,得到一个builder对象,然后在builder对象上面调用类似setter的方法,来设置每个相关的可选的参数。最后调用无参的build方法来生成不可变的对象。

/**

         * 构建器模式

         * 

         */

        public class Student {

            /*必填*/

            private String name;

            private int age;

            /*选填*/

            private String sex;

            private String grade;

        

            public static class Builder {

                private String name;

                private int age;

                private String sex = "";

                private String grade = "";

        

                public Builder(String name, int age) {

                    this.name = name;

                    this.age = age;

                }

                public Builder sex(String sex) {

                    this.sex = sex;

                    return this;

                }

                public Builder grade(String grade) {

                    this.grade = grade;

                    return this;

                }

                public Student build() {

                    return new Student(this);

                }

            }

            private Student(Builder builder) {

                this.name = builder.name;

                this.age = builder.age;

                this.sex = builder.sex;

                this.grade = builder.grade;

            }

        }

上面的例子的本质就是一个内部类。

客户端代码:

       Student student = new Student.Builder("joan","mea").grade("1年级").build();

这样在很多参数时,客户端的代码实现很容易,可读性也很好,builder利用单独的方法来设置每个参数,你想要多少个可变的参数都可以,主要每个setter方法中都有一个可变的参数即可。


第3条:用私有构造器或者枚举类型强化Singleton属性

Singlteton就是单例的意思,指仅仅被实例化一次的类。单例模式有很多种写法,像饿汉模式、饱汉模式、内部类、双重检验锁、枚举等写法。利用implements serializable 可以让单例变得可以序列化,但是这里需要注意的一点是可序列化的单例,在反序列化之后就不一定还是单例了,这可以提供一个readResolve方法,让反序列化之后还是同一个实例对象。

import java.io.Serializable;

/**
 * Created by shidong.wu on 2017/9/18.
 */
public class Instance implements Serializable{

    private static final Instance obj = new Instance();

    private Instance(){
    }
    public static Instance getInstance(){
        return obj;
    }
    private Object readResolve(){
        return obj;
    }
}



第4条:通过私有构造器强化不可实例化的能力

我们都有自己的工具类,类似java.lang.Math, java.lang.Arrays 这样的,里面全是静态方法。对于这样的工具类我们不希望被实例化,然而在缺少显示构造器的情况下,编译器会自动提供一个公有的、无参的缺省构造器。因此,我们让这个类包含一个私有的构造器,他就不会被实例化了。


第5条:避免创建不必要的对象

该条目建议的是最好能重用对象,让不是每次在调用的时候在创建一个新的对象出来,这里有3点建议;

1、构造器在每次调用的时候会创建新的对象,而静态工厂则不会,优先使用静态工厂

2、合适的场景,可以使用final静态域代替局部变量,

3、要优先使用基本类型而不是装箱类型,要当心无意识的自动装箱。


第6条:清除过期的对象引用

一般java的内存回收机制会回收已经用完的对象,但是某个对象引用要是一直不被释放,则就不能被回收,长时间就会出现内存泄漏。所以过期不用的对象一定要注意及时的清除掉。一般内存泄漏常见出现下面的几种情况:

1、类的内存是自己管理的

2、把对象的引用放到缓存中,很容易被遗忘掉,么有被回收

3、监听器和其他的回调


第7条:避免使用终结方法

终结方法:finalize通常是不可预测的,一般不建议使用

有如下的缺点:

1、不能保证被及时的执行

2、非常的耗性能

但是也一样有如下合法用处

1、忘记使用显示的终止方法时,终结方法可以充当“安全网”,虽然不能保证终结方法及时的执行,但总比没有的强,这里的终止方法简单介绍一下,典型的例子就是InputStream的close方法,一般和try、finally一起用。

2、与对象本地对等体有关,本地对等体对象不是一个普通的对象,所以Java 垃圾回收器不会回收它,用终结方法执行回收任务最合适不过了。