《Effective Java》阅读笔记1 考虑使用静态工厂方法替代构造方法

​记得研一刚进实验室的时候,看见师兄手里拿着一本书《Effective Java》,心理想着,这微信大佬(师兄已经被微信录取)看的书,肯定很有用,所以自己也买来一本喵喵。结果可想而知,看不懂。。。。现在自己再次拿起来看,发现有点意思了。所以把它分享出来。

书获取方式---->>>你们要的免费书来了

1.序

对于一个类,要获取它的一个实例,通常的做法是提供一个公用的构造函数,然而还有另一种方法,我们称之为静态工厂方法,实质上也就是一个简单的静态方法,它返回一个类的实例。其实,静态工厂方法获取对象实例,我们并不陌生,来看两个例子。

构造方法创建对象
在Java中,创建对象常用的方法是通过公有构造方法创建;
举个例子:如下,是Boolean类的一个构造方法,以及通过该构造方法创建一个Boolean对象;

  public Boolean(String s) {
        this(toBoolean(s));
  }
  
  Boolean bTrue = new Boolean("true");

静态工厂方法创建对象
其实,创建对象还有另外一种方法,通过公有静态工厂方法来创建对象,不过这种方法往往容易被程序员忽略;
举个例子,如下是Boolean类的valueOf方法,以及通过该静态工厂方法返回的Boolean实例,注意,这里并没有创建Boolean实例对象,而是返回事先创建好的Boolean对象;

public static Boolean valueOf(String s) {
    return toBoolean(s) ? TRUE : FALSE;
}

Boolean bTrue = Boolean.valueOf("true");

2.优势

2.1.静态工厂方法与构造器不同的第一大优势在于,它们有名称 (增强了代码的可读性)
//使用构造器方法获取到一个素数
BigInteger prime = new BigInteger(int, int ,Random);

//使用静态工厂方法
BigInteger prime = BigInteger.probablePrime(int, Random);

详细示例
假设我们需要写一个产生随即数的类RandomIntGenerator,该类有两个成员属性:最小值min和最大值max,
假设我们的需求是需要创建三种类型的RandomIntGenerator对象,
1、大于min,小于max;
2、大于min 小于Integer.MAX_VALUE;
3、大于Integer.MIN_VALUE 小于max
如果我们不使用静态工厂方法,代码一般如下设计:

class RandomIntGenerator
{
    /**
     * 最小值
     */
    private int min = Integer.MIN_VALUE;
    /**
     * 最大值
     */
    private int max = Integer.MAX_VALUE;

    /**
     * 大于min 小于max
     * @param min
     * @param max
     */
    public RandomIntGenerator(int min, int max)
    {
        this.min = min;
        this.max = max;
    }
    
    /**
     * 大于min 小于Integer.MAX_VALUE
     */
    public RandomIntGenerator(int min)
    {
        this.min = min;
    }

//    报错:Duplicate method RandomIntGenerator(int) in type RandomIntGenerator
//    /**
//     * 大于Integer.MIN_VALUE 小于max
//     */
//    public RandomIntGenerator(int max)
//    {
//        this.max = max;
//    }
}

观察以上代码,我们发现,以上代码不仅可读性差(new RandomIntGenerator(1, 10)与new RandomIntGenerator(10),不查文档,不看注释很难知道其创建的对象的具体含义),而且在设计最后一个构造方法的时候,还报错,因为已经存在一个参数一致的工作方法了,提示重复定义;
那么假设我们使用静态工厂方法会怎样呢,如下所示:

class RandomIntGenerator
{
    /**
     * 最小值
     */
    private int min = Integer.MIN_VALUE;
    /**
     * 最大值
     */
    private int max = Integer.MAX_VALUE;

    /**
     * 大于min 小于max
     * @param min
     * @param max
     */
    public RandomIntGenerator(int min, int max)
    {
        this.min = min;
        this.max = max;
    }
    /**
     * 大于min 小于max
     * @param min
     * @param max
     */
    public static RandomIntGenerator between(int min, int max)
    {
        return new RandomIntGenerator(min, max);
    }
    /**
     * 大于min 小于Integer.MAX_VALUE
     */
    public static RandomIntGenerator biggerThan(int min)
    {
        return new RandomIntGenerator(min, Integer.MAX_VALUE);
    }

    /**
     * 大于Integer.MIN_VALUE 小于max
     */
    public static RandomIntGenerator smallerThan(int max)
    {
        return new RandomIntGenerator(Integer.MIN_VALUE, max);
    }
}

成功满足需求:创建三种类型的RandomIntGenerator对象,而且创建对象的时候,代码可读性比使用构造方法强

2.2.不必在每次调用它们的时候都创建一个新对象(提升性能)

通过构造方法创建对象的时候,每次都要new一个新对象。而通过静态方法,可以选择每次采用单例模式来复用对象,这对于一个性能要求高的系统,可以显著提高运行速度。

详细解析:【offerMe–设计模式必备】—单例模式

 * 静态内部类,使用双重校验锁,线程安全【推荐】
     */
    public static class Singleton7 {
        private volatile static Singleton7 instance = null;
        private Singleton7() {
        }
        public static Singleton7 getInstance() {
            if (instance == null) {
                synchronized (Singleton7.class) {
                    if (instance == null) {
                        instance = new Singleton7();
                    }
                }
            }
            return instance;
        }
}
2.3.静态工厂方法可以返回原返回类型的任何子类型对象(增大程序灵活性,减少API数量)

这样使我们在选择返回对象的类时就有了更大的灵活性。

class Father {
    private Father() {
    }

    public static Father newInstance(String type) {
        if (type.equals("ChildA")) { // 根据类型判断返回那个子类对象
            return new ChildA();
        } else {
            return new ChildB();
        }
    }

