现在的位置: 首页 > 综合 > 正文

Java 静态工厂方法

2013年09月27日 ⁄ 综合 ⁄ 共 5241字 ⁄ 字号 评论关闭

静态工厂方法讲解

创建类的实例的最常见的方式是用new语句调用类的构造方法。在这种情况下,程序可以创建类的任意多个实例,每执行一条new语句,都会导致Java虚拟机的堆区中产生一个新的对象。假如类需要进一步封装创建自身实例的细节,并且控制自身实例的数目,那么可以提供静态工厂方法。

        例如Class实例是Java虚拟机在加载一个类时自动创建的,程序无法用new语句创建java.lang.Class类的实例,因为Class类没有提供public类型的构造方法。为了使程序能获得代表某个类的Class实例,在Class类中提供了静态工厂方法forName(String name),它的使用方式如下:

 Class c=Class.forName("Sample"); //返回代表Sample类的实例

下面我们可以看一下java.lang.Class的源码:

public final
    class Class<T> implements java.io.Serializable, 
			      java.lang.reflect.GenericDeclaration, 
			      java.lang.reflect.Type,
                              java.lang.reflect.AnnotatedElement {
//……
	
	/*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.  
	 * 在这里可以看到:Class类的构造方法是私有的,所以我们无法通过New的方式获取一个Class实例
     */
    private Class() {}
//……							  
}

但是Class提供有静态工厂方法forName(String name):

//……
	/** Called after security checks have been made. */
	//本地forName方法
    private static native Class forName0(String name, boolean initialize,
					    ClassLoader loader)
	throws ClassNotFoundException;
	//带有一个参数的静态方法
	public static Class<?> forName(String className) 
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());
    }


	// 有三个参数的静态方法
	public static Class<?> forName(String name, boolean initialize,
				   ClassLoader loader)
        throws ClassNotFoundException
    {
	if (loader == null) {
	    SecurityManager sm = System.getSecurityManager();
	    if (sm != null) {
		ClassLoader ccl = ClassLoader.getCallerClassLoader();
		if (ccl != null) {
		    sm.checkPermission(
			SecurityConstants.GET_CLASSLOADER_PERMISSION);
		}
	    }
	}
	return forName0(name, initialize, loader);
    }
	//本地forName方法
    /** Called after security checks have been made. */
    private static native Class forName0(String name, boolean initialize,
					    ClassLoader loader)
	throws ClassNotFoundException;

//……

静态工厂方法与用new语句调用的构造方法相比,有以下区别。

        (1)构造方法的名字必须与类名相同。这一特性的优点是符合Java语言的规范,缺点是类的所有重载的构造方法的名字都相同,不能从名字上区分每个重载方法,容易引起混淆。静态工厂方法的方法名可以是任意的,这一特性的优点是可以提高程序代码的可读性,在方法名中能体现与实例有关的信息。例如Gender类有两个静态工厂方法:getFemale()和getMale()。

Gender.java:

public class Gender {
	private String description;
	private static final Gender female = new Gender("女");
	private static final Gender male = new Gender("男");

	private Gender(String description) {
		this.description = description;
	}

	public static Gender getFemale() {
		return female;
	}

	public static Gender getMale() {
		return male;
	}

	public String getDescription() {
		return description;
	}
}

这一特性的缺点是与其他的静态方法没有明显的区别,使用户难以识别类中到底哪些静态方法专门负责返回类的实例。为了减少这一缺点带来的负面影响,可以在为静态工厂方法命名时尽量遵守约定俗成的规范,当然这不是必需的。目前比较流行的规范是把静态工厂方法命名为valueOf或者getInstance。

   valueOf:该方法返回的实例与它的参数具有同样的值,例如:

  Integer a=Integer.valueOf(100); //返回取值为100的Integer对象

从上面代码可以看出,valueOf()方法能执行类型转换操作,在本例中,把int类型的基本数据转换为Integer对象。

getInstance:返回的实例与参数匹配,例如:

 //返回符合中国标准的日历

   Calendar cal=Calendar.getInstance(Locale.CHINA);

(2)每次执行new语句时,都会创建一个新的对象。而静态工厂方法每次被调用的时候,是否会创建一个新的对象完全取决于方法的实现。

(3)new语句只能创建当前类的实例,而静态工厂方法可以返回当前类的子类的实例,这一特性可以在创建松耦合的系统接口时发挥作用。

