公有的静态方法,只是一个返回类实例的静态方法。
静态工厂方法的优势:
有名称,如果构造器本身没有正确的描述被返回的对象,具有适当名称的静态工厂方法会更加适用。由于一个类仅能提供一个构造器,程序员可能会调用错误的构造器。用静态方法代替构造器,可以避免构造器的错误使用。
不必每次都返回新的对象,直接返回已经创建好的对象就行,适合单例模式、不可实例化、不可变的类,这样可以确保不生成两个相等的实例。
(有点没理解好)可以返回原返回类型的任何子类型的对象。 服务提供者框架(Service Provider Framework) 多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现(通过重写服务类中的那个关键函数)。感觉上好像就是一个多态的使用而已,还是没理解到位? 下面是一个实现的例子: 这个例子大概就是,有好多service provider,它们可以提供不同的service。最一般的想法就是,一个provider类里面弄一个doService的方法,但这么做显然不太好,会有许多重复的代码。于是需要一个Service接口,里面至少要有一个doService方法,其他Provider再具体生成的时候都需要去继承这个接口来实现它们自己的doService方法。此外还需要有个Services的类来提供一些工具函数(API)让provider对Service的调用过程变得更方便。Services里面基本全是一些静态方法。比如生成新的Provider实例(这里就用到了上面提到的用静态方法代替构造器),根据已有的Provider实例名来返回实例,把一个新生成的实例注册到Service的管理器中(用一个map 保存实例名字字符串和实例对象的映射关系)
源码如下: package chapter2_serviceprovider; public interface Service { //some methos to do public void doService(); }
package chapter2_serviceprovider; public interface Provider { Service newService(); } package chapter2_serviceprovider; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; // 1 provide a map to store the relationship of name and provider instance // 2 provide a registration api to excute the puts method of map // 3 provide Service access api to return the instance of provider from map public class Services { private Services(){} //providers is a map to store the //relation of the service name and the provider private static final Map<String,Provider>providers= new ConcurrentHashMap<String,Provider>(); public static final String DEFAULT_PROVIDER_NAME="<def>"; //provider registraion api public static void registerDefaultProvider(Provider p){ registerProvider(DEFAULT_PROVIDER_NAME,p); } public static void registerProvider(String name,Provider p){ providers.put(name, p); } //Service access api public static Service newInstance(){ return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name){ Provider p=providers.get(name); if(p==null){ throw new IllegalArgumentException( "No provider registered with name:"+name); } return p.newService(); } } package chapter2_serviceprovider; public class testServiceProvider { //create different providers //every provider should realize the newService in their own way private static Provider DEFAULT_PROVIDER = new Provider() { public Service newService() { return new Service() { public void doService() { System.out.println("do the service in the default way") ; } }; } }; private static Provider SERVICE_WAYA = new Provider() { public Service newService() { return new Service() { public void doService() { System.out.println("do the service in waya") ; } }; } }; private static Provider SERVICE_WAYB = new Provider() { public Service newService() { return new Service() { public void doService() { System.out.println("do the service in wayb") ; } }; } }; public static void main(String[] args) { //regist the created Providers //namely create the maping betwwen the provider and its key name Services.registerDefaultProvider(DEFAULT_PROVIDER); Services.registerProvider("servicea",SERVICE_WAYA); Services.registerProvider("serviceb",SERVICE_WAYB); // create the newinstance Service s1=Services.newInstance(); Service s2=Services.newInstance("servicea"); Service s3=Services.newInstance("serviceb"); //invoke the service s1.doService(); s2.doService(); s3.doService(); } }创建参数化实例的时候,可以更加简洁,这里的例子用的是集合中的例子 如果想要通过泛型创建一个Map比如 Map<String,List> m=new HashMap<String,List>() 这样可能显得很繁琐,尤其是使用迭代器循环的时候,可能里里外外要好多层。 如果HashMap提供一个类似的静态工厂方法(貌似目前还没有类似的方法) Public static<K,V>HashMap<K,V>newinstance(){ return new HashMap<K,V>() }
这样直接通过HashMap.newinstance()来生成新的实例,会方便许多。 在自己编写类似的工具类的时候,可以借鉴相关的思路,来简化程序 缺点一 光看有点是不对的,如果该类不含有共有的或者受保护的构造器,就不能被实例化。比如说Collections类,它里面全都是static方法和私有的构造器,因此不能被实例化。鼓励使用复合的继承的方式对其进行操作
缺点二 与其他静态方法实际上没有什么本质的区别。 总结 静态方法和共有构造器各有用处,静态方法通常情况下更合适。因此我们的第一反应应该是使用静态工厂,而非是使用共有的构造器。
这部分主要介绍的是Builder模式
具体内容:
下面是一个实际的例子
package chapter2_buildermodel; //using builder to create the instance of NutritionFacts public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; //initialize the parameters by a builder private NutritionFacts(Builder builder){ servingSize=builder.servingSize; servings=builder.servings; calories=builder.calories; fat=builder.fat; sodium=builder.sodium; carbohydrate=builder.carbohydrate; } public void info(){ System.out.println("the servingSize is "+servingSize); System.out.println("the servings is "+servings); System.out.println("the calories is "+calories); System.out.println("the fat is "+fat); System.out.println("the sodium is "+sodium); System.out.println("the carbohydrate is "+carbohydrate); } public static class Builder{ //required parameters private final int servingSize; private final int servings; //optionl parameters (shuld provide a default value) private int calories=0; private int fat=0; private int carbohydrate=0; private int sodium=0; //every Builder method return this builder instance public Builder(int servingSize,int servings){ this.servingSize=servingSize; this.servings=servings; } public Builder calories(int val){ calories=val; return this; } public Builder fat(int val){ fat=val; return this; } public Builder carbohydrate(int val){ carbohydrate=val; return this; } public Builder sodium(int val){ sodium=val; return this; } //return this initialized instance by the build method at last public NutritionFacts build(){ //invoke the private constructor of NutritionFacts //this constructor use a builder instance to initaialze the parameter return new NutritionFacts(this); } } public static void main(String[] args) { //create the NutritionFacts instance by the Builder model NutritionFacts cocaCola=new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build(); cocaCola.info(); } }这样处理起来,比那种使用多个构造方法的形式确实灵活好多,但是还是比较麻烦。在一些类似的脚本语言中,可能对于类似问题的处理会好一些,比如早python中,直接在函数声明的时候就能指定默认的参数。传参的时候也能用显式的方式指定到底是给哪个变量传递的。
总结 如果类的构造器或者静态工厂方法有多个参数,或者以后可能会拓展出多个参数出来,Builder模式是很推荐的选择,对参数的检验也变得更加灵活,更易于编写维护。 当然缺点就是代码可能会显得冗长,只有在多参数的时候才适合使用。
书上提供的另外两种方式是设置多个构造器,或者是使用JavaBean的方式,具体参考书上内容。
单例模式(singleton)似乎是必须要掌握的一个设计模式了,好多时候只需要生成一个单独的实例,具体的可以参考之前整理的这个(http://www.cnblogs.com/Goden/p/3956803.html)
再补充一点,因为反射方式的存在,可以通过反射的方式调用私有的构造器生成新的实例,于是为了防止有人利用这一点恶意破坏程度,可以再构造函数中添加保护机制,如果实例被第二次创建的时候,就会抛出一个异常。
还有就是单例的序列化的问题
之前那个是比较常见的一个单例实现,通过枚举的方式实现单例,看起来似乎更加简洁,并且单元素的枚举类型已成为实现singleton的最佳方法。(枚举这里相关内容具体参考第6章) 枚举就是单例的泛型化,这个说的也是很好,应该加强理解。在一个枚举类中,定义的每个instance都是单例的。
有些类是不希望被实例化的,比如某些工具类,如java.lang.Math,或者java.util.Arrays,再或者java.util.Collections等等,对于这种类型的类,通常会提供给一个私有的构造器,来防止这个类被实例化。通常在这种私有的构造器上加一个注释,就是明确的指出,这个类不希望被实例化,比如// supperss default constructor for noninstamtiability
这个比较容易理解,就是在方法中尽量减少 Object o=new Object()这种语句,这样的话,每次方法被调用都会创建一个新的对象出来,到底是效率低了好多。 根据前面的几条,能使用静态方法的,最好还是使用静态方法。 下面这个例子,是好方法和差方法的对比: package chapter2_objectReuse;
import java.util.Calendar; import java.util.Date; import java.util.TimeZone; //check if the birth date betwwen baby boomer public class Personold { private final Date birthDate=null; //other fields, methods, and constructor omitted //Bad Way (the Calendar instance is created every time when the method is invoked): public boolean isBabyBoomer(){ Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY,1,0,0,0); Date boomStart=gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY,1,0,0,0); Date boomEnd=gmtCal.getTime(); return birthDate.compareTo(boomStart)>=0&&birthDate.compareTo(boomEnd)<0; } } package chapter2_objectReuse; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; public class Personnew { private final Date birthDate=null; //other fields, methods, and constructor omitted //appoint the start time and the end time private static final Date BOOM_START=null; private static final Date BOOM_END=null; // a better way to initialize the parameter static{ Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY,1,0,0,0); Date BOOM_START=gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY,1,0,0,0); Date BOOM_END=gmtCal.getTime(); } public boolean isBabyBoomer(){ return birthDate.compareTo(BOOM_START)>=0&&birthDate.compareTo(BOOM_END)<0; } }第一个例子中,每次都要新生成一个Calemdar类,影响效率,后面的一个实现中,通过static块的方式,将参数都初始化好存了下来,这样比较好。
后面还说了适配器的情形(有点没看懂)还有自动装箱的情况,优先使用基本类型而不是自动装箱类型,比如计算long的时候写成了Long,这样的话,实例就会被自动装箱,生成许多不必要的实例,影响开销。
现在有点晕了,后面又说,这种避免对象创建的策略也不是绝对的,小对象的创建和回收都是比较廉价的,如果通过创建附加的对象能够提升程序的清晰性,这也是推荐的,看来还是具体情况具体分析了。
这一个tip主要是讨论一个内存泄露的问题。 内存泄露可以由过期引用引起,所谓过期引用就是指永远不会再被接触的引用。 比如下面的这个例子。 package chapter2_overduereference;
import java.util.Arrays; import java.util.EmptyStackException; public class Stack { private Object[]elements; private int size=0; private static final int DEFAULT_INITIAL_CAPACITY=16; public Stack(){ elements=new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e){ ensureCapacity(); elements[size++]=e; } public Object pop(){ if(size==0) throw new EmptyStackException(); return elements[--size]; } //ensure space for one more elements //roughly doubling the capacity each time the array need grow private void ensureCapacity(){ if(elements.length==size) elements=Arrays.copyOf(elements, 2*size+1); } }看起来代码也没什么错误,关键是在pop函数的地方,如果先入栈好多,再出栈,那么先前的引用就没有被释放掉,因为对于垃圾回收器来说,前面已经不用的数组区域和后面正在使用的数组区域是没有区别的,因此要用手工的方式使得不用的引用指向null才能保证被垃圾回收期器回收(与垃圾回收器的机制有关?)。这主要是因为stack是自己管理内存的。
好的做法应该是这样: public Object pop(){ if(size==0) throw new EmptyStackException(); Object result= elements[--size]; elements[size]=null; return result; } 事实上在官方的Stack实现中,也是这样做的: 在pop方法中,调用了一个removeElementAt方法来去掉指定位置的元素,在这个方法的最后,也把去掉的那个元素的引用赋成了null。
还有两种内存泄露的来源,由于没有实例,理解的不是太好。 缓存 监听器和其他回调
内存泄露一般不容易被发现,应该提前就预测到内存泄露可能会发生的地方。
这个tip还是蛮重要的,算是有一点亲身体会。特别是使用finally来关闭连接状态的时候,无论是socket或者是是数据库的连接,并且还是那种多个线程的环境下。 这个后面介绍的部分还是有点没看懂。
除非是作为安群网,或者是为了终止非关键的本地资源,否则请不熬使用终结方法。在一些很少见的情况下,既然使用了终结方法,就要牢记调用super.finalize方法。(这一部分不太了解)如果用终结方法作为安全网,要记得记录终结方法的非法用法。最后,如果需要把终结方法与共有的非final类关联起来,请考虑使用终结方法的守卫者,以确保即使使子类的终结方法未能调用super.finalize该终结方法也会被执行。
转载于:https://www.cnblogs.com/Goden/p/4258993.html