Advanced development requires efficient Java

Advanced development at least some advanced skills, right? So where do these advanced skills come from?

The first way must be reading books, what? You don't have that much time?

Fortunately, I am busy here and you have turned on the speed reading mode and followed me to learn the "Efficient Java" series.

Our first stop is here: static factory methods

1. What is a static factory method?

  • A static factory method is a way to create an object.

  • The static factory method here is not the factory pattern in the design pattern.

  • A static factory method is just an ordinary static method of a class, and its final effect is equivalent to that of a constructor.

Having said that, let me give you an example to understand.

Integer integer = Integer.valueOf("1");
Boolean aBoolean = Boolean.valueOf(true);
Calendar calendar = Calendar.getInstance();
复制代码

The valueOfand getInstanceis called a static factory method .

2. Advantages of static factory methods

So why encourage static factory methods to create objects instead of constructors?

The reasons are as follows:

  • Static factory methods have names, while constructors do not

  • Don't have to create an object every time a static factory method is called

  • Can return any subtype object of the current class

  • Reduce the chance of user error

Let's take a look at these advantages one by one:

2.1 Static factory methods have names, but constructors do not

Because the constructor itself does not exactly describe the object to be returned, and the static factory method has a method name, so it can more clearly describe the object to be returned. Especially the constructor with multiple different parameters can be a little confusing when choosing a constructor, while the static factory method is more clear.

//Date类
Date date0 = new Date();
Date date1 = new Date(2022L);
Date date2 = new Date("2022");
Date date3 = new Date(2022,3,20);
Date date4 = new Date(2022,3,20,18,30,59);

// LocalDate类
public static LocalDate now(){}
public static LocalDate ofEpochDay(long epochDay){}
public static LocalDate of(int year, Month month, int dayOfMonth) {}
复制代码

Looking at the construction methods of so many different parameters, if you don't understand Date, will you be confused? Take another look at the LocalDate class in Java8.

2.2 Don't have to create an object every time a static factory method is called

The constructor will create a new object every time it is called, and using the static factory method does not need to create a new object every time, we can create the object in advance or reuse the existing object to achieve the purpose of improving performance.

Let me give you an example:

1. The valueOfmethod , when the value is between -128 ~ 127, will not create a new Integer class, but will be taken from the cache.

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
          return IntegerCache.cache[i + (-IntegerCache.low)];
      return new Integer(i);
}
复制代码

2、Boolean 类中的 valueOf 方法,直接取出提前创建好的静态常量返回,也没有创建新的对象。

public final class Boolean{
	public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);
  	public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
}
复制代码

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

这里牵涉到最基础的多态:

  • 向上转型:多态本身就是向上转型过的过程

    使用格式:父类类型 变量名=new 子类类型();

还有就是设计模式六大原则中的<里氏替换原则>:

  • 任何使用父类的地方都能替换成子类来使用。
public class Test {
	//静态工厂方法,返回Test的子类型T1
    public static Test valueOf(){
        return new T1();
    }
    private static class T1 extends Test{}
}
复制代码

虽然可以这样用,但是不建议这样使用,因为这样不利于解耦,父类最好不要依赖于子类。

推荐的做法是:像 Collections 类一样,它像一个工具类,它提供了许多静态工厂方法,你去看会发现静态工厂方法返回的并不是确定的类型,而是List接口的一些子类,这些子类被作为私有类定义在Collections 中,我们无法直接构造这些类,但却可以通过静态工厂方法使用它。

这样做的目的是精减API的数量,对客户端来说也是一种减压。我们有时不必知道返回的类是什么,但我们可以像我们熟知的接口一样使用它。

// SingletonList 是私有的,实现List接口的类
public static <T> List<T> singletonList(T o) {
     return new SingletonList<>(o);
}
// EmptyList 是私有的,实现List接口的类
public static final <T> List<T> emptyList() {
     return (List<T>) EMPTY_LIST;
}
复制代码

2.4 减少使用者出错的概率

先看下例子:

class Run {
    public static final int RUNNING = 1;
    public static final int STOP = 2;
    protected int state;
    public Run(int state) {
        this.state = state;
    }
    public void process(){
        // if RUNNING / STOP
    }
}
复制代码

上面例子中,我只想对 RUNNING / STOP 两种状态处理,但我却无法控制使用者的行为,比如他可以这样调用:new Run(4)

这种情况怎么办呢?

class Run {
    public static final int RUNNING = 1;
    public static final int STOP = 2;
    protected int state;
    //私有构造方法外部无法调用
    private Run(int state) {
        this.state = state;
    }
    public static Run running(){
        return new Run(RUNNING);
    }
    public static Run stop(){
        return new Run(STOP);
    }
    public void process(){
        // if RUNNING / STOP
    }
}
复制代码

经过这样改造后,我们严格控制了取值范围,使用者出错的机会就大大减少了。

3. 总结

作为类的提供者,我们要尽量确保自身性能好,具有灵活性,让使用者使用起来更容易,更不容易出错。

恰恰静态工厂方法可以让我们做到这些,所有建议创建对象的时候优先考虑使用静态工厂方法。

Guess you like

Origin juejin.im/post/7077750408667463717