静态工厂方法最主要的特点是:每次被调用的时候,不一定要创建一个新的对象。利用这一特点,静态工厂方法可用来创建以下类的实例。

        单例类:只有惟一的实例的类。

        枚举类:实例的数量有限的类。

        具有实例缓存的类:能把已经创建的实例暂且存放在缓存中的类。

        具有实例缓存的不可变类:不可变类的实例一旦创建,其属性值就不会被改变。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cgwshxs/archive/2008/12/05/3455136.aspx

使用静态工厂方法主要有以下优点:

  第一:静态工厂方法可以突破构造函数不能自由命名的限制,对于不同的工厂方法可以采用不同的会意的名字,是程序具有更好的可读性。JAVA平台库的java.text.Format的子类NumberFormat就有getInstance() , getPrecentInstance() , getCurrencyInstatnce()等静态方法,通过不同的名字产生特定的对象。

java.text.NumberFormat 部分源码:

    public final static NumberFormat getIntegerInstance() {
        return getInstance(Locale.getDefault(), INTEGERSTYLE);
    }

    /**
     * Returns an integer number format for the specified locale. The
     * returned number format is configured to round floating point numbers
     * to the nearest integer using half-even rounding (see {@link
     * java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}) for formatting,
     * and to parse only the integer part of an input string (see {@link
     * #isParseIntegerOnly isParseIntegerOnly}).
     *
     * @see #getRoundingMode()
     * @return a number format for integer values
     * @since 1.4
     */
    public static NumberFormat getIntegerInstance(Locale inLocale) {
        return getInstance(inLocale, INTEGERSTYLE);
    }

    /**
     * Returns a currency format for the current default locale.
     */
    public final static NumberFormat getCurrencyInstance() {
        return getInstance(Locale.getDefault(), CURRENCYSTYLE);
    }

 第二:静态工厂方法是用来产生对象用的,至于产生什么类型的对象没有限制,这就意味这只要返回原返回类型或原返回类型的子类型都可以,这样就加大了程序设计和使用的灵活行,如java.util集合框架就采用了这种优势,这样就可以更好达到封装的目的,降低API的数目和用户的使用难度,java.util.Connections是集合框架的辅助类用于对原有的集合类进行进一步封装,产生一些同步的集合,不可修改的视图。都是采用静态工厂方法实现的,至于方法内部的实现类就完全别封装了。也迫使我们使用接口编程。 

java.util.Collections:

/*
 * ……
 */


public static <T> Set<T> singleton(T o) {
	return new SingletonSet<T>(o);
}

public static <T> List<T> singletonList(T o) {
	return new SingletonList<T>(o);
}

public static <K,V> Map<K,V> singletonMap(K key, V value) {
	return new SingletonMap<K,V>(key, value);
}

/*
 * ……
 */

  第三:静态工厂方法所创建的对象可以在编译时不存在,动态创建对象,采用反射,类似SPRING的IOC容器反转。最典型的例子就是spi服务提供者框架,Service Provider Iframe 是一种用于在运行时刻产生对象的框架,达到对象的创建与使用分离,是对象的客户和对象之间解耦,增加程序的灵活性和可扩展性。既然spi可以动态创建对象,那么采用什么机制来创建什么对象,创建对象的依据是什么了,spi必须一种统一的注册机制,对于要创建的对象,应该在XML文件中配置,到时候,只要提供一个字符串,就可以凭借该字符串来创建配置的对象。简单实现如下:

class SPITest {
	private static Map SPIMap = new HashMap();
	private void initSPIMapIfNeccury() {
		if (SPIMap == null) {
			SPIMap = new HashMap();
		}
		// initMap user the sepecify XML
		// the map key is a beanName ,the value is a Object which config by XML
	}
	public Object getInstace(String beanName) {
		Object result = null;
		try {
			if (SPIMap.containsKey(beanName)) {
				result = SPIMap.get(beanName);
			} else {
				throw new Exception(
						" please config the xml file ,this bean is not exists!");
			}
		} catch (Exception exce) {
			exce.printStackTrace();
		}
		return result;
	}
}

可以明显看出待创建的对象具体创建哪个对象,在编译时并不知道,只有在运行时刻才知道,将对象的创建工作推迟到运行时,这既是优点,又是缺点,失去了编译检查的功能。

参考:http://blog.csdn.net/mingyunduoshou/article/details/6149758

【上篇】
【下篇】

抱歉!评论已关闭.