以此篇开始记录EffectiveJava的读书笔记,对于一门编程语言需要了解其核心语言:是算法式的、函数式的、还是面向对象的。需要了解其词汇:标准类库提供了哪些数据结构、功能。还需要知道如何以符合习惯且高效的方式来组织你的代码。本书聚焦于最后一点,对于绝大多数编程教科书来说只关注前面两点。如何写出易于维护、拓展,高效的代码,是阅读本系列的核心观念。
用静态工厂方法代替构造器
《Effective Java》条目一的核心结论是:优先使用静态工厂方法替代构造器,因为它在命名、灵活性、性能等方面具备显著优势,但也存在无法被子类化、不易被发现等局限。
优点一:与构造器不同,它们有名称。
场景 :创建一个Phone类,需支持 "普通手机" 和 "折叠屏手机" 两种实例。
            
            
              java
              
              
            
          
          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。
            
            
              java
              
              
            
          
          public class Color {
    private final String name;
    // 缓存池:存储已创建的基础颜色实例
    private static final Map<String, Color> 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子类实例。
            
            
              java
              
              
            
          
          // 父类:抽象形状
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),动态返回不同的实现类。
            
            
              java
              
              
            
          
          // 父接口:图片读取器
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,且仅通过静态工厂方法创建实例,子类无法调用父类构造器,导致无法继承。
            
            
              java
              
              
            
          
          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类同时有静态工厂方法和普通静态方法,开发者可能忽略创建实例的方法。
            
            
              java
              
              
            
          
          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 个优势:
- 有明确名称 :能通过方法名直接表达创建对象的意图,比如
BigInteger.probablePrime(100)比new BigInteger(100)更清晰,一看就知道是创建 "可能为素数" 的实例。 - 不必每次调用都创建新对象 :可以提前创建实例并缓存,重复返回,节省资源。典型例子是
Boolean.valueOf(boolean),它只返回Boolean.TRUE和Boolean.FALSE两个静态常量,从不新建对象。 - 可以返回原类型的任何子类型对象 :让代码更灵活,比如
java.util.Collections的静态方法(如emptyList())返回的是隐藏的私有子类实例,而非List接口的直接实现,用户无需关心具体实现类。 - 参数化创建更简洁 :当创建参数化类型对象时,无需重复声明类型。比如
Map<String, List<String>> map = HashMap.newHashMap(),比new HashMap<String, List<String>>()更简洁(Java 7 之前的泛型简化场景)。 - 可以延迟创建对象:能控制对象创建的时机,比如实现惰性初始化,在真正需要时才创建实例,提升系统启动速度。