【问题标题】:Static Factory Method is creating a new object everytime its invoked静态工厂方法每次调用时都会创建一个新对象
【发布时间】:2015-06-30 01:47:18
【问题描述】:

在 Effective Java 中,它提到“与构造函数不同,静态工厂方法不需要在每次调用时都创建新对象”。

class Car{
     String color;
     Boolean spoiler;

     public Car(String s){
        color=s;
        spoiler = false;
     }

     public static Car redCar(){
        return new Car("red");
    }
  }

在主类中:

    Car c2 = Car.redCar();
    Car c3 = Car.redCar();        

c2 和 c3 是不同的对象。我没有得到“每次调用都不需要创建新对象”的上下文。

【问题讨论】:

  • 看看什么是单例模式
  • 你忘记了文章的重要部分:这允许不可变类(第 15 条)使用预先构造的实例,或者在构造实例时缓存实例,并重复分配它们以避免创建不必要的重复对象
  • 您错过了不需要部分。您的实现每次都清楚地返回新实例。

标签: java static factory


【解决方案1】:

因为你就是这样做的:

public static Car redCar(){
    return new Car("red");
}
        //  ^ here

如果你想返回相同的值,你可以这样做:

private static final Car RED_CAR = new Car("red");

public static Car redCar(){
    return RED_CAR;
}

关键是调用new Car()总是返回一个新实例。调用Car.newInstance() 意味着Car 类可以决定做什么。

例如:

private static final Map<String, Car> CARS = new HashMap<>();

public static Car newInstance(final String colour){
    return CARS.computeIfAbsent(colour, Car::new);
}

这使用Car 构造函数作为method reference 到新的Map.computeIfAbsent 方法,如果该颜色的Car 不存在于Map 中,则调用它。这是一个幼稚的(非线程安全的)缓存实现。

所以:

final Car one = Car.newInstance("red");
final Car two = Car.newInstance("red");
System.out.println(one == two) // true

【讨论】:

    【解决方案2】:

    “与构造函数不同,静态工厂方法不需要在每次调用时创建一个新对象”。这并不意味着调用静态工厂方法将必然返回相同的对象(如您的示例所示),只是它可能(与构造函数不同)。

    您可以,例如,以不同的方式实现 redCar(),以便它始终返回相同的对象:

    class Car{
         /* snipped */
    
         private static final RED = new Car("red");
    
         public static Car redCar(){
            return RED;
         }
    }
    

    【讨论】:

      【解决方案3】:

      与所有事情一样,程序完全按照您的要求执行。如果您的静态方法每次调用时都使用“new”;然后您每次都创建新对象。

      unlike constructors static factory methods are not required to create a new object each time they're invoked" 的意思是你的代码可以决定不调用 new;但例如返回一个“缓存”对象。

      含义:当你使用“新”时;你调用构造函数; Java 的语义导致创建 new 对象。没有办法阻止 had,它是硬连线到语言中的。

      但是当您使用静态方法时,定义了该方法的语义。

      【讨论】:

        【解决方案4】:

        也许汽车不是最好的例子,但考虑一个要求,即您的工厂应该只生产每种颜色的汽车。你可以这样实现它(省略不必要的属性):

        class Car {
            String color;
        
            public Car(String color) {
                this.color = color;
            }
        
            public static Car car(String color) {
                Car car = CARS.get(color);
                if (car != null) return car;
                car = new Car(color);
                CARS.put(color, car);
                return car;
            }
        
            private static final Map<String, Car> CARS = new HashMap<>();
        }
        

        查看Integer 类及其工厂方法valueOf。此外,这种工厂方法对单例很有用(尽管它们有自己的警告)。

        【讨论】:

        • cars.computeIfAbsent(color, Car::new),见my answer。此外,请遵守 Java 命名约定,编译时常量应在 CAPITAL_UNDERSCORE_CASE 中。
        【解决方案5】:

        在这里您正在创建新对象,

        return new Car("red");  
        

        静态工厂方法第一次创建对象,下次从静态工厂方法返回时返回相同的实例。

        【讨论】:

          【解决方案6】:

          Factory 的工作是创建一个对象。如果你不想暴露对象是如何创建的,你可以在 factory 下隐藏创建。

          最近我碰巧处理了一个用例,其中单例的概念是基于一些附加限制来定义的。例如,捕获 file1.txt 的所有 File 对象都是单例的(或相同的对象)。同样,捕获 file2.text 的 File 对象是单例的。但是捕获 file1.text 和 file2.text 的 File 对象是不同的。

          为此,创建一个静态全局列表,添加所谓的静态对象(例如,基于文件名)。如果您不希望将 Singleton(再次基于文件)对象添加到此列表中,请覆盖 equals。

          现在,如果有人要求工厂给您一个与您在 equals 中指定的对象匹配的对象(任何参数使两个对象相等),请搜索全局列表,如果该对象存在则返回它,否则创建一个新对象,添加它到列表中,然后返回对象。

          这个故事的寓意是,你不必从工厂退回新物品。您可以根据需要弯曲单例(如果您不需要纯单例)。并且通过使用静态工厂方法,可以调用 ClassName.factory 而无需实例化它。

          【讨论】:

            【解决方案7】:

            Bloch 描述的想法是,静态工厂可以使用实例的池或缓存,当请求时它会传递或决定其内部逻辑来创建新实例(可能会进入缓存)也)。这通常只适用于不可变对象,否则您会遇到一些难以跟踪的跨对象效果。

            【讨论】:

              【解决方案8】:

              您给出的实现不是静态工厂。您已经制作了如下课程:

              class Car{
                   String color;
                   Boolean spoiler;
                   public static final Car car = new Car("name");
              
                   public Car getInstance(){
                      return car;
                   }
                   private Car(String s){
                      color=s;
                      spoiler = false;
                   }
              
                   public static Car redCar(){
                      return new Car("red");
                  }
                }
              
              and then in main you have to call
              
              Car.getInstance();
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多