【Effective Java】1~4条笔记整理 静态工厂、builder模式、单例、私有构造方法

724 阅读7分钟

看完《Effective Java》感觉写的相当好,中间大部分都以作为参考手册,故整理出来。结合框架源码,jdk源码,自己的demo,给大家更多的示例,便于大家理解。每一条的介绍为个人理解,如果有任何不对,希望指出。对于博客有任何建议也希望给出建议。

1.用静态工厂方法代替构造器

传统创建对象通常是使用者使用构造器来完成,第一条给了另外一种方式,就是通过静态方法,返回一个对象,该对象可以每次new一个或者反复使用同一个对象,这个完全交给开发者来决定。

举个例子

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

java.lang.Integer中的valueOf方法,在初始化的时候就创建了-128~127的Integer对象,大于这个范围的就会new一个对象。创建对外不可知,开发者可以自己进行一些优化

那么这种方式由那些优势和弊端呢。

  • 优势:

    • 有名称,更加直观,例如Integer.longValue() Integer.floatValue() Integer.doubleValue()能一目了然的看出区别
    • 可以往复利用同一个对象(上面有例子了啊)
    • 可以返回返回类型的子类型
        //Collections中的源码
        public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) {
            return new SynchronizedSortedMap<>(m);
        }
    
        static class SynchronizedSortedMap<K,V>
            extends SynchronizedMap<K,V>
            implements SortedMap<K,V>
        {
            private static final long serialVersionUID = -8798146769416483793L;
    
           ...
        }
    
    • 返回的子类型,可以随着版本的不同而不同
    //这个是书中给的例子,这个不太好找,就用书中的了
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
            Enum<?>[] universe = getUniverse(elementType);
            if (universe == null)
                throw new ClassCastException(elementType + " not an enum");
    
            if (universe.length <= 64)
                return new RegularEnumSet<>(elementType, universe);
            else
                return new JumboEnumSet<>(elementType, universe);
        }
    
    • 可以返回的对象的类可以先不存在
        //  Worker method called by the public getConnection() methods.
        private static Connection getConnection(
            String url, java.util.Properties info, Class<?> caller) throws SQLException {
            /*
             * When callerCl is null, we should check the application's
             * (which is invoking this class indirectly)
             * classloader, so that the JDBC driver class outside rt.jar
             * can be loaded from here.
             */
            ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
            synchronized(DriverManager.class) {
                // synchronize loading of the correct classloader.
                if (callerCL == null) {
                    callerCL = Thread.currentThread().getContextClassLoader();
                }
            }
    
            if(url == null) {
                throw new SQLException("The url cannot be null", "08001");
            }
    
            println("DriverManager.getConnection(\"" + url + "\")");
    
            // Walk through the loaded registeredDrivers attempting to make a connection.
            // Remember the first exception that gets raised so we can reraise it.
            SQLException reason = null;
             //在注册的驱动列表中遍历
            for(DriverInfo aDriver : registeredDrivers) {
                // 没有加载驱动的权限就跳过
                if(isDriverAllowed(aDriver.driver, callerCL)) {
                    try {
                        println("    trying " + aDriver.driver.getClass().getName());
                        Connection con = aDriver.driver.connect(url, info);
                        if (con != null) {
                            // 这里返回的con,可能是第三方的子类
                            println("getConnection returning " + aDriver.driver.getClass().getName());
                            return (con);
                        }
                    } catch (SQLException ex) {
                        if (reason == null) {
                            reason = ex;
                        }
                    }
    
                } else {
                    println("    skipping: " + aDriver.getClass().getName());
                }
    
            }
    
            // if we got here nobody could connect.
            if (reason != null)    {
                println("getConnection failed: " + reason);
                throw reason;
            }
    
            println("getConnection: no suitable driver found for "+ url);
            throw new SQLException("No suitable driver found for "+ url, "08001");
        }
    
  • 劣势

    • 没有共有/保护的构造器,就不能被继承
    • 乱取名不容易被发现及使用(要遵循统一规范,见名知意)
  • 个人demo

/**
 * 第一条:
 * 静态工厂方法
 * 优势:
 * 1.创建对象的一种方式
 * 2.有名称,更直观
 * 3.可以往复利用同一个对象
 * 4.可以返回返回类型的子类型
 * 5.返回的子类型,可以随着版本的不同而不同
 * 6.可以返回的对象的类可以先不存在
 *      eg. jdbc
 * 劣势:
 * 没有公有构造器,不能被子类化
 * 很难发现这些接口
 */
public class StaticFactoryDemo {

    private static final StaticFactoryDemo instance=new StaticFactoryDemo();

    private StaticFactoryDemo(){}

    public static void main(String[] args) {
        Boolean.valueOf("ss");
    }

    public static StaticFactoryDemo getInstance(){
        return instance;
    }

