【Effective Java 条目一】– 用静态工厂方法替代构造器

2025-12-04 0 544

静态工厂方法代替构造

《Effective Java》条目一的核心结论是:优先使用静态工厂方法替代构造器,因为它在命名、灵活性、性能等方面具备显著优势,但也存在无法被子类化、不易被发现等局限。

优点一:与构造器不同,它们有名称。

场景:创建一个Phone类,需支持 “普通手机” 和 “折叠屏手机” 两种实例。

public class Phone {
    private final String type; // 手机类型:普通/折叠屏

    // 构造器:名称固定为Phone,无法区分创建逻辑
    public Phone(String type) { // 调用时new Phone(\"foldable\"),语义模糊
        this.type = type;
    }

    // 静态工厂方法:名称直接说明用途
    public static Phone createNormalPhone() { // 明确创建“普通手机”
        return new Phone(\"normal\");
    }

    public static Phone createFoldablePhone() { // 明确创建“折叠屏手机”
        return new Phone(\"foldable\");
    }
}

// 使用对比
Phone p1 = new Phone(\"foldable\"); // 不直观:不知道\"foldable\"代表什么
Phone p2 = Phone.createFoldablePhone(); // 直观:一眼懂是创建折叠屏手机

优点二:与构造器不同,不用在每次被调用时都创建一个新对象。

这种技术在享元模式下经常使用,如果程序经常请求创建相同的对象,特别是当创建对象的开销大时,利用这种技术开源提升性能。这样的类被称为实例受控的类,对于重复多次的调用,静态工厂方法可以返回同一个对象。静态工厂方法可控制对象创建,对频繁使用的实例(如单例、枚举常量)进行缓存,避免重复创建,节省内存。

场景:创建Color类,缓存 “红、绿、蓝” 三种基础颜色,避免重复 new。


public class Color {
    private final String name;
    // 缓存池:存储已创建的基础颜色实例
    private static final Map COLOR_CACHE = new ConcurrentHashMap();

    // 私有构造器:禁止外部直接new
    private Color(String name) {
        this.name = name;
    }

    // 静态工厂方法:先查缓存,无则创建并缓存
    public static Color getInstance(String name) {
        // 关键:先从缓存取,避免重复创建
        return COLOR_CACHE.computeIfAbsent(name, Color::new); 
    }

    // 测试:多次调用getInstance(\"red\"),返回同一个对象
    public static void main(String[] args) {
        Color red1 = Color.getInstance(\"red\");
        Color red2 = Color.getInstance(\"red\");
        System.out.println(red1 == red2); // true(同一对象,未重复创建)
    }
}

优点三:与构造器不同,它们可以返回所声明的返回类型的任何子类型的对象。

静态工厂方法的返回类型可以是父类,实际返回的是子类实例,灵活扩展 API(用户无需知道子类存在)。

场景Shape作为父类,静态工厂方法返回Circle/Rectangle子类实例。

// 父类:抽象形状
public abstract class Shape {
    // 静态工厂方法:返回Shape的子类实例
    public static Shape getShape(String type) {
        return switch (type) {
            case \"circle\" -> new Circle(); // 返回子类Circle
            case \"rectangle\" -> new Rectangle(); // 返回子类Rectangle
            default -> throw new IllegalArgumentException(\"未知形状\");
        };
    }

    public abstract void draw(); // 抽象方法
}

// 子类1:圆形(无需对外暴露)
class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println(\"画圆形\");
    }
}

// 子类2:矩形(无需对外暴露)
class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.println(\"画矩形\");
    }
}

// 使用:用户只依赖Shape父类,无需知道子类存在
public class Test {
    public static void main(String[] args) {
        Shape circle = Shape.getShape(\"circle\");
        circle.draw(); // 画圆形(实际是Circle子类在执行)
    }
}

优点四:在方法每次被调用时,所返回对象的类可以随输入参数的不同而改变。

静态工厂方法可根据条件动态选择返回的类(如根据版本、配置切换实现类),不影响外部调用。

场景ImageReader根据图片格式(JPG/PNG),动态返回不同的实现类。

// 父接口:图片读取器
public interface ImageReader {
    void read(String path);

    // 静态工厂方法:根据格式动态返回实现类
    public static ImageReader getReader(String format) {
        if (\"jpg\".equalsIgnoreCase(format)) {
            return new JpgImageReader(); // JPG格式返回JpgReader
        } else if (\"png\".equalsIgnoreCase(format)) {
            return new PngImageReader(); // PNG格式返回PngReader
        }
        throw new IllegalArgumentException(\"不支持的格式\");
    }
}

// JPG实现类
class JpgImageReader implements ImageReader {
    @Override
    public void read(String path) {
        System.out.println(\"读取JPG图片:\" + path);
    }
}

// PNG实现类
class PngImageReader implements ImageReader {
    @Override
    public void read(String path) {
        System.out.println(\"读取PNG图片:\" + path);
    }
}

// 使用:用户无需关心具体实现类,按格式调用即可
public class Test {
    public static void main(String[] args) {
        ImageReader jpgReader = ImageReader.getReader(\"jpg\");
        jpgReader.read(\"photo.jpg\"); // 读取JPG图片:photo.jpg
    }
}

缺点一:类如果不含公有的构造器,就不能被子类化(无法继承)

若类的构造器是private,且仅通过静态工厂方法创建实例,子类无法调用父类构造器,导致无法继承。

public class Utils {
    // 私有构造器:禁止外部new,也禁止子类调用
    private Utils() {}

    // 静态工厂方法:创建Utils实例
    public static Utils getInstance() {
        return new Utils();
    }
}

// 错误:无法继承Utils(父类无公有构造器,子类无法初始化)
class MyUtils extends Utils { 
    // 编译报错:Implicit super constructor Utils() is not visible for default constructor.
}

缺点二:类如果不含公有的构造器,就不能被子类化(无法继承)

静态工厂方法(如getInstance/of)和普通静态方法(如Math.abs)在语法上完全一致,开发者可能找不到创建实例的入口(构造器有new关键字提示,静态工厂没有)。

场景User类同时有静态工厂方法和普通静态方法,开发者可能忽略创建实例的方法。

public class User {
    private String name;

    private User(String name) {
        this.name = name;
    }

    // 静态工厂方法:用于创建User实例(但和普通静态方法无语法区别)
    public static User of(String name) {
        return new User(name);
    }

    // 普通静态方法:用于处理用户名(和of()语法完全一致)
    public static String formatName(String name) {
        return name.trim().toUpperCase();
    }
}

// 使用问题:开发者可能只看到formatName(),忽略of()是创建实例的方法
public class Test {
    public static void main(String[] args) {
        String upperName = User.formatName(\"zhangsan\"); // 容易发现
        User user = User.of(\"zhangsan\"); // 若不知道of()是工厂方法,可能找不到创建入口
    }
}

总结、静态工厂方法的核心优势

相比传统的new构造器,静态工厂方法主要有 5 个优势:

  1. 有明确名称:能通过方法名直接表达创建对象的意图,比如BigInteger.probablePrime(100)new BigInteger(100)更清晰,一看就知道是创建 “可能为素数” 的实例。
  2. 不必每次调用都创建新对象:可以提前创建实例并缓存,重复返回,节省资源。典型例子是Boolean.valueOf(boolean),它只返回Boolean.TRUEBoolean.FALSE两个静态常量,从不新建对象。
  3. 可以返回原类型的任何子类型对象:让代码更灵活,比如java.util.Collections的静态方法(如emptyList())返回的是隐藏的私有子类实例,而非List接口的直接实现,用户无需关心具体实现类。
  4. 参数化创建更简洁:当创建参数化类型对象时,无需重复声明类型。比如Map<String, List> map = HashMap.newHashMap(),比new HashMap<String, List>()更简洁(Java 7 之前的泛型简化场景)。
  5. 可以延迟创建对象:能控制对象创建的时机,比如实现惰性初始化,在真正需要时才创建实例,提升系统启动速度。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 【Effective Java 条目一】– 用静态工厂方法替代构造器 https://www.zuozi.net/3508.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务