Java 的静态工厂方法以及相比于构造器的优势

本文转载自:https://www.jianshu.com/p/ceb5ec8f1174

  • 序:什么是静态工厂方法

    • 2.1 静态工厂方法与构造器不同的第一优势在于,它们有名字
    • 2.2 第二个优势,不用每次被调用时都创建新对象
    • 2.3 第三个优势,可以返回原返回类型的子类
    • 2.4 第四个优势,在创建带泛型的实例时,能使代码变得简洁

 

1. 序:什么是静态工厂方法

在 Java 中,获得一个类实例最简单的方法就是使用 new 关键字,通过构造函数来实现对象的创建。
就像这样:

    Fragment fragment = new MyFragment();
    // or
    Date date = new Date();

不过在实际的开发中,我们经常还会见到另外一种获取类实例的方法:

    Fragment fragment = MyFragment.newIntance();
    // or 
    Calendar calendar = Calendar.getInstance();
    // or 
    Integer number = Integer.valueOf("3");

↑ 像这样的:不通过 new,而是用一个静态方法来对外提供自身实例的方法,即为我们所说的静态工厂方法(Static factory method)

知识点:new 究竟做了什么?

简单来说:当我们使用 new 来构造一个新的类实例时,其实是告诉了 JVM 我需要一个新的实例。JVM 就会自动在内存中开辟一片空间,然后调用构造函数来初始化成员变量,最终把引用返回给调用方。

2. Effective Java

在关于 Java 中书籍中,《Effective Java》绝对是最负盛名几本的之一,在此书中,作者总结了几十条改善 Java 程序设计的金玉良言。其中开篇第一条就是『考虑使用静态工厂方法代替构造器』,关于其原因,作者总结了 4 条(第二版),我们先来逐个看一下。

2.1 静态工厂方法与构造器不同的第一优势在于,它们有名字

由于语言的特性,Java 的构造函数都是跟类名一样的。这导致的一个问题是构造函数的名称不够灵活,经常不能准确地描述返回值,在有多个重载的构造函数时尤甚,如果参数类型、数目又比较相似的话,那更是很容易出错。

比如,如下的一段代码 :

Date date0 = new Date();
Date date1 = new Date(0L);
Date date2 = new Date("0");
Date date3 = new Date(1,2,1);
Date date4 = new Date(1,2,1,1,1);
Date date5 = new Date(1,2,1,1,1,1);

—— Date 类有很多重载函数,对于开发者来说,假如不是特别熟悉的话,恐怕是需要犹豫一下,才能找到合适的构造函数的。而对于其他的代码阅读者来说,估计更是需要查看文档,才能明白每个参数的含义了。

(当然,Date 类在目前的 Java 版本中,只保留了一个无参和一个有参的构造函数,其他的都已经标记为 @Deprecated 了)

而如果使用静态工厂方法,就可以给方法起更多有意义的名字,比如前面的 valueOfnewInstancegetInstance 等,对于代码的编写和阅读都能够更清晰。

2.2 第二个优势,不用每次被调用时都创建新对象

这个很容易理解了,有时候外部调用者只需要拿到一个实例,而不关心是否是新的实例;又或者我们想对外提供一个单例时 —— 如果使用工厂方法,就可以很容易的在内部控制,防止创建不必要的对象,减少开销。

在实际的场景中,单例的写法也大都是用静态工厂方法来实现的。

如果你想对单例有更多了解,可以看一下这里:☞《Hi,我们再来聊一聊Java的单例吧》

2.3 第三个优势,可以返回原返回类型的子类

这条不用多说,设计模式中的基本的原则之一——『里氏替换』原则,就是说子类应该能替换父类。
显然,构造方法只能返回确切的自身类型,而静态工厂方法则能够更加灵活,可以根据需要方便地返回任何它的子类型的实例。

Class Person {
    public static Person getInstance(){
        return new Person();
        // 这里可以改为 return new Player() / Cooker()
    }
}
Class Player extends Person{
}
Class Cooker extends Person{
}

比如上面这段代码,Person 类的静态工厂方法可以返回 Person 的实例,也可以根据需要返回它的子类 Player 或者 Cooker。(当然,这只是为了演示,在实际的项目中,一个类是不应该依赖于它的子类的。但如果这里的 getInstance () 方法位于其他的类中,就更具有的实际操作意义了)

2.4 第四个优势,在创建带泛型的实例时,能使代码变得简洁

这条主要是针对带泛型类的繁琐声明而说的,需要重复书写两次泛型参数:

Map<String,Date> map = new HashMap<String,Date>();

不过自从 java7 开始,这种方式已经被优化过了 —— 对于一个已知类型的变量进行赋值时,由于泛型参数是可以被推导出,所以可以在创建实例时省略掉泛型参数。

Map<String,Date> map = new HashMap<>();

所以这个问题实际上已经不存在了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值