    public static StaticFactoryDemo getNewInstance(){
        return new StaticFactoryDemo();
    }

}

public static void main(String[] args) {
        StaticFactoryDemo staticFactoryDemo=StaticFactoryDemo.getNewInstance();
        System.out.println(staticFactoryDemo);
        System.out.println(staticFactoryDemo.getInstance());
    }

2.当构造方法参数过多时使用builder模式

如果一个类有许多可选参数,可有可无,有的可以用默认值,这时候就会有很多构造器,一个构造器调用另一个构造器(重叠构造器),这种会随着参数的增加而增加,感觉十分笨重。例如

public final class String
    
    public String() {
        this.value = "".value;
    }

//还有些,碍于篇幅删了
    
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    
    public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
    
    public String(byte bytes[], Charset charset) {
        this(bytes, 0, bytes.length, charset);
    }

    public String(byte bytes[], int offset, int length) {
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }
   
    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }
   
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }
    
}

除了重叠构造器的模式,还可以用builder模式,就是有一个内部类,来创建对象。

//hutool中ExecutorBuilder就用到了builder模式
public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
    private static final long serialVersionUID = 1L;
    private int corePoolSize;
    private int maxPoolSize = 2147483647;
    private long keepAliveTime;
    private BlockingQueue<Runnable> workQueue;
    private ThreadFactory threadFactory;
    private RejectedExecutionHandler handler;
    private Boolean allowCoreThreadTimeOut;

    public ExecutorBuilder() {
        this.keepAliveTime = TimeUnit.SECONDS.toNanos(60L);
    }

    public ExecutorBuilder setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
        return this;
    }

    public ExecutorBuilder setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
        return this;
    }

    public ExecutorBuilder setKeepAliveTime(long keepAliveTime, TimeUnit unit) {
        return this.setKeepAliveTime(unit.toNanos(keepAliveTime));
    }

    public ExecutorBuilder setKeepAliveTime(long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
        return this;
    }

    public ExecutorBuilder setWorkQueue(BlockingQueue<Runnable> workQueue) {
        this.workQueue = workQueue;
        return this;
    }

    public ExecutorBuilder useSynchronousQueue() {
        return this.useSynchronousQueue(false);
    }

    public ExecutorBuilder useSynchronousQueue(boolean fair) {
        return this.setWorkQueue(new SynchronousQueue(fair));
    }

    public ExecutorBuilder setThreadFactory(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        return this;
    }

    public ExecutorBuilder setHandler(RejectedExecutionHandler handler) {
        this.handler = handler;
        return this;
    }

    public ExecutorBuilder setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
        return this;
    }

    public static ExecutorBuilder create() {
        return new ExecutorBuilder();
    }

    public ThreadPoolExecutor build() {
        return build(this);
    }
    //也达到了重叠构造器的作用,设置了默认值
    private static ThreadPoolExecutor build(ExecutorBuilder builder) {
        int corePoolSize = builder.corePoolSize;
        int maxPoolSize = builder.maxPoolSize;
        long keepAliveTime = builder.keepAliveTime;
        BlockingQueue workQueue;
        if (null != builder.workQueue) {
            workQueue = builder.workQueue;
        } else {
            workQueue = (BlockingQueue)(corePoolSize <= 0 ? new SynchronousQueue() : new LinkedBlockingQueue());
        }

        ThreadFactory threadFactory = null != builder.threadFactory ? builder.threadFactory : Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = (RejectedExecutionHandler)ObjectUtil.defaultIfNull(builder.handler, new AbortPolicy());
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.NANOSECONDS, workQueue, threadFactory, handler);
        if (null != builder.allowCoreThreadTimeOut) {
            threadPoolExecutor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut);
        }

        return threadPoolExecutor;
    }
}

使用

public static void main(String[] args) {
        ExecutorBuilder.create().setCorePoolSize(5).
                setMaxPoolSize(20).
                setWorkQueue(new LinkedBlockingDeque<Runnable>(20)).
                setKeepAliveTime(20, TimeUnit.SECONDS).
                setHandler(new ThreadPoolExecutor.AbortPolicy()).
                setThreadFactory(Executors.defaultThreadFactory()).build();
    }
  • 优点
    • 明了
    • 子类可以继承重写builder
  • 缺点
    • 创建某个对象要额外创建它的构造器
  • 个人demo
/**
 * 构造器模式
 * 优点:
 * 明了
 * 子类可以继承充血builder
 * 缺点:
 * 创建某个对象要额外创建它的构造器
 */
public class ConstructorDemo {
    private final int id;
    private final int age;
    private final String name;
    private final String email;
    public static class Builder{
        //必须的参数
        private final int id;
        private final String name;
        //非必需的参数
        private  int age;
        private  String email;
        public Builder(int id,String name){
            this.id=id;
            this.name=name;
        }
        public Builder age(int age){
            this.age=age;
            return this;
        }

