• Effective Java阅读()-item16

     

    Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

     

    16、接口优于抽象类

    作者有一个观点就是

    ²        接口是定义mixin(混合类型)的理想选择。

    mixin的定义:一个类除了实现它的基本类型”primitive type”之外,还可以实现这个mixin类型,以表明它提供可供选择的行为,也就是说它允许将可选的功能混合到一个类的基本功能中。

    作者举例Comparable就是一个mixin接口,这个接口提供的可选功能就是它的实例可以与其他的可相互比较的对象进行排序操作。

     

    基于上面这一点,接口使得我们能够构造出非层次结构的类型框架。

    什么是层次结构框架(class hierarchy?

    例如:Shapeß------Rectangleß----------Square这样一路继承下来,就是一个继承体系。

    现在如果Shape假设并没有提供可比较的基本功能,而我们要在Square中实现可比较的功能,我们就可以在Square那加入一个旁支Comparable接口,让Square去实现它即可。

    Shapeß------Rectangleß----------Square ß----Square’s sub class

                                                     |

                         Comparableß------

     

    上面蓝色部分就是一个class hierarchy,Comparble恰恰就在这个class hierarchy上建立了一个非层次化的结构。

     

    注:abstract class不能用来定义mixin,因为java不提供class的多重继承机制。

     

    ²        abstract skeletal implementation class(抽象骨架实现类型)--结合抽象类和接口的优点,java中模拟多重继承

     

    Interface缺点是无法拥有方法的实现。而抽象骨架实现的优点在于为抽象类提供实现上的帮助,也避免了抽象类作为类型定义时所特有的限制。

     

    public abstract class AbstractMapEntry implements Map.Entry {
      // Primitives
      public abstract Object getKey();
      public abstract Object getValue();
      //  ...
      // Implements the general contract of Map.Entry.equals
      public boolean equals(Object o) {
        if (o == this) return true;
        if (!o instanceOf Map.Entry))
          return false;
        Map.Entry arg = (Map.Entry) o;
        
        return eq(getKey(), arg.getKey()) &&
            eq(getValue(), arg.getValue());
      }
      
      // Since Object equals was overriden, we better override hashCode!
      public int hashCode() {
        return
          (getKey() == null ? 0: getKey().hashCode()) ^
          (getValue() == null ? 0: getValue().hashCode());
      }
    }

    书中提示:抽象骨架类专为继承而设计,所以要有详细的文档说明。

     

    ²        抽象类的演化比接口的演化容易

    我们举例说明这一点吧

    Considering the following code:

    //未演化前的代码:

    public abstract class AbrBase{

             public void a();

             public void b();

    };

     

    public class Sub1 extends AbrBase{

             public void a(){

             }

             public void b(){

             }

    };

     

    public interface IBase{

             public void c();

             public void d();

    };

     

    public class Sub2 implements IBase{

             public void c(){

             }

             public void d(){

             }

    };

     

    //进化后代码

    public abstract class AbrBase{

             public void a();

             public void b();

    public void e(){// 为抽象类添加一新的具体的方法,注意抽象方法也不行

    }

    };

     

    public class Sub1 extends AbrBase{//在抽象类添加一具体方法后,子类可以不用改动

             public void a(){

             }

             public void b(){

             }

    };

     

    public interface IBase{

             public void c();

             public void d();

    public void f(); //为接口添加一新的方法

    };

     

    public class Sub2 implements IBase{

             public void c(){

             }

             public void d(){

             }

             public void f(){ //子类必须修改实现新添加的方法,否则编译将不能通过

             }

    };

     

    解决办法:提供抽象骨架类

    //进化之前代码

    public interface IBase{

             public void c();

             public void d();

    };

     

    public abstract class AbrBase implements IBase{

             //primitives

             public void a();

             public void b();

     

             //implenments the method of IBase interface

             public void c(){

             }

             public void d(){

             }

    };

     

    public class Sub extends AbrBase{

             public void a(){

             }

             public void b(){

             }

    //继承AbrBaseIBase的实现

    };

     

    进化后代码:

    public interface IBase{

             public void c();

             public void d();

    public void f(); //为接口添加一新的方法

    };

     

    public abstract class AbrBase implements IBase{

             //primitives

             public void a();

             public void b();

     

             //implenments the method of IBase interface

             public void c(){

             }

             public void d(){

             }

             public void f(){ //修改AbrBase以实现IBase新增加的method

             }

    };

    public class Sub extends AbrBase{//无需改变,继承超类对f()方法的实现

             public void a(){

             }

             public void b(){

             }

    //继承AbrBaseIBase的实现

    };

    That’s all!^_^

     

     

  • Effective Java阅读(四)-item13、14

     

    Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

     

    13、支持不变性(immutable

    我感觉我们只需记住书中列出的几条规则:

    1. 不要提供任何 「可修改对象內容」的方法
    2.
    保证没有可被子类override的方法
    3.
    令所有field final
    4.
    令所有field都是 private
    5.
    保证对任何可变组件的互斥存取

    Immutable object优点:

    ²        Immutable object本质上是线程安全的,不需要同步

    ²        Immutable object可以被自由的share

    ²        Immutable object对于其他对象来说,形成了很多构件

     

    缺点:

    对于每一个不同的值,都要形成一个独立的object

    如:

    String newStr = “AB”+”CD”;

    这样可能会带来的是效率问题。

     

     

    14、复合(composition)优先于继承(extends not implements

    在本Item中,作者针对安全继承提出几个观点:

    ²        包内继承很safe,因为super classsub class都在一个Programmercontrol下。

    ²        继承自专门为继承而设计并有很好的文档的类也很安全。

     

    相反越界继承就是不安全的。

    关于继承的几个观点:

    ²        继承打破的封装性;

    ²        subclass确实是superclass的子类型的时候,也就是确实是“is a”的关系时才使用继承。

     

    继承将会传播 superclass API 的所有缺陷,而复合允许你设计新的 API,隐藏 superclass 的缺陷。

     

    书里还提到了composition vs delegete的概念,以前我对delegate的概念也不是很清楚,这次正好顺便好好分析一下:

    以书中的代码为例:
    public class InstrumentedSet implements Set {
        private final Set s;
        private int addCount = 0;
        .....
        public InstrumentedSet(Set s) {
            this.s = s;
        }

    }

    针对这段代码作者观点:“有时,复合(composition)和转发(forwarding)这两项技术的结合被错误地引用为委托(delegation)” 从技术的角度而言,这不是委托(delegation),除非包装对象把自己(InstrumentedSet)传递给一个被包装的对象(Set)”

     

    关于delegate:GOF那本书中如是说“Delegation is a way of making composition as powerful for reuse as inheritance [Lie86, JZ91]. In delegation, two objects are involved in handling a request: a receiving object delegates operations to its delegate. This is analogous to subclasses deferring requests to parent classes. But with inheritance, an inherited operation can always refer to the receiving object through the this member variable in C++ and self in Smalltalk. To achieve the same effect with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver 。。。。

    The main advantage of delegation is that it makes it easy to compose behaviors at run-time and to change the way they're composed”。

     

    下面的代码改自某论坛上一个网友的代码,我自己觉得这段代码对于正确理解delegate很有帮助。

    Considering the following code:

     

    //MyDelegatee.java

    public class MyDelegatee {

         void methodB() {

              }

    }

     

    //MyDelegate.java

    public class MyDelegate {

       MyDelegatee delegatee;

       void methodA() {

                delegatee.methodB();

       }

    }

    上面的代码就不是delegate,而是compositionforwarding,因为MyDelegate直接调用了MyDelegatee object的方法,这只是forwarding methods

    //MyDelegatee.java

    public class MyDelegatee {

         void methodB(MyDelegate delegate) {

                       delegate.do();

              }

    }

     

    //MyDelegate.java

    public class MyDelegate {

       MyDelegatee delegatee;

       void methodA() {

                delegatee.methodB(this);

       }

       void do(){

       }

    }

    MyDelegate已经把自身pass给了MyDelegatee,并且MyDelegatee调用了MyDelegate的方法,这是一种indirection。也就是说delegatee一定会调用delegate的某些方法,因此你首先得把delegate传递给delegatee

     

    我们在举个实际一点的例子,董事长和总经理的故事:

    Considering the following code:

    //Chairman.java

    public class Chairman {

             private GeneralManager gm = new GeneralManager();

     

             public void doThroughGM(){

                       gm.investmentDecisionMaking(this);

             }

            

             //董事长拥有的权利

             public void investmentDecisionMaking(){

                       /*

                        *董事长具有战略投资决策权,

                        *董事长可将此权利授权给总经理

                        */

                        System.out.println("总经理被授权执行投资决策");

                      

             }

             public static void main(String[] args) {

                       Chairman chairman = new Chairman();

                       chairman.doThroughGM();

             }

    }

     

    //GeneralManager.java

    public class GeneralManager {

            

             public void investmentDecisionMaking(Chairman chairman){

                       //总经理被授权获得的权利

                       chairman.investmentDecisionMaking();

             }      

    }

    //output:

    总经理被授权执行投资决策

     

    上面的例子模仿了一个现实世界的过程,在现实世界中,假如董事长把权利授权给总经理,总经理一定会获取董事长才拥有的权利,它会利用这些权利来替公司做事。

    举了这些例子后对delegate有些概念了吧^_^

     

    参考资料:

    1http://www.javaworld.com/javaworld/javaqa/2001-09/01-qa-0914-delegate.html

    2http://forum.javaeye.com/viewtopic.php?t=6120

      

  • Dominoo项目日记(二)

     

    Dominoo项目从10月份启动到现在已有近2月,我们做了些工作,但是项目的进度缓慢也是我们几个人有目共睹的。针对前一阶段的工作上的不利局面,我们几个做了一次讨论。

     

    最近两周的工作使我们的Dominoo有了很大进展,经过大家的讨论我们在一些技术问题上达成了共识。dreamhead建议我和toidi_xu结对编程,并将dmcXML解析的工作交给我们来做(毕竟我在java编程上还属于菜鸟级),而dreadhead会腾出时间来完成下一步涉及技术问题的研究。不过在最近的讨论中我和toidi_xu也都积累了一些问题,今天我们就针对这些问题进行了讨论。

     

    toidi_xu的问题让我们重新复习了一下Dominoo的提出的动机和设计思想。由于toidi_xu没能参加我们dominoo启动时的第一次讨论会,所以对有些Dominoo的“战略思想”问题有些疑惑。dreamhead带我们回顾的Dominoo的提出过程以及其中所蕴含的东西,让我又有了深刻的体会:

     

    结合dreamheadmodel_zhang的思想我把我们得到的结论说一下:

    1、我们的Dominoo所处在软件开发流程中的位置

                               

    Domain problem 

    -----------------------------------------------------------------------------------------------------------

                                                                                               |

    What(做什么)                                                                 |

                                                                                               |

    ------------------------------------------------------------------------------|

    How(怎么做)  |  logic(如一个业务逻辑                  |

    |       或是一个算法)                 |   Solution                 

                                |      |                                                   |

                                |        |                                                    |

                                |  adapter (将logic转换位                  |

    |        |          Do的实体)                            |

    |        |-------二进制代码                          |

                                |        |-------汇编代码                             |

                                |        |-------高级语言(C/C++/Java     |

                                |        |-------Dominoo(面向设计)         |

                                |        |-------XXXX(面向需求)

    --------------------------------------------------------------------------------------------------------

    Do(运行的实体)    |      CPU

     

    说明一下上面的图,我们在解决问题的时候实际上都是在寻找问题域的一个解空间,在这个过程中我们要明确what , how , do。在how这个阶段,结合dreamheadmodel_zhang产生的思想就是从what转换到logic,然后我们使用Adapter将我们的logic转换为可以运行在CPU上的产品,adapter的发展从binary code 到汇编到高级语言,再到我们所要实现的Dominoo(面向设计的工具或者说语言也可以),在这个发展过程中开发人员或者设计人员的关注程度逐渐从底层提高到问题领域,也许有一天我们使用自然语言描述需求,某个工具就能帮我们生成可运行的软件,这是我们理想的境界。不过我们现在做不到,我们必须脚踏实地的一点点向上攀登。

     

    Dominoo提供给你一个可以快速验证你的设计是否可行有效的解决方案,同时也可以帮你生成最终的产品。你需要作的就是设计和少量动态语义的编码。

     

    model_zhang曾经提出在我们的Dominoo上实现测试设计和执行的自动化,甚至是测试重构等概念。让我大开眼界。^_^

     

    我这几天积蓄的问题大多集中在实现的细节上,这里就不详细说了。

     

     

  • Effective Java阅读()-item12

     

    Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

     

    item12~item18讨论的都是单个类或接口的设计技术。

     

    12、使类和成员的可访问能力最小化

    作者提出“你应尽可能的是每一个类或成员不被外界访问”

     

    ²        顶层类或接口的访问级别

    public --- 意味着你的类是提供给客户API的一部分,你有义务永远支持它,维护它。

    package-private --- 如果一个类或接口做成packge-private,那么意味着它实际上已成为包的实现的一个部分,而不是包提供给客户(使用者)的API的一部分。在以后包的维护过程中你对该类进行修改、替换和删除对客户并无影响。

     

    ²        or接口内成员的访问级别

    在几乎每一本java教科书中都有,我这里就不浪费文字了。

    不过书中有几个观点还是值得我们注意的:

    Ø         如果一个方法改写了超类的一个方法,那么子类中该方法的访问级别低于超类中的访问级别是不允许的。这样可确保子类的实例可被用在任何超类实例使用的场合。

    Ø         公有类不应该包含public field,例外情况通过公有的静态final field来暴露类中的常量是可以的。

    Ø         如果类内的一个final field包含一个指向可变对象的引用,那么它具有非final field的一切缺点,虽然引用本身是final的不能被修改,但是它引用的对象却可以被修改,这将是一个很危险的安全漏洞。如果你非要这么做的话,请确保被 public static final field所引用的对象是不可变的。

    Considering the following code

    public class TestItem12 {

    public static final Integer[] intArray = { 1 , 4 , 5 , 6};//可以看出我们提供该final Integer的意图是其////不被修改,但实际上是可以被修改的。

             public static void main(String[] args) {

                       System.out.println("Before modifying :");

                       //这是我们的意图,我们要使用static final field的值。

                       for (Integer i : TestItem12.intArray ){

                                System.out.println(i);

                       }

     

    //这可不是我们的意图,但是这样做编译器不会告诉你你的final引用的对象被修改了,you //are in danger

                       TestItem12.intArray[1] = 11;

                       TestItem12.intArray[2] = 10;

                       System.out.println("After modifying :");

                       for (Integer i : TestItem12.intArray ){

                                System.out.println(i);

                       }

             }

    }

    //output

    Before modifying :

    1

    4

    5

    6

    After modifying :

    1

    11

    10

    6

    可以看到引用本身并未发生变化但是所引用对象的值发生了变化。我们改变一下:

    public class TestItem12 {

             public static final Integer[] valuesOfArray(){

                       return (Integer[])intArray.clone();

             }

             private static final Integer[] intArray = { 1 , 4 , 5 , 6};

             public static void main(String[] args) {

                       Integer[] ia = TestItem12.valuesOfArray();

                       for (Integer i : ia ){

                                System.out.println(i);

                       }

    a[2] = 123;

                       for (Integer i : TestItem12.valuesOfArray() ){

                                System.out.println(i);

                       }                         

             }

    }

    //output:

    1

    4

    5

    6

    1

    4

    5

    6

    当然这样会牺牲一些性能。^_^

           在这个过程中顺便谈谈clone()这个方法吧,很有意思的,呵呵,看代码吧。

    public class TestItem12 {

             public static final Integer[] valuesOfArray(){

                       return (Integer[])intArray; // remove the clone() method

             }

                 

             private static final Integer[] intArray = { 1 , 4 , 5 , 6};

             public static void main(String[] args) {

                       Integer[] ia = TestItem12.valuesOfArray();

                       for (Integer i : ia ){

                                System.out.println(i);

                       }

    a[2] = 123;

                       for (Integer i : TestItem12.valuesOfArray() ){

                                System.out.println(i);

                       }

    }

    //output:

    1

    4

    5

    6

    1

    4

    123

    6

    看到上面代码,remove the clone method后,输出了123,也就是说final reference所引用的对象的值被修改了。这样做是十分危险的,因为它直接暴露了类内部的成员。Clone的作用是它重新new了一块内存,并用intArray对其进行了初始化。这样实际上外部的引用就不会和内部的private引用指向同一块memory了。Simple.^_^

     

     

     

     

  • Effective Java阅读()-item46

     

    Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

     

    4、避免创建重复对象

    我们知道构造函数每次被调用的时候都会创建一个新的对象,在有些情况下这样会很浪费。那我们能不能重复使用一个对象(这个对象应该是immutable的),而不是在每次需要的时候都要创建一个新的对象呢?这就是这个item4所要讲述的事情。

     

    ²        重用immutable对象

    item1时我们说过使用static factory method方法可以控制对象的创建,在这里我们将用到此种方法。

     

    举个Boolean的例子,Boolean只有两个值true or false,而且Booleanimmutable

    Considering the following code:

     

    private static final Boolean TRUE = new Boolean(true);
    private static final Boolean FALSE = new Boolean(false);

    public static final Boolean valueOf(final boolean b)
    {
      if( b )
        return TRUE;
      else
        return FALSE;
    }

    可以看出immutable classes可以使用static factory method来避免产生重复对象。

     

    ²        重用已知不会被修改的可变对象

    看看下面的例子:(改编自书中的例子,大部分摘取)

    import java.util.*;

     

    public class Person1 {

             private final Date birthDate;

            

             Person1(Date birthDate) {

                       this.birthDate = birthDate;

             }

     

             public boolean isEighties(){//判断是否是80年代的人

                Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

            gmtCal.set(1980, Calendar.JANUARY, 1, 0, 0, 0);

            Date EIGHTIES_START = gmtCal.getTime();

            gmtCal.set(1990, Calendar.JANUARY, 1, 0, 0, 0);

            Date EIGHTIES_END = gmtCal.getTime();

     

                       return birthDate.compareTo(EIGHTIES_START) >= 0 &&

                   birthDate.compareTo(EIGHTIES_END)   <  0;

             }

     

             public static void main(String[] args) {

                       Person1 p = new Person1(new Date());

            long startTime = System.currentTimeMillis();

            for (int i=0; i<1000000; i++){

                p.isEighties();

                       }

            long endTime = System.currentTimeMillis();

            long time = endTime - startTime;

            System.out.println(time+" ms.");

             }

    }

    //output

    9250ms

     

    可以看出上述代码每次调用isEighties函数时,Calendar,Date,TimeZone都要被实例化一次,这样的代价适合昂贵的。我们改进一下,将不变的Date对象的实例化的工作放在一个static初始化中,看看效果如何。

    import java.util.*;

     

    public class Person {

             private final Date birthDate;

            

             Person(Date birthDate) {

                       this.birthDate = birthDate;

             }

            

             private static final Date EIGHTIES_START;

             private static final Date EIGHTIES_END;

     

             static {

            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

            gmtCal.set(1980, Calendar.JANUARY, 1, 0, 0, 0);

            EIGHTIES_START = gmtCal.getTime();

            gmtCal.set(1990, Calendar.JANUARY, 1, 0, 0, 0);

            EIGHTIES_END = gmtCal.getTime();

        }

     

             public boolean isEighties(){

                       return birthDate.compareTo(EIGHTIES_START) >= 0 &&

                   birthDate.compareTo(EIGHTIES_END)   <  0;

             }

     

             public static void main(String[] args) {

                       Person p = new Person(new Date());

     

            long startTime = System.currentTimeMillis();

            for (int i=0; i<1000000; i++){

                p.isEighties();

                       }

            long endTime = System.currentTimeMillis();

            long time = endTime - startTime;

            System.out.println(time+" ms.");

             }

    }

    //output

    62ms

     

    ! 9250 vs 62 ,速度得到飞快提升。

     

    6、避免使用终结函数

    在这一节中作者也写了很多,不过我最感兴趣的是“finalizer chaining”终结函数链。

    其主要的内容就是“如果一个类(除了Object类)拥有一个finalize函数,并且它的子类overridefinalize函数(java中所有类的member func都是virtual的,可以被override的),那么你应该在子类的finalize函数中手动调用父类的finalize函数”。

     

    protected void finalize() throws Throwable {

             try {

                       //finalize subclass state

                       ...

             }finally{

                       super.finalze();

    }

    }

     

    如果你不手动调用父类的finalize函数,父类的finalize函数将永远不会被调用。那么父类的finalize函数中的关键资源将不能够被释放。

    但是程序员总是有些“粗心大意”的,我们不能指望程序员都能记住要手动调用父类的finalize,那么我们就要想办法了。书中提到了“finalizer guardian”的概念。书中的论点是:“对于所有带有finalize函数的非finalpublic class,都应该考虑finalizer guardian技术”这里举例说一下我的理解,以书中的例子为例:

     

    从原来的

    public class Foo {

             protected void finalize() throws Throwable {

                      //finalize the class Foo object

                                .....

                       }

             };

             //.....

    }

     

    =è进化为

    public class Foo {

             private final Object finalizerGuardian = new Object() {

                       protected void finalize() throws Throwable {

                                //finalize the outer class Foo object

                                .....

                       }

             };

             //.....

      //注意:该Foo类中并无finalize函数,所有关键资源的释放都由inner class overridefinalize负责释放。

    }

     

    我们首先看看进化后类中的角色:

    Foo类:拥有关键资源需要释放且可能作为父类被继承。

    Inner class: 继承自Object类,overrideObjectfinalize方法,并在finalize方法中帮助释放其outer class Foo的关键性资源。

     

    关键性资源释放的流程:由于inner class objectFoo object同生命周期,它们可以同时启动终结过程,inner class在终结过程中就会帮助Foo class释放关键性资源,保证关键性资源不会泄露。这样即使Foo类的子类在finalize中没有手动调用(如果父类使用了finalizer guardian技术,子类就无需手动调用父类的finalize了)父类的finalize,父类的关键资源也会被释放掉。That’s all^_^

     

     

  • Effective Java阅读()-item1

     

    Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

     

    1、使用静态工厂方法代替构造函数

    静态工厂方法优点:

    ²        可命名性:(而构造函数的名字必须和类名一致),使class使用起来较容易,构造函数只是根据不同的函数signature来区分,对使用者来说容易发生调用错误。

    ²        内部cache特性:在静态工厂内部可采用cache等机制控制对象实例的产生,比如singleton机制。

    ²        返回一个原类型的一个子类型的对象。(体现了面向接口,不知道我这么理解是否正确)

     

    用我蹩脚的初学的java代码来说明问题吧^_^

    Consider the following code:

    //BaseObj.java

    public abstract class BaseObj{

     

             public static BaseObj getInstance(String className){

                       try{

                                Class c = Class.forName(className);

                                return (BaseObj)c.newInstance();

                       }catch(Exception e){

                                return null;

                       }

             }

             public abstract void show();

    };

     

    //SubObj1.java

    public class SubObj1 extends BaseObj{

             public void show(){

                       System.out.println(" I am SubObj1");

             }

    };

     

    //SubObj2.java

    public class SubObj2 extends BaseObj{

             public void show(){

                       System.out.println(" I am SubObj2");

             }

    };

    //TestStaticFactoryMethod.java

    public class TestStaticFactoryMethod{

             public static void main(String[] args) {

                       BaseObj.getInstance("SubObj1").show();

                       BaseObj.getInstance("SubObj2").show();

             }

    }

     

    //output:

    I am SubObj1

    I am SubObj2

     

    从代码可以看出我们可以通过Reflection机制在runtime期间产生某种BaseObj的子类型,所以在编写BaseObj代码时我们根本不需要知道BaseObj到底有几个子类型。有的人说即使这样我们在使用的时候也要明确传入子类的类型的名字,也就是说还要指名道姓,如上面代码中我们传入"SubObj1""SubObj2",有人提出使用abstract factory的模式,显然有可能解决问题,但是就上面的论述"返回一个原类型的一个子类型的对象"而言,使用abstract factory显然是混淆了概念。这里可以用一些折中的办法,比如在BaseObj中维护一个map表,并利用配置文件来动态load sub class’s name and keys,这样我们只需修改配置文件就可以动态的增加子类型。

     

    设计模式中的Factory pattern与这里谈到的工厂方法还是有一定区别的,不要混为一谈。首先我们这里谈的static factory method的产生对象是什么我们要搞清楚,是产生static factory method本身的对象或者其子类型的对象(上面的代码是产生子类型的对象)。而设计模式中的工厂模式中“工厂”和“产品”之间并无继承关系。

    如设计模式工厂模式的一段例子代码:

    public class Factory{

      public static Sample creator(int which){

      //getClass 产生Sample 一般可使用动态类装载装入类。
      if (which==1)
        return new SampleA();
      else if (which==2)
        return new SampleB();

      }

    }

    可以看出工厂的类型为:Factory ,而产品为SampleA or SampleB

     

    静态工厂方法缺点:

    ²        类如不提供public or protected就不能被子类化;

    ²        它和其它静态方法没有任何区别,在文档中不能显著体现出来它的作用

     

    现在java标准包中很多都是在abstract class中提供static factory method的,abstract 类本身就是用来被继承的,所以说第一个缺点被淡化了,书的作者的观点是这个缺点鼓励程序员使用复合,少用继承。至于第二个缺点现在有两个被很多包使用的static factory method的名字,valueOfgetInstance。我们这么用就行了^_^

     

    从上面代码的分析中我们可以看到工厂方法有优点也有缺点,使不使用工厂方法你自己决定吧^_^

  • JDK5.0新特性研究(三)

     

    今天在“Java技术论坛”上看到了“J2se5.0新增小功能”这个帖子,大家的集思广益让我又有了一些收获。

     

    1、再谈java引入的格式化输入和输出

    在“JDK5.0新特性研究(一)”中,当时只是看了一些简单的例子,自己也没有深入看看jdk5.0doc。今天看到论坛上的一些例子还是把我吸引了,自己又翻了翻昨天刚下的jdk5.0doc,觉得java引入的新的格式化输入输出还是蛮好的^_^

    Java的格式化输入输出功能都是由java.util.Formatter类提供的。

     

    Java提供printf:

    //format output:

                  String str = String.format("my name is %s" , "tony bai");

                  System.out.println(str);//my name is tony bai.

     

                  System.out.printf("[%s] is %d years old", "dreamhead", 26);//输出一行无换行。

                 

                  System.out.printf("[%s] is %d years old", "tony bai", 22).println();//输出一行有换行。

    熟悉C/C++语法的对printf的使用应该不陌生,javaprintf的使用几乎和C/C++没什么区别。

     

    Java提供scanner,相当于scanf:

                  System.out.println(“Please enter your job id:”);

    Scanner sc = new Scanner(System.in);

                  int id = sc.nextInt();

                  System.out.println(“Please enter your job name:”);

                  String jbName = sc.next();

     

    运行后,首先让你输入job id,这是个int值,如果你输入字符,将会看到如下异常被throwed:

    Exception in thread "main" java.util.InputMismatchException

            at java.util.Scanner.throwFor(Scanner.java:819)

            at java.util.Scanner.next(Scanner.java:1431)

            at java.util.Scanner.nextInt(Scanner.java:2040)

            at java.util.Scanner.nextInt(Scanner.java:2000)

            at Main.main(Main.java:59)

    C/C++中没有这种类型检查,我们举个简单的例子:

    //test.c , written in standard c

    #include <stdio.h>

     

    int main(int argc, char *argv[])

    {

           int job_id = 0;

           printf("enter your job id:\n");

           scanf("%d" , &job_id);

           printf("your job id is %d:\n" , job_id);

           return 0;

    }

     

    //test.cpp written in standard c++

    #include <iostream>

    #include <string>

    using namespace std;

     

    int main(int argc, char *argv[])

    {

           int job_id = 0;

           cout << "enter your job id:" << endl;

           cin >> job_id;

           cout << "your job id is " << job_id << endl; 

           return 0;

    }

     

    上面两段程序完成统一功能。即接受你输入的job id,然后输出你的job id

    运行上面两个程序,我们输入”years”作为我们的job id,程序输出的结果是:

    “your job id is 0”;说明程序没有接受我们的输入而直接输出初始值了。这种情况可不是我们所要的,所以说java做得还是不错的,抛出了异常,以免输入错误的类型值。

     

    2UUID

    使用过com组件的人都对uuidUniversally Unique IDentifier)不陌生,每个com组件都有自己全球唯一的uuid,在microsoftvisual studio中提供一个小工具可以生成一个uuidJava5.0中也引入了uuid,而且使用起来也很简单。

     

    UUID uuid = UUID.randomUUID(); //use a static factory method to create an instance

    System.out.printf("UUID : %s", uuid).println();

    //outputUUID : a4bf4134-1395-4b13-a536-c1f538f95ff8

     

  • JDK5.0新特性研究(二)

     

    4Type safe Enumerations

    Java不支持enumjava程序员抱怨较多的,这次sun终于在java5.0中加入了对enum的支持,不过还是如上面几个new features一样,只是在java compiler一级支持。

     

    我们看看enum类型被javac转换成什么了?

    //Color.java

    public enum Color {

           RED,

           GREEN,

           BLUE

    };

     

    上面是一个很简单的enum声明,下面是反编译器输出的代码:

    public final class Color extends Enum

    {

     

        public static final Color[] values()

        {

            return (Color[])$VALUES.clone();

        }

     

        public static Color valueOf(String s)

        {

            Color acolor[] = $VALUES;

            int i = acolor.length;

            for(int j = 0; j < i; j++)

            {

                Color color = acolor[j];

                if(color.name().equals(s))

                    return color;

            }

     

            throw new IllegalArgumentException(s);

        }

     

        private Color(String s, int i)

        {

            super(s, i);

        }

     

        public static final Color RED;

        public static final Color GREEN;

        public static final Color BLUE;

        private static final Color $VALUES[];

     

        static

        {

            RED = new Color("RED", 0);

            GREEN = new Color("GREEN", 1);

            BLUE = new Color("BLUE", 2);

            $VALUES = (new Color[] {

                RED, GREEN, BLUE

            });

        }

    }

    sun公司的一份培训材料中写到:

    Problem:

    ----Variable needs to hold limited set of values e.g. card suit can only be spade, diamond, club, heart

    Solution:

    ---New type of class declaration Enum type has public, self-typed members for each enum constant

    ---New keyword, enum Works with switch statement

     

    下面我们根据上面的叙述来使用一下enum:

    public class TestEnum{

           public enum Color {

                         RED,

                         GREEN,

                         BLUE

                  };

        public static void main(String[] args) {

                  for (Color clr : Color.values() ){

                         System.out.println(clr);

    /*output

    RED

    GREEN

    BLUE

    */

                  }           

           }

    }

     

    反编译后我们会看到如下的代码:

    public static void main(String args[])

    {

            Color acolor[] = Color.values();

            int i = acolor.length;

            for(int j = 0; j < i; j++)

            {

                Color color = acolor[j];

                System.out.println(color);

            }

    }

     

    通过这两个例子也可以看出enum不过是从Enum继承下来的类罢了,只是javac将相关的变换细节隐藏了。不过却给我们带来了极大的便利。

     

    5Varargs

    Problem:

    --- To have a method that takes a variable number of parameters

    --- Can be done with an array, but not nice

    --- Look at java.text.MessageFormat

    . Solution: Let the compiler do it for you

    --- New syntax:

    --- public static String format (String fmt , Object... args);

    --- Java. gets printf !

     

    例子:

    public static void myPrintf(Object... args){

                  for(Object obj : args){

                         System.out.println(obj);

                  }

    }

        public static void main(String[] args) {

                  int i = 6;

                  myPrintf("Hello" , "Tiger" , "JDK5.0");

                  myPrintf("Harbin" ,"Beijing","Shenyang" ,"Changchun");

                  myPrintf("The number is :" , i);

           }

    我们看一下反编译器输出的代码:

     

                  public static transient void myPrintf(Object aobj[])

               {

                   Object aobj1[] = aobj;

                   int i = aobj1.length;

                   for(int j = 0; j < i; j++)

                   {

                       Object obj = aobj1[j];

                       System.out.println(obj);

                   }

               }

     

    byte byte0 = 6;

            myPrintf(new Object[] {

                "Hello", "Tiger", "JDK5.0"

            });

            myPrintf(new Object[] {

                "Harbin", "Beijing", "Shenyang", "Changchun"

            });

            myPrintf(new Object[] {

                "The number is :", Integer.valueOf(byte0)

            });

    从反编译的代码可以看出javac内部使用了数组来完成对变参数的支持。

     

    6Static imports

    Problem:

    ---Having to fully qualify every static referenced from external classes

    .Solution: New import syntax

    ---import static TypeName.Identifier;

    ---import static Typename.*;

    ---Also works for static methods and enums e.g Math.sin(x) becomes sin(x)

     

    上面是引用sun那个ppt中的一些东东,这个我就不想详细写了,今天较累,看看电影轻松一下吧!

    Java5.0第一部分就写到这。

  • Everything is an object

     

    读过《Thinking in Java》中著名的一章“Everything is an object”,而且不止一遍,不过经过今天和Dreamhead的探讨,才发现我对Java中的“Everything is an object”的理解还是那么的不到位。

     

    我和Dreamhead谈到我在研究JDK5.0时遇到的问题:

    String[] s = {Hello ,

    Tiger;

    !}

    现在我想用传统的for loop来打印出字符串数组内的每一个字符串,我的问题是我该如何获取这个数组内的元素个数。我便查询一下jdkdoc,发现String类有一个length()方法,我就在s上使用了,结果编译器提示“can not find the method”,我就很奇怪了,为什么查到了String类下有概method,而compiler却提示找不到呢?后来我也使用了s.length,编译也通过了,执行也正确了。但是我心中的疑惑一直没有解开。今天和Dreamhead讨论下一步项目的工作计划时提到了这个问题,Dreamhead首先就说出了“Everything is an object”,String s是一个object ,同样String[] s也是一个object,也就是说我应该将String[] 放在一起看作一个类型,而不仅仅认为这是个String 类型的静态数组。这样length作为String[]类型的一个field就顺理成章了,我也就茅塞顿开了。也许上面的问题对于java老手来说很easy或者说根本不屑一提,但是对于我这个由C++过渡到java的选手来言,理解这个是很重要的。

     

    最后还是牢记“Everything is an object in java”。

  • JDK5.0新特性研究

     

    DreamHead计划使用“Tiger”来开发我们的Dominoo,理由是2-3年后“Tigerwill be mature

    我们对刚刚发布不久的“Tiger”了解的不多,晚上我们几个group member坐了下来,听DreamHead讲解“Tiger”的新特性。回到寝室自己做了些实验,有了些体会。

     

    通过对“Tiger”的讨论和研究,得知Sun推出“Tiger”目的就是为了简化Java程序员的开发工作。在下面的详细解释及代码例子中你会深刻的体会到这一点。

     

    1Auto-boxing and auto-unboxing conversion

    Problem:

    Conversion between primitive types and wrapper objects (and vice-versa)

    Needed when adding primitives to a collection

    Jdk5.0给我们的解决方案就是:Let the compiler(Javac) do it

    事实上javac真的是这么做的么,耳听为虚,眼见为实,看代码吧!

    我使用IntelliJ-Idea4.5.2写的代码(现在IntelliJ4.5已经可以支持jdk5.0的语法了,今早看了一下Eclipse的网站,Eclipse要到3.1才支持jdk5.0新加入的特性)

    []:在IntelliJ4.5中“File〉“Setting〉左下角“Classic Viewà最下面的“Language level for project”选择5.0即可。

     

    /*

             * test auto-boxing and auto-unboxing

             */

            //old 1.4 style

            Integer intObj1 = new Integer(22);

            int i = intObj1.intValue();

            System.out.println(i);      //output: 22

     

            //new 1.5 style

            Integer intObj2 = 23;  //auto-boxing

            int j = intObj2;      //auto-unboxing

            System.out.println(j); //output: 23

     

            //old 1.4 style

            ArrayList al1 = new ArrayList();

            al1.add(new Integer(24)); //or al1.add(Integer.valueOf(24))

     

            //new 1.5 style

            ArrayList al2 = new ArrayList();

            al2.add(24);   // auto-boxing conversion  

     

    编译为.class模块后,我们使用DJ Java Decompiler重新反编译得到以下代码(只写出对应上面代码的):

    Integer intObj1 = new Integer(22);

            int i = intObj1.intValue();

            System.out.println(i);

     

            Integer intObj2 = Integer.valueOf(23);

            int j = intObj2.intValue();

            System.out.println(j);

     

            ArrayList al1 = new ArrayList();

            al1.add(new Integer(24));

     

            ArrayList al2 = new ArrayList();

            al2.add(Integer.valueOf(24));

     

    从上面代码中我们也可以看出,javac的确帮了我们的忙,它把繁重的活都揽去了,给我们留下了清闲,哦,忘说了,俺也是懒人^_^

     

    2Generics in java

    Problem: Collection element types

    -- Can not be checked at compile time

    -- Assignment must use cast

    -- Can cause runtime exception or errors(ClassCastException)

     

    解决方案:

    --Tell the compiler what type your collection is

    -- Compiler can fill in casts for you

    还是那个原则,告诉编译器你的collection中需要什么类型,编译器为你完成一切(类型检查,转型等)。

    看代码:

    ArrayList<String> strArrayList = new ArrayList<String>();

            strArrayList.add("Hello");

            System.out.println(strArrayList.iterator().next());  //output:Hello

                  System.out.println(strArrayList.getClass());        //output:class java.util.ArrayList

     

    反编译后代码:

                  ArrayList strArrayList = new ArrayList();

            strArrayList.add("Hello");

            System.out.println((String)strArrayList.iterator().next());

                  System.out.println(strArrayList.getClass());

    实际上包括转型等都是javac帮我们做了。

    从上面的代码中我们还能看出问题:看紫色标记的代码,我们打印strArrayList的类型,该变量在声明的时候是ArrayList<String>类型的,我们的头脑里会认为打印出的应该是“class java.util.ArrayList<String>,可实际打印出的信息告诉我们collection中的元素的类型信息被丢弃了。也就是说ArrayList<Integer>ArrayList<String>本不该是同一类型,不过我们看到的实际情况却是他们是同一类型。由于java中的generic只是在compiler一级得到支持,在jvm一级是不知道有generic这回事的,也许sun可能在不久的将来修正这一缺陷,别忘了java的强大的竞争对手C# 2.0中已经在vm中加入对generic的支持。

     

    这里要提一下DreamHeadjava模板的理解,挺逗的,蛮有启发性。DreamHead 剧本<男主角>  电影版本来理解java generic ,

    射雕剧本<黄日华>  83版射雕 = new  射雕剧本<黄日华>();

    射雕剧本<李亚鹏>  03版射雕 = new  射雕剧本<李亚鹏>();

     

    Java generic与其他预言generic的简单比较,Javacompiler一级加入generic,导致的后果就是丢掉了类型信息,我们知道C++模板是不会丢掉类型信息的,但是会使可执行文件大小发生膨胀。C#2.0由于有VM的支持,所以既拥有类型信息,又不会使可执行文件的体积膨胀,但是总体来言,C++generic最强大,也最成熟,当然和它支持的比较早也不无关系。

     

    [] 在《Adding Generics to the Java Programming Language:Participant Draft Specification》一文中关于上述问题有以下说明:

    A parameterized class or interface declaration defines a set of types, one for each possible instantiation of the type parameter section. All parameterized types share the same class or interface at runtime.

    For instance, the code

    Vector<String> x = new Vector<String>();

    Vector<Integer> y = new Vector<Integer>();

    return x.getClass() == y.getClass();

     

    will yield true.

     

    3Enhanced for loop(foreach)

    C#早已引入foreach这个关键字,C++也提供foreach算法(如果要弄懂这个需要学习的东西就比较多了^_^),javajdk5.0开始引入这种高级forloop机制,不过java的设计者还是很聪明的,java并没有引入新的关键字,因为要是这样做的话,需要进行的改动工作就太多了。Java利用原有的for关键字和一个“:”就把这个完成了,不过java引入这一机制也只是方便程序员开发,这一机制也都是由javac来支持的,jvm一级并不知道foreach这一机制的存在。我们看看代码吧!

     

    Vector<String> strVec = new Vector<String>();

                  strVec.add("Hello");

                  strVec.add("Tiger");

                  strVec.add("!");

     

                  //old 1.4 style - dynamic container

                  for (Iterator i = strVec.iterator(); i.hasNext(); ){

                         System.out.println(i.next());

                  }

     

                  //new 1.5 style - dynamic container

                  for ( String str1 : strVec ){

                         System.out.println(str1);

                  }

     

                 

                  String [] strArray = {

                         "Java 2",

                         "Platform",

                         "Standard",

                         "Edition",

                         "1.5"};

                  //old 1.4 style - static container

                  for(int i = 0 ; i < strArray.length ; ++i){

                         System.out.println(strArray[i]);

                  }

     

                  //new 1.5 style - static container

                  for(String str2 : strArray){

                         System.out.println(str2);

                  }

    我们还是来看看反编译后的代码:

    for(Iterator iterator1 = vector.iterator(); iterator1.hasNext(); System.out.println(s))

            s = (String)iterator1.next();

     

                  String args2[] = args1;

            int j = args2.length;

            for(int k = 0; k < j; k++)

            {

                String s1 = args2[k];

                System.out.println(s1);

            }

    注:上面对应颜色的代码相对应。

    可以看出javac又在帮我们的忙。紫色部分我们看到仅用3行代码就搞定,而编译器得辛苦生成那么多代码。