After reading Effective Java, I feel quite well written. Most of the middle is used as a reference manual, so I put it together. Combined with the framework source code, JDK source code, their own demo, give you more examples, easy for you to understand. Each introduction for personal understanding, if there is any wrong, hope to point out. I would also like to give any suggestions for the blog.

1.Replace the constructor with static factory methods

Traditionally, objects are created by the user using a constructor. The first method provides an alternative, which is a static method that returns an object that can be new each time or used repeatedly. It is entirely up to the developer to decide.

For example

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
Copy the code
    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(a) {}}Copy the code

The valueOf method in java.lang.Integer creates an Integer ranging from -128 to 127 when initialized. Any object larger than this value will be new. The creation is unknown to the outside world, and developers can make some optimizations themselves

So what are the advantages and disadvantages of this approach?

  • Advantage:

    • There are names, more intuitive, for exampleInteger.longValue() Integer.floatValue() Integer.doubleValue()You can tell the difference at a glance
    • You can use the same object over and over again (see examples above)
    • Subtypes of the return type can be returned
        // The source code for Collections
        public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) {
            return new SynchronizedSortedMap<>(m);
        }
    Copy the code
        static class SynchronizedSortedMap<K.V>
            extends SynchronizedMap<K.V>
            implements SortedMap<K.V>
        {
            private static final long serialVersionUID = -8798146769416483793L; . }Copy the code
    • The subtype returned can vary from version to version
    // This is the example given in the book. This is not easy to find, so use the book
    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);
        }
    Copy the code
    • The classes of objects that can be returned may not exist first
        // 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;
             // Pass through the list of registered drivers
            for(DriverInfo aDriver : registeredDrivers) {
                // Do not have permission to load the driver
                if(isDriverAllowed(aDriver.driver, callerCL)) {
                    try {
                        println(" trying " + aDriver.driver.getClass().getName());
                        Connection con = aDriver.driver.connect(url, info);
                        if(con ! =null) {
                            // Return con, which may be a third party subclass
                            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");
        }
    Copy the code
  • disadvantage

    • Without a common/protected constructor, it cannot be inherited
    • Random names are not easy to be found and used (to follow the unified standards, see the meaning of the name)
  • Personal demo

/** * first: * static factory method * advantages: * 1. A way to create objects * 2. Has a name and is more intuitive * 3. Can use the same object over and over * 4. Subtypes that can be returned from the return type * 5. Subtypes that can be returned can vary from version to version * 6. JDBC * disadvantages: * No public constructor, cannot be subclassed * Difficult to find these interfaces */
public class StaticFactoryDemo {

    private static final StaticFactoryDemo instance=new StaticFactoryDemo();

    private StaticFactoryDemo(a){}

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

    public static StaticFactoryDemo getInstance(a){
        return instance;
    }

    public static StaticFactoryDemo getNewInstance(a){
        return newStaticFactoryDemo(); }}Copy the code
public static void main(String[] args) {
        StaticFactoryDemo staticFactoryDemo=StaticFactoryDemo.getNewInstance();
        System.out.println(staticFactoryDemo);
        System.out.println(staticFactoryDemo.getInstance());
    }

Copy the code

2.Use the Builder pattern when there are too many constructor parameters

If a class has many optional arguments, some optional, some default values, then there will be many constructors, one constructor calling another constructor (overlapping constructor), which will increase as the argument increases, feeling cumbersome. For example,

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

// Some have been deleted due to space
    
    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()); }}Copy the code

In addition to the overlapping constructor pattern, you can also use the Builder pattern, which has an inner class to create objects.

// The Builder mode is used in the HuTool ExecutorBuilder
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(a) {
        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(a) {
        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(a) {
        return new ExecutorBuilder();
    }

    public ThreadPoolExecutor build(a) {
        return build(this);
    }
    // Also serves as the overlapping constructor, setting the default value
    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); }returnthreadPoolExecutor; }}Copy the code

use

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();
    }
Copy the code
  • advantages
    • clear
    • Subclasses can inherit to override the Builder
  • disadvantages
    • To create an object, create its constructor
  • Personal demo
/** * Constructor pattern * advantages: * clear * subclasses can inherit the constructor * disadvantages: * creating an object requires creating an additional constructor */
public class ConstructorDemo {
    private final int id;
    private final int age;
    private final String name;
    private final String email;
    public static class Builder{
        // Required parameters
        private final int id;
        private final String name;
        // Non-required parameters
        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(a){
            return new ConstructorDemo(this); }}private ConstructorDemo(Builder builder){
        this.id=builder.id;
        this.age=builder.age;
        this.name=builder.name;
        this.email=builder.email; }}Copy the code
  • call
  public static void main(String[] args) {

        ConstructorDemo constructorDemo=new ConstructorDemo.Builder(1."hello").age(12).email("[email protected]").build();
    }
Copy the code

3.Enforce the Singleton property with a private constructor or enumeration type

A singleton is a way of obtaining an object that can only be instantiated once. It can be accessed directly without the need to instantiate the object of the class. This way, there is only one instance in memory, reducing the frequent creation and destruction operations. Implementing the singleton pattern is also very simple, static, privatizing the constructor. According to the creation method is different, it is divided into lazy han type and hungry Han type (who gets the name), and it can also be directly public member variables. The details are as follows:

  • code
/** * Singleton mode */
public class SingletonDemo {
    // Public mode
    public static final SingletonDemo publicInstance=new SingletonDemo();

    private SingletonDemo(a){}}Copy the code
/** * Singleton mode */
public class SingletonDemo {

    // Private mode
    private static final SingletonDemo singleton=new SingletonDemo();
    private SingletonDemo(a){}

    public static SingletonDemo getInstance(a){
        returnsingleton; }}Copy the code
public class SingletonDemo {

    // Private mode lazy

    private static volatile SingletonDemo singleton = null;
    private SingletonDemo(a){}  
    public static SingletonDemo getInstance(a){
        // Double check
        if (singleton == null) {
            synchronized (SingletonDemo.class) {
                if (singleton == null) {
                    singleton = newSingletonDemo(); }}}returnsingleton; }}Copy the code

The traditional lazy style

/** * Singleton mode */
public class SingletonDemo {

    // Private mode is han Han style

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

    public static SingletonDemo getInstance(a){
        if (singleton == null) {
             singleton = new SingletonDemo();
        }
        returnsingleton; }}Copy the code

The lazy form is created when it is used, and the hungry form is created from the beginning. This is the difference between the two.

  • Concurrent environment issues
    • Final: Final can prevent reordering, so final can be used in concurrent environments.
    • Lazy: Lock blocks are used arbitrarily to minimize lock granularity, but many instructions may be written down to the first levelif (singleton == null)Then it happened to switch to another thread, so it checked again inside the lock. This is the double check. But it’s not enough.new SingletonDemo();There are actually three instructions for this statement
      1. Allocate memory and assign default values
      2. Member variable initialization
      3. Assign values to object references

      There’s a risk of reordering the command, maybe 23 switches, maybe getting the object first and then initializing it, there’s a risk, so you need to increase voliate to ensure visibility. (I have sorted out related concurrent articles beforelink)

  • The enumeration

There is a third way to create singletons, which is by enumeration

public enum SingletonEnum {
    SINGLET;
    public void doSomething(a) {
        System.out.println("doSomething"); }}Copy the code
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //singleton();
        singleton1();

    }

    private static void singleton1(a) 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(a) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        / / the singleton
        SingletonDemo singletonDemo=SingletonDemo.getInstance();
        Constructor<SingletonDemo> constructor = SingletonDemo.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonDemo sReflection = constructor.newInstance();
        //falseSystem.out.println(sReflection==singletonDemo); }}Copy the code

constructor.setAccessible(true); Private constructors can be called. The enumeration method is also recommended by the author

  • other

Another form of an enumeration singleton can be seen on the Web

public class Singleton {}Copy the code
public enum SingletonEnum {
    SINGLET;
    private Singleton instance;

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

    public Singleton getInstance(a) {
        returninstance; }}Copy the code
public static void singleton2(a) {
    Singleton s=SingletonEnum.SINGLET.getInstance();
    Singleton s1=new Singleton();
    System.out.println(s==s1);
}
Copy the code

This way I feel ambiguous ah, the singleton is to not let the creation of a second, so it is not pants fart

4.Enforce non-instantiable capabilities through private constructors

If a class that does not want to be instantiated, such as a utility class, declares a private constructor and does not declare a constructor, the compiler automatically provides a common constructor that takes no arguments, such as java.util.collections

public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    // Suppress the default constructor to ensure that it is not created
    private Collections(a) {}}Copy the code