2004年十一月月 发布的文章

Effective Java阅读笔记-item24、25、34

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

24、需要时使用保护性拷贝
在学习这个item之前我们看看下面这段“危险的”代码(改编自书中例子):

Considering the following code:
//Period.java
import java.util.Date;
public final class Period {
    private final Date start;
    private final Date end;
    public Period(Date start , Date end){
        //do some checking , end should be later than start
        this.start = start;
        this.end   = end;
    }
    public Date start(){
        return this.start;
    }
    public Date end(){
        return this.end;
    }
    //remainder omitted…
    public static void main(String[] args) {
        Date start = new Date(2004 , 11 , 28);
        Date end   = new Date(2004 , 11 , 30);
        Period p = new Period(start , end);
        
        System.out.println("Normal period:");
        System.out.println(p.start());
        System.out.println(p.end());

        //danger code part1
        System.out.println("Unnormal period part1:");
        end.setMonth(4);
        System.out.println(p.start());
        System.out.println(p.end());

        //danger code part2
        System.out.println("Unnormal period part2:");
        p.end().setMonth(5);
        System.out.println(p.start());
        System.out.println(p.end());
    }
}

//output:
Normal period:
Wed Dec 28 00:00:00 CST 3904
Fri Dec 30 00:00:00 CST 3904
Unnormal period part1:
Wed Dec 28 00:00:00 CST 3904
Mon May 30 00:00:00 CST 3904
Unnormal period part2:
Wed Dec 28 00:00:00 CST 3904
Thu Jun 30 00:00:00 CST 3904

Period类声称提供一段不可改变的时间段,不过从上面的输出结果来看我们可以轻易的修改这个时间段。为了使上面的Period类真正成为非可变类,我们需要进行“defensive copy”。

Danger code part1提示我们“对构造函数的每个可变参数进行保护性拷贝”;
Danger code part2提示我们“对类中public方法返回的可变内部域进行保护性拷贝”;

修改如下:
public Period(Date start , Date end){
        //do some checking , end should be later than start
        this.start =new Date( start.getTime());
        this.end = new Date(end.getTime());
}
public Date start(){
    return (Date)this.start.clone();
}
public Date end(){
    return (Date)this.end.clone();
}
修改后再运行,结果如下:
Output:
Wed Dec 28 00:00:00 CST 3904
Fri Dec 30 00:00:00 CST 3904
Unnormal period part1:
Wed Dec 28 00:00:00 CST 3904
Fri Dec 30 00:00:00 CST 3904
Unnormal period part2:
Wed Dec 28 00:00:00 CST 3904
Fri Dec 30 00:00:00 CST 3904

切记:在把类的内部组件的引用返回给客户之前,你应该返回一个保护性拷贝,如内部数组。

还有一点就是如果有可能你尽量使用immutable object作为你的内部组件,这样你就可以不必关心“defensive copy”的问题了。

25、小心设计方法的原型
在这个item中记住几个原则即可(这几个原则对我这个菜鸟来说用处还是蛮大的):
- 小心选择方法名字(可参考那本java developers almanac的索引来看看java中方法都是如何命名的)
- 对于参数类型,优先选用接口而不是类
- 小心使用function object , 原因不是主流的代码风格,难于理解。

34、通过接口引用对象
Item25中提到了“对于参数类型,优先选用接口”,更一般的讲我们应该用接口来引用对象。
书中的例子:
List subscribers = new Vector(); //good – use interface as type!
Vector subscribers = new Vector(); //bad-use class as type!

如果没有合适的接口,使用类来引用对象也是完全适合的。
其中有一种情况是当我们没有合适的接口时,我们尽量使用基类(往往是abstract class)来引用实现类(子类)的对象。

Effective Java阅读笔记-item18

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

18、优先考虑静态成员类(static member class)
在C++的应用中我们很少使用嵌套类,我只在MFC和COM组件中遇到过这些,而且这些嵌套类被隐藏在应用背后,一般的应用中则很少使用。而在Java中嵌套类的应用还是要比在C++中多些。Java中嵌套类主要之功用就是辅助其outer class,为outer class提供服务。

嵌套类分类:
- static member class:
- nonstatic member class:
- anonymous class:
- local class

按照书中的方式我们根据各种类型嵌套类的用途来说:
* static member class
基本格式:
public class OuterClass{
    publc/private static class InnerClass{
        //…
    }
}

对于static型的member class:
多个outer class instance共享一个static member class,就是说static member class和outer类的一般静态成员地位是一样的, static member class可以访问outer class的所有成员,包括private的成员。

书中的例子:(两层嵌套,既使用了static member class又使用了anonymous classes)
//Calculator.java
public class Calculator {
   public static abstract class Operation {
      private final String name;
      Operation(String name)   { this.name = name; }
      public String toString() { return this.name; }
      // Perform arithmetic op represented by this constant
      abstract double eval(double x, double y);      
      // Doubly nested anonymous classes
      public static final Operation PLUS = new Operation("+") {
         double eval(double x, double y) { return x + y; }
      };      
   }
   // Return the results of the specified calculation
   public double calculate(double x, Operation op, double y) {
      return op.eval(x, y);
   }
}
//CalcTest.java
public class CalcTest {
    public static void main(String args[]) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        operate(x, Calculator.Operation.PLUS, y);
    }
    static void operate(double x, Calculator.Operation op, double y) {
        Calculator c = new Calculator();
        System.out.println(x + " " + op + " " + y + " = " +
                           c.calculate(x, op, y));
    }
}
//command: java CalcTest 3 4
//output: 3.0 + 4.0 = 7.0

* nonstatic member class
基本格式:
public class OuterClass{
    publc/private class InnerClass{
        //…
    }
}

对于non-static型的member class:

每个outer class instance都要维护一个member class的实例,且这种关系在outer class被实例化后就不变了。nonstatic member class的实例是在其outer class实例化时才有意义的。

所以当嵌套类的实例可以不依赖于其outer class的实例而存在时。我们应该使用static member class。
nonstatic member classes 常被用來定义 Adapter ,允许我们将outer class的实例视为某些不相关的类的实例。
例如:像Set和List这样的集合接口的实现往往利用nonstatic member class来实现iterator

书中例子:
public MySet extends AbstractSet{
    //…
    private class MyIterator implements Iterator{//inner class or nested class
    
    }
    public Iterator iterator(){
        return new MyIterator();
    }
}

* anonymous classes(匿名类)

匿名类不是outer class的一个成员,它在使用处被同时声明和实例化。匿名类通常只是实现了其接口或超类中的方法。它们不会声明新方法。

匿名类的用途很广泛。
- 用来创建一个function object
如代码:
Collections.sort(list, new Comparator() {
    public int compare(Object o1, Object o2) {
      return ((String)o1).length() – ((String)o2).length();
    }
  }
);
像这样的匿名类可读性差,建议不要把匿名类写的很长。
这里我们假设匿名类的名字为$,这实际上的代码可能是像这样的:
class $ implements Comparator{
    public int compare(…){
        //implement the compare method
}
}
Collections.sort(list, new $());

- 创建process object,例如 Thread、Runnable 实例
- 给 static factory 方法使用,可以在 static factory method 返回一份 anonymous 的实例

* local class(局部类)
使用local class把握一个原则:local class与局部变量的使用(包括声明和scope)几乎一模一样。

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(){
    }
//继承AbrBase对IBase的实现
};

进化后代码:
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(){
    }
//继承AbrBase对IBase的实现
};
That’s all!^_^




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多