        public Builder email(String email){
            this.email=email;
            return this;
        }

        public ConstructorDemo build(){
            return new ConstructorDemo(this);
        }
    }
    private ConstructorDemo(Builder builder){
        this.id=builder.id;
        this.age=builder.age;
        this.name=builder.name;
        this.email=builder.email;
    }
}
  • 调用
  public static void main(String[] args) {

        ConstructorDemo constructorDemo=new ConstructorDemo.Builder(1,"hello").age(12).email("hh@qq.com").build();
    }

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

单例就是只能被实例话一次,提供唯一获取的对象的方式,可以直接访问,不需要实例化该类的对象。这样在内存中只有一个实例,减少了频繁的创建销毁的操作。实现单例模式也十分简单,static修饰,私有化构造器。根据创建方式不同,分成懒汉式和饿汉式(谁取得名字),也可以直接直接公有化成员变量。 具体如下:

  • 代码
/**
 * 单例模式
 */
public class SingletonDemo {
    //公有模式
    public static final SingletonDemo publicInstance=new SingletonDemo();

    private SingletonDemo(){}

}
/**
 * 单例模式
 */
public class SingletonDemo {

    //私有模式 饿汉式
    private static final SingletonDemo singleton=new SingletonDemo();
    private SingletonDemo(){}

    public static SingletonDemo getInstance(){
        return singleton;
    }

}

public class SingletonDemo {

    //私有模式 懒汉汉式

    private static volatile SingletonDemo singleton = null;
    private SingletonDemo(){}  
    public static SingletonDemo getInstance(){
        //双重检查
        if (singleton == null) {
            synchronized (SingletonDemo.class) {
                if (singleton == null) {
                    singleton = new SingletonDemo();
                }
            }
        }
        return singleton;
    }
}

传统的懒汉式

/**
 * 单例模式
 */
public class SingletonDemo {

    //私有模式 饿汉汉式

    private static volatile SingletonDemo singleton = null;
    private SingletonDemo(){}

    public static SingletonDemo getInstance(){
        if (singleton == null) {
             singleton = new SingletonDemo();
        }
        return singleton;
    }



}

懒汉式就是用到的时候再创建,饿汉式就是一开始就创建了。这个就是2者的区别。

  • 并发环境问题
    • 饿汉式:final可以禁止指令重拍序,所以饿汉式加上final就可以在并发环境中使用,
    • 懒汉式:为了最小化锁粒度,随意用了锁代码块,但是很多指令可能制定到第一层的if (singleton == null)后恰巧切换到别的线程,所以在锁内部又检查了一次。这就是双重检查。但是还不够,new SingletonDemo();就这个语句其实对应了3个指令
      1. 申请内存,赋值默认值
      2. 成员变量初始化
      3. 赋值给对象引用 这就有指令重拍序的风险,可能23对调,可能先获取对象,再初始化,就有风险,所以需要增加voliate保证可见性。(本人之前也整理过并发相关文章链接
  • 枚举 创建单例还有第三种方式,就是枚举
public enum SingletonEnum {
    SINGLET;
    public void doSomething() {
        System.out.println("doSomething");
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //singleton();
        singleton1();

    }

    private static void singleton1() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException  {
        SingletonEnum s=SingletonEnum.SINGLET;
        //Exception in thread "main" java.lang.NoSuchMethodException
        Constructor<SingletonEnum> constructor = SingletonEnum.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonEnum si=constructor.newInstance();
        System.out.println(si==s);

    }

    private static void singleton() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        //单例
        SingletonDemo singletonDemo=SingletonDemo.getInstance();
        Constructor<SingletonDemo> constructor = SingletonDemo.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonDemo sReflection = constructor.newInstance();
        //false
        System.out.println(sReflection==singletonDemo);
    }

}

constructor.setAccessible(true);可以调用私有构造器。枚举方式也是作者推荐的一种方式

  • 其他 网上还看到一个枚举单例的另外一种形式
public class Singleton {
}
public enum SingletonEnum {
    SINGLET;
    private Singleton instance;

    public void doSomething() {
        instance=new Singleton();
    }

    public Singleton getInstance() {
        return instance;
    }
}
public static void singleton2() {
    Singleton s=SingletonEnum.SINGLET.getInstance();
    Singleton s1=new Singleton();
    System.out.println(s==s1);
}

这种方式我觉得有歧义啊,单例就是为了不让创建第二个,这样不是脱裤子放屁么

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

如果某个类不希望被实例话,例如工具类,要显示声明一个私有的构造器,不声明构造器,编译器会自动提供一个共有的无参的构造器,例如java.util.Collections

public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    // 抑制默认构造器,确保不会被创建
    private Collections() {
    }
}