标签 Java 下的文章

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 class和sub class都在一个Programmer的control下。
- 继承自专门为继承而设计并有很好的文档的类也很安全。

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

关于继承的几个观点:
- 继承打破的封装性;
- 当subclass确实是superclass的子类型的时候,也就是确实是“is a”的关系时才使用继承。

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

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

public class InstrumentedSet implements Set {
    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,而是composition和forwarding,因为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有些概念了吧^_^。

参考资料:
1、http://www.javaworld.com/javaworld/javaqa/2001-09/01-qa-0914-delegate.html
2、http://forum.javaeye.com/viewtopic.php?t=6120

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吧.^_^

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

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

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

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats