《Effective java》笔记(第二版) --第四章(13-16)

个人笔记

第四章 ——类和接口


第十三条: 使类和成员的可访问性最小化
尽可能使每个类或者成员不被外界访问

私有的东西将不被智能提示和导出到API文档中,这是内部的实现,而不是给外部暴露用,为了不使用户(使用这个类的人)疑惑和增加类的可变性,类暴露的接口,不是private修饰的都是可以被使用的,那么如果别人使用了你不该使用的部分,而你这个类下个版本更新,却删除了这个方法,将不止导致用户对于你的类的厌恶,而且会破坏原有的代码,所以就尽量私有化
类的可访问性,缩小到最小,除非有必须暴露这部分的理由,否则就该是private,Effective c++中也提到过

实例域绝不能是公有的(见14条)
1: new MyClass().value;
2: MyClass().getValue();
2的方式比起1更加优雅,也增加了随时修改内部却不影响外部使用的好处,并且,如果用户能直接访问非final的value的话,那么就能进行修改,进行赋值,即使你并不希望用户这样做
特殊情况,这些数据域,也就是实例域,如果是static final的,用于和这个类所配合的FLAG,那么就可以去暴露它 
第十四条: 在公有类中使用访问方法而非公有域

考虑下面的代码

class MyClass{
    public int value;
}

用new MyClass().value访问有很多的不好,用户使用了这些API就会很难去更改,如果下个版本要改成

class MyClass{
public int mValue;
}

用户的所有使用这个实例域,这个暴露出来的数据域的代码全部都必须修改,但是,如果你用方法去访问就能避免这种不优雅的写法

class MyClass{
private int value;//尽量不暴露多余的东西
public int getValue(){
return value;
    }
}

这样无论你怎么修改value这个实例域,都不会对外部的用户造成影响

如果类是包级私有的,或者是私有的嵌套类,直接暴露它的数据域并没有本质的错误

解释:
包级私有的意思嘛,也就是一个文件夹中,你的package中,src目录下私有的类,暴露这样的数据域的话,并没有很大问题,比如一个平面坐标类

class Map{
...
private class Address{
public Address(int X, int Y){this.X = X; this.Y = Y;}
public int X;
public int Y;
public int getX(){return X;}
public int getY(){return Y;}
}
...
}

那么这样访问

//内部
Address address =  new Address(100,800);
address.X; address.Y;//的访问的方式
//比起
address.getX();..
//显然能写更少的代码,因为是私有,或者只是供外部公有类使用的的嵌套类,这样做并没有什么错,
//建议两种方式都提供,如果你愿意的话,这样用起来更方便,Android也就是这样做的,更偏向用户的舒适么,反正如果打算暴露的话,仔细考虑后果,再做决定


第十五条: 使可变性最小化

String为什么是final修饰的类?

遵循5条原则

1,不要提供任何会修改对象状态的方法

直接的返回class的引用

2,保证类不会被扩展

假设我有一个类代表所有的妹子,它是可以被继承的


class meizi {
    private String say = "hello meizi";

    public void say() {
        System.out.println(say);
    }

再来一个子类

class loli extends meizi {
    public void say() {
        System.out.println("妖妖灵");
    }

因为子类包含父类的暴露接口,那么子类就能被替换为父类,如果存在恶意的用户,本来你只是想向妹子说声hello

    public static void main(String[] args) {
        meizi m = new meizi();
        m = new loli();// 有人从中作梗
        m.say();
    }

结果: 妖妖灵
然后就会被送去喝茶,这样不好,所以这个要保证不被请去喝茶,这个类就该被final修饰为不能继承,保证了一些接口不被覆写(Override)

final class meizi {...}

这样就不会有可恶的loli了,想起来了没有,和Effective c++的对const的条款一样

3,使得所有的域都是final的

已经知道
根据Java Language Specification中对Java内存模型的定义, JVM中存在一个主内存(Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。每个线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。根据上述内存模型的定义,要在多个线程间安全的同步共享数据就必须使用锁机制,将某线程中更新的数据从其工作内存中刷新至主内存,并确保其他线程从主内存获取此数据更新后的值再使用。原文
而且,如果一个指向新创建实例的引用在缺乏同步机制的时候,能够
保证在线程之间传递就能保证正确的行为,参考java内存模型


4,使得所有的域都成为私有

5,确保对于任何可变组件的互斥访问

既然是想设计一个不可变的类,那么除了防止域被直接访问之外,还得从不返回直接的引用,在构造器,或者各种setter的时候,不要给用户一个持有引用的机会,好比这种用法

MyClass(Date cur){
this.cur = new Date(cur);
}

这样用户就没有机会做恶意改变了


第十六条: 复合优先于继承

继承代表着类的作者承认子类 is - a 父类的关系,在后续的API中能够这样使用,而 复合——将其他东西作为这个类的成员来调用只是承诺 has - a的关系,继承会破坏封装,导致父类和子类的关系耦合,修改也麻烦,一改就是两个或者更多…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值