为什么说静态工厂方法可能会比构造器更好?听说读完就会有一个女朋友?

2024-06-15 07:32

本文主要是介绍为什么说静态工厂方法可能会比构造器更好?听说读完就会有一个女朋友?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 什么是静态工厂方法
    • 静态工厂方法比构造器好在哪里
      • 静态工厂方法有名称而构造器没有(前者可读性更好)
      • 不必每次调用它们的时候都创建一个新对象
      • 可以返回原返回类型的任何子类对象
      • 创建泛型对象可以更加简洁
    • 静态工厂的局限之处
    • 总结

前言:相信读过《Effective Java》(第二版)的小伙伴知道我在说什么,作者在书中总结了78条黄金建议,其中建议的第一条就是:考虑用静态工厂方法替代构造器。可以说是开幕雷击,第一条我就懵X了,什么是静态工厂方法?为什么要替代构造器?是构造器不香吗?本文将根据这些疑问刨根问底,详细分析静态工厂方法的优劣,相信我,有问题欢迎评论指出,本篇文章一定不会枯燥,坚持读完哦~

在这里插入图片描述

什么是静态工厂方法

当我们想要获得一个类的实例对象的时候,恐怕一下子就通过new一个对象来获得了。那么静态工厂方法是怎么实现的呢?其实也很简单:

public static Girl newGirlFriend(boolean isBeautifulGirl) {return new Girl(isBeautifulGirl);
}

如此,我们通过类名加newGirlFriend就可以轻松地拿到对象。

所谓的静态工厂方法就是不通过new,而通过一个静态方法返回自身的实例对象

静态工厂方法比构造器好在哪里

由《Effective Java》可以得到四点静态工厂方法比构造器好的地方:
①静态工厂方法有名称而构造器没有(前者可读性更好)
②不必每次调用它们的时候都创建一个新对象
③可以返回原返回类型的任何子类对象(不局限于单一特定的类)
④创建泛型对象可以更加简洁

静态工厂方法有名称而构造器没有(前者可读性更好)

说到有名称这点,构造器是真的没法儿比:

public class GirlFriend {private boolean isBeautiful;private boolean isTall;public GirlFriend(){}public GirlFriend(boolean isBeautiful) {this.isBeautiful = isBeautiful;    }public GirlFriend(boolean isBeautiful, boolean isTall) {this.isBeautiful = isBeautiful;    this.isTall = isTall;    }
}

调用者如果不是非常熟悉这段代码,必须要看文档中有哪些重载的构造方法,稍有不慎,就会调错,毕竟构造方法都是一个名字,只有参数不同而已。但是静态工厂方法就不一样了,可以起很多能让调用者见名知意的名字:getBeautifulGirlfriend(); getTallGirl();,是不是看出差距了

另外:正是因为这一特性,所以哪怕他们有相同的参数,也不会报错,如果构造器有两个构造方法含有相同参数,必定出错,但是任性的静态工厂方法想怎么起名字就怎么起名字,完美避过了这个坑~
在这里插入图片描述

不必每次调用它们的时候都创建一个新对象

当我们需要获得类的实例对象的时候,为了性能,我们不在乎他是不是新对象,使用new必然会得到一个不同地址的对象(姑且称之为新对象),当我们使用new GirlFriend();这种方式直接创建对象,相当于对JVM说,我要一个对象,然后JVM会去根据双亲委派原则加载验证准备解析(new一个对象发生了什么?),所以开支是蛮大的,尤其在重复新建一个对象的时候。小斌哥我之前的代码就都是这种毛病,并没有使用单例模式,每个方法都new同一个对象,我去,简直是伤害了我的CPU,心疼我的旧电脑,怪不得那么快就挂了(玩笑话);使用静态工厂方法最简单的一个例子就是单例模式,我们可以确保获取多个对象的时候只需要创建一次对象并且销毁一次对象,这样就很“环保”了,对内存的开支会达到最小,这点是构造器望尘莫及的。

说到单例模式,大家会想到“饿汉式”与“懒汉式”,举个最简单的例子不考虑线程安全给大家展示一下单例模式是如何做到多次获取对象只创建并销毁一次:
饿汉式:

public class Singleton{private static Singleton singleton = new Singleton ();private Singleton (){}public static Singleton getInstance(){return singleton;
}

懒汉式:

public class Singleton{private static Singleton singleton = null;public static Singleton getInstance(){if(singleton==null){singleton = new Singleton();}return singleton;}
}

使用单例模式不管创建多少次都会返回相同的对象,所以相当于对象被多次循环利用了,提升了效率。在这里插入图片描述

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

这体现了里氏原则:任何基类可以出现的地方,子类一定可以出现。 比如有这样的UML:
在这里插入图片描述

public static Singleton getGirlFriend(){return new 女朋友();//可以返回新垣结衣()、长泽雅美()、松冈茉优()、绫濑遥()//因为都是女朋友的子类
}

这个优点有些欺负构造器了,毕竟它没有返回值。

创建泛型对象可以更加简洁

作者想表示过去泛型书写比较复杂,比如:

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

额,但是jdk1.7之后这个问题已经木有了,毕竟这本书有一定年头了(第二版参考的是jdk1.6),所以就当这条没有吧。

另外,静态工厂方法还有其他好处,比如可以隐藏实现细节,只提供用户最简单的方法入口,比如让你写一个计算器软件,用户只需要输入:3*(7/1.455)+11-12,而方法的具体实现细节是私有的,用户是看不到的,也不需要看到,否则就体现不了封装的特性了,一个是用户使用着麻烦,另外是出错的可能性更大,你暴露给用户更多的参数更多的方法,出BUG的可能性就更大。这就好比现在的手机,只有关机键、音量键,如果把手机后盖拆开暴露给你,手机被整坏的可能性必然会提升啊。。

我们熟知的工具类Collections就是一个很好的例子,他把构造器设置为私有,所以调用者不能通过new来调用类中的方法,只能通过类名加方法名去调用,并且内部需要计算的方法都设为private,调用者看不到也不需要去知道,比如sort方法和binarySearch等。

静态工厂的局限之处

凡事都是两面的,虽然静态工厂方法很好,但是它只要构造方法私有化了,就不能被实例化,也就是说,想让拒绝访问构造器的类拥有子类是不可能的(就像做了绝育手术)。第二点就是它跟普通的静态方法没啥两样,虽然我们管它叫静态工厂方法,但是它依然只是一个很简单的静态方法而已,并不像构造器,有自己的专属名称,还可以在API文档中看到他的名字。但是静态工厂方法也会有一些不成文的规定,正如我们会看到一些newInstance()、valueOf()、getInstance()等,这些往往就是静态工厂方法实现的。

总结

作者给我们这条建议,并非让我们用静态构造方法彻底替换构造器,只是希望我们在以后可以有多一种选择,直接new并不是唯一的方法,他们各有各的好,需要我们去进行合适的选择。

这篇关于为什么说静态工厂方法可能会比构造器更好?听说读完就会有一个女朋友?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062813

相关文章

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

oracle DBMS_SQL.PARSE的使用方法和示例

《oracleDBMS_SQL.PARSE的使用方法和示例》DBMS_SQL是Oracle数据库中的一个强大包,用于动态构建和执行SQL语句,DBMS_SQL.PARSE过程解析SQL语句或PL/S... 目录语法示例注意事项DBMS_SQL 是 oracle 数据库中的一个强大包,它允许动态地构建和执行