【问题标题】:Does every static factory methods internally uses new keyword to create object?每个静态工厂方法在内部都使用 new 关键字来创建对象吗?
【发布时间】:2017-08-18 15:53:49
【问题描述】:

我检查了以下常用的静态工厂方法,每个方法内部都使用 new 关键字来创建对象

  1. valueOf()

     public static String More ...valueOf(char c) {
         char data[] = {c};
         return new String(data, true);
     }
    
     public static String More ...valueOf(int i) {
         return Integer.toString(i);
     }
    
  2. toString() 返回return new String(buf, true);

    来源:java.lang.String.java

第二部分相关来源:getInstance()

public static Calendar More ...getInstance()
{
    Calendar cal = createCalendar(TimeZone.getDefaultRef(), 
    Locale.getDefault(Locale.Category.FORMAT));
    cal.sharedZone = true;
    return cal;
}

createCalendar 方法内部使用new 关键字创建Calendar 对象。

来源:java.util.Calendar.java

有没有静态工厂方法,内部不使用new关键字创建对象?

【问题讨论】:

  • Long.valueOf 有时会使用池中预先创建的对象
  • 创建新对象的唯一方法是使用new(或反射)。但是,工厂可能不会自己创建对象,而是使用缓存或预先创建的对象。
  • Static 和 new 在功能上不相关,因此它们很可能确实存在,或者您可以自己创建它们

标签: java methods static


【解决方案1】:

简短的回答是(在 Java 中)创建对象的唯一方法是 new。

在某个地方,工厂方法必须返回使用new 构造的对象。

有一些像System.out 这样的全局对象(原则上)可以由运行时环境以其他方式实例化。

这并不意味着工厂方法会一对一地调用 new 并对其进行调用。 例如,Integer.valueof(int v) 需要缓存至少在 [-128,127] 范围内的值。 因此,在第一次调用 Integer.valueof(0) 之前或期间应该调用(或实际上调用)new,但后续调用将返回缓存的对象。

见https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#valueOf(int)

【讨论】:

    【解决方案2】:

    你有 3 种方法:

    1) 在编译时知道要实例化的类型的工厂使用new 运算符。
    这是最好的方法,因为它是直接的,并且编译器还强制执行对象创建的有效性。

    2) 在某些特定情况下,要实例化的类型在编译时可能是未知的。
    在这种情况下,反射是不可避免的。
    它依赖Class.forName() 来检索类和Class.newInstance() 方法来实例化类。

    例如查找java.net.InetAddress 类。
    它有一个以这种方式工作的loadImpl() 工厂方法。
    您可以看到Class.forName() 和Class.newInstance() 是可能引发“技术”异常的方法,这些异常只会在运行时被发现。

    static InetAddressImpl loadImpl(String implName) {
        Object impl = null;
    
        /*
         * Property "impl.prefix" will be prepended to the classname
         * of the implementation object we instantiate, to which we
         * delegate the real work (like native methods).  This
         * property can vary across implementations of the java.
         * classes.  The default is an empty String "".
         */
        String prefix = AccessController.doPrivileged(
                      new GetPropertyAction("impl.prefix", ""));
        try {
            impl = Class.forName("java.net." + prefix + implName).newInstance();
        } catch (ClassNotFoundException e) {
            System.err.println("Class not found: java.net." + prefix +
                               implName + ":\ncheck impl.prefix property " +
                               "in your properties file.");
        } catch (InstantiationException e) {
            System.err.println("Could not instantiate: java.net." + prefix +
                               implName + ":\ncheck impl.prefix property " +
                               "in your properties file.");
        } catch (IllegalAccessException e) {
            System.err.println("Cannot access class: java.net." + prefix +
                               implName + ":\ncheck impl.prefix property " +
                               "in your properties file.");
        }
    
        if (impl == null) {
            try {
                impl = Class.forName(implName).newInstance();
            } catch (Exception e) {
                throw new Error("System property impl.prefix incorrect");
            }
        }
    
        return (InetAddressImpl) impl;
    }
    

    3) 有时,使用new 或newInstance() 创建的对象可能会被缓存以避免多次创建同一个对象。
    这对不可变对象可能有意义。

    【讨论】:

      猜你喜欢
      • 2015-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      • 1970-01-01
      • 2010-10-17
      • 1970-01-01
      • 2011-06-04
      相关资源
      最近更新 更多