    public void getName() { 
        System.out.println("My name is father");
    }

    private static class ChildA extends Father {
        public void getName() { 
            System.out.println("My name is child A");
        }
    }

    private static class ChildB extends Father {
        public void getName() {
            System.out.println("My name is child B");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Father c1 = Father.newInstance("ChildA");
        c1.getName();
        Father c2 = Father.newInstance("ChildB");
        c2.getName();
    }
}
2.4.可以使代码变得更加简洁(具体类型取决于传入的参数)

2.4.1 示例1

package tmp;

class MyMap<K,V> {
    /**
     *
     */
    public MyMap()
    {
    }

    public static <K,V> MyMap<K,V> newInstance(){
        return new MyMap<K, V>();
    }
}

public class Main
{
    public static void main(String[] args)
    {
        MyMap<String, String> map1 = new MyMap<String, String>();

        //更加简洁,不需要重复指明类型参数,可以自行推导出来
        MyMap<String, String> map2 = MyMap.newInstance();
    }
}

但实际上,现在Java最新的版本已经构造函数已经不需要补充参数了。

HashMap<String,List<String>> map = new HashMap<>();

2.4.2 示例2

//传统写法Map<String, String> map =new HashMa<String,String>();

//用guava写法Map<String, String> map = Maps.newHashMap(); 

3.缺点

3.1.类如果不含有公有的类或者受保护的构造器,就不能被子类化

如下类,不能被其它类继承;

class MyMap<K,V> {
    /**
     *
     */
    private MyMap()
    {
    }

    public static <K,V> MyMap<K,V> newInstance(){
        return new MyMap<K, V>();

    }
}
3.2.它们与其他的静态方法实际上没什么区别,因此我们约定了一些静态工厂方法的常用名称
  • 1.valueOf —— from 和 to 更为详细的替代 方式,例如:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • 2.of —— 聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如:
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
  • 3.instance 或 getinstance —— 返回一个由其参数 (如果有的话) 描述的实例,但不能说它具有相同的值,例如:
StackWalker luke = StackWalker.getInstance(options);
  • 4.create 或 newInstance —— 与 instance 或 getInstance 类似,除此之外该方法保证每次调用返回一个新的实例,例如:
Object newArray = Array.newInstance(classObject, arrayLen);
  • 5.getType —— 与 getInstance 类似,但是在工厂方法处于不同的类中的时候使用。getType 中的 Type 是工厂方法返回的对象类型,例如:
FileStore fs = Files.getFileStore(path);
  • 6.newType —— 与 newInstance 类似,但是在工厂方法处于不同的类中的时候使用。newType中的 Type 是工厂方法返回的对象类型,例如:
BufferedReader br = Files.newBufferedReader(path);

4.补充知识点

4.1.guava将很多集合类做了静态工厂方法封装
//用guava的工具类 
List<String> list = Lists.newArrayList(); 
Set<String> set = Sets.newHashSet();
Map<String, String> map = Maps.newHashMap(); 

//而不是下面这种方式
Map<String, String> map =new HashMa<String,String>();
4.2.什么是immutable(不可变)对象
  • 在多线程操作下,是线程安全的
  • 所有不可变集合会比可变集合更有效的利用资源
  • 中途不可改变
ImmutableList<String> immutableList = ImmutableList.of("1","2","3","4");

这声明了一个不可变的List集合,List中有数据1,2,3,4。类中的 操作集合的方法(譬如add, set, sort, replace等)都被声明过期,并且抛出异常。 而没用guava之前是需要声明并且加各种包裹集合才能实现这个功能

 // add 方法  @Deprecated @Override
  public final void add(int index, E element) {
    throw new UnsupportedOperationException();
  }

当我们需要一个map中包含key为String类型,value为List类型的时候

//以前我们是这样写的
Map<String,List<Integer>> map = new HashMap<String,List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
map.put("aa", list);
System.out.println(map.get("aa"));//[1, 2]

//现在这么写
Multimap<String,Integer> map = ArrayListMultimap.create();		
map.put("aa", 1);
map.put("aa", 2);
System.out.println(map.get("aa"));  //[1, 2]
4.3.注意区分静态工厂方法和工厂方法模式

注意,这里的静态工厂方法与设计模式里的工厂方法模式不是一个概念:

静态工厂方法 通常指的是某个类里的静态方法,通过调用该静态方法可以得到属于该类的一个实例;
工厂方法模式 是一种设计模式,指的是让具体的工厂对象负责生产具体的产品对象,这里涉及多种工厂(类),多种对象(类),如内存工厂生产内存对象,CPU工厂生产CPU对象;

不过,如果要说相似的话,静态工厂方法跟简单工厂模式倒有那么点像,不过区别也挺大,简单工厂模式里的静态工厂方法会创建各种不同的对象(不同类的实例),而静态工厂方法一般只创建属于该类的一个实例(包括子类);

总之,静态工厂方法和公共构造方法都有它们的用途,并且了解它们的相对优点是值得的。通常,静态工厂更可取,因此避免在没有考虑静态工厂的情况下直接选择使用公共构造方法。

5.参考文献

https://zhuanlan.zhihu.com/p/90837015
https://www.cnblogs.com/chenpi/p/5981084.html

在这里插入图片描述

本公众号分享自己从程序员小白到经历春招秋招斩获10几个offer的面试笔试经验,其中包括【Java】、【操作系统】、【计算机网络】、【设计模式】、【数据结构与算法】、【大厂面经】、【数据库】期待你加入!!!

1.计算机网络----三次握手四次挥手
2.梦想成真-----项目自我介绍
3.你们要的设计模式来了
4.一字一句教你面试“个人简介”
5.接近30场面试分享
6.你们要的免费书来了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haikuotiankongdong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值