如何高效的创建对象

简介: 不禁思考,以后怎样避免这样的问题。难道以后想创建某些对象,都得点进去看下源码,看下构造器是如何处理的?当然没必要,我们其实可以有更好的方案——静态工厂方法。

引子

事情是这样的,今天写了单元测试下一个hsf服务,查一下最近一个月的数据

@Test
public void testServiceList(){
    ActivityDTO dto = new ActivityDTO();
    activity.setStartDate(new Date(2022, 3, 1, 0, 0, 0));
    activity.setEndDate(new Date(2022, 3, 31 0, 0, 0));
    // 最近一个月的数据肯定是有的,结果发现什么都没查到...
    List<Activity> list = activityService.list(dto);
}

没多久就定位问题了,打开了Date的构造方法查看源码,不经吐槽:

我以为构造出的年月日,你却做了这么多处理,往后推迟了1900年1个月!怪不得落得“过时”的下场。

@Deprecated
public Date(int year, int month, int date, int hrs, int min, int sec) {
    int y = year + 1900;
    // month is 0-based. So we have to normalize month to support Long.MAX_VALUE.
    if (month >= 12) {
        y += month / 12;
        month %= 12;
    } else if (month < 0) {
        y += CalendarUtils.floorDivide(month, 12);
        month = CalendarUtils.mod(month, 12);
    }
    BaseCalendar cal = getCalendarSystem(y);
    cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.getDefaultRef());
    cdate.setNormalizedDate(y, month + 1, date).setTimeOfDay(hrs, min, sec, 0);
    getTimeImpl();
    cdate = null;
}

不禁思考,以后怎样避免这样的问题。

难道以后想创建某些对象,都得点进去看下源码,看下构造器是如何处理的?

当然没必要,我们其实可以有更好的方案——静态工厂方法。

下面我们就通过几个典型例子认识下它的优点:

静态工厂方法

1. 方法名称即可表达创建对象的含义
LocalDateTime now = LocalDateTime.now();
LocalDateTime localDateTime = LocalDateTime.of(2022, 3, 1, 0, 0, 0);

// 构造器表意不明
new Date();
new Date(2022, 3, 1, 0, 0, 0);
2. 不一定要创建新的对象
// 当值在low,high 之间时,如Integer.valueOf(1)是没有创建新的Integer对象,而是取的缓存
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}
3. 可以返回原类型的任何子类型对象
// 构造方法只能返回确切的自身类型,而静态工厂方法则能够更加灵活,可以根据需要方便地返回任何它的子类型的实例
// (Collections)SingletonList是私有内部类,extends AbstractList<E>
public static <T> List<T> singletonList(T o) {
     return new SingletonList<>(o);
}
// EmptyList是私有内部类,extends AbstractList<E>
public static final <T> List<T> emptyList() {
     return (List<T>) EMPTY_LIST;
}
4. 减少对外暴露不必要的属性
public class Company {
    public static final int TYPE_TECHNOLOGY = 1;
    public static final int TYPE_INTELLIGENT = 2;
    public static final int TYPE_INTERNATIONAL = 3;
    int type;

    /**
     * 限制外部创建非定义对象
     * 如:new Company(4)
     */
    private Company(int type) {
        this.type = type;
    }

    public static Company newTechnology() {
        return new Company(TYPE_TECHNOLOGY);
    }

    public static Company newIntelligent () {
        return new Company(TYPE_INTELLIGENT);
    }

    public static Company newInternational() {
        return new Company(TYPE_INTERNATIONAL);
    }
}

简而言之,创建对象优先使用工厂方法,这个可以减少调用者出错的机率;

类的提供者对入口的改造和构造方法的限制,可以让更加有效的控制类。

正如《Effective Java》建议:考虑使用静态工厂方法代替构造器

相关文章
|
Python
面向对象编程:核心概念与实践
面向对象编程:核心概念与实践
284 0
|
存储 数据处理 流计算
流模式vs批模式:你选对了吗?
本文由阿里云 Flink 团队刘文聪老师在撰写。文章分析了 Flink 的流批模式在不同维度存在的特点与差异,帮助开发者朋友们更好地理解 Flink 的流批模式。
1085 12
流模式vs批模式:你选对了吗?
|
供应链
领导供应链团队实现高绩效的4种策略
领导供应链团队实现高绩效的4种策略
|
搜索推荐 算法
【C/排序算法】:堆排序和选择排序
【C/排序算法】:堆排序和选择排序
268 0
|
前端开发 开发者 微服务
前端技术-es6 语法-对象拓展运算符 | 学习笔记
简介:快速学习前端技术-es6 语法-对象拓展运算符
193 0
|
运维 云计算
【ACE Meetup天津站】云计算时代的运维管理
此次Meetup 中,主讲嘉宾于梦洋老师就云计算、大数据运维、分布式集群、自动化运维管理等多方面,与在场成员进行了详细介绍和交流。
1494 0
|
消息中间件 数据格式 JSON
RabbitMQ之TTL(Time-To-Live 过期时间)
1. 概述 RabbitMQ可以对消息和队列设置TTL. 目前有两种方法可以设置。第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间。第二种方法是对消息进行单独设置,每条消息TTL可以不同。
2674 0
|
XML 数据格式 程序员
|
4天前
|
缓存 人工智能 安全
GPT-5.6 Terra与GPT-5.5性能实测:成本减半后的跑分对比与快速迁移指南
GPT-5.6 Terra 的定价为每百万 token 输入 2.50/输出 15。GPT-5.5 则是 5/ 30。Terra 的每一项费率,包括 $0.25/M 的缓存读取,都恰好是 GPT-5.5 的一半,因此在任何工作负载组合下,Terra 都固定 便宜 2.0x。以每天 10 万次请求、3K token 提示词计算,大约是 Terra 每天 2,000,GPT−5.5每天 4,000,即每月约 60,000对 120,000。问题在于:OpenAI 没有发布任何针对 Terra 的编码基准。那个著名的 91.9% Terminal-Bench 数字是 Sol 在 Ul