effective java之类和接口

类和接口

类和成员被外界可访问的最小化

设计良好的模块

这个模块对于外部其他的模块,是否隐藏其内部数据和实现的细节。设计良好的模块会隐藏所有的实现细节,把他的api与具体的实现清晰的隔离

模块之间只通过他们的API进行通信,一个模块不需要知道其他模块的内部实现情况,这被称之为信息隐藏或者封装

信息隐藏或者封装的好处

  1. 有效的解除各个模块之间的耦合关系,使得这些模块可以独立的开发、测试、优化,使用或者维护
  2. 模块之间可以并发开发,加快系统开发速度
  3. 方便后期的维护拓展,加强对系统性能控制
  4. 提高程序模块的可移植性,模块复用

尽可能的使每个类和和成员不被外界访问

如果一个包级私有的顶层类(没有内部类)或者接口,只是在某一个类的内部使用到,就应该考虑让它成为唯一使用它的那个类的私有内部类,这样的话可以将访问的范围从包中的所有类到当前使用它的那个类

比如,在listview中,adapter对应的内部静态的ViewHolder
button的点击事件OnClickListener()
各种对话框中内部接口监听器

在类中使用方法访问域而非直接访问公有域

通俗的说,就是不要使用对象.成员变量,如user.name,
而应该是对象.成员变量方法,如user.getName()

//point类是供外部使用的
public class Point(){
    public double x;
    public double y;
}

以上类特点:
1. 数据域(也就是成员变量)可变的,是可以直接被访问,没有提供封装访问方法
2. 如果不更改API,无法改变该类的数据表示法,也无法添加任何的约束
3. 被外部访问时,无法进行辅助功能,比如有效性合法性检查

如果类可以在其所在包的外部被其他类访问,就需要考虑提供方法访问该类内的域,这样可以保留以后在该类内部修改表示数据的灵活性,假如公有类的数据域暴露,可能涉及多处地方引用,要想再改变会很麻烦

通常这种情况,会提供访问成员变量的getXX方法,改变成员变量的setXX方法,也就是setter和getter

然而,如果被访问类是包级别或者是某个类的内部类,那么直接暴露成员变量做法是可以得,因为其作用的范围是外部类,也没有超出该类所在的包,直接修改数据的表示方式是可以接受的

比如,在Adapter的内部,一般存在对应的ViewHolder,其表示数据方式就很简单

static class ViewHolder{
    ImageView iv;
    TextView tv;
}

公有类(可被外部类访问)永远都不能暴露可变的成员变量
公有类(可被外部类访问)暴露不可变的成员变量是可以的,其危害相对较小

复合优先于继承

什么是复合?

不创建现有类A的子类,而是创建一个新类B,新类B中有成员变量,该变量的引用指向现有的类A,这种设计叫做复合,因为现有的类A变成了新类B中的一个组件
新类B中的每个方法,可以调用现有类A中的方法,也可以有自己的方法。新类B中的方法称之为转发方法

如何确认是继承?

只有当子类真正是超类的子类型的时候,才适合用继承。
对于两个类A和B,只有两者之间确实存在“is-a”关系时,类B才应该继承类A。需要注意的是,在继承前先确认类B是否是类A的子类,如果确认,则可以继承,如果不确认,则不要继承。千万不要为了使用某个方法而去继承

如果是为了使用现有类的方法,可以使用继承来新建类,但一旦使用继承,现有类新增的方法就会暴露给新建子类。如果既想使用现有类的方法,又希望在现有类增加新方法情况下不暴露给新建类,可以使用复合

注意:如果在可以使用复合的情况下,使用继承,则会暴露实现细节,同时,如果父类中存在某些缺陷设计,会通过继承将缺陷传递给其子类。使用复合方式,新建类持有现有类的引用,可以使用现有类的方法,即便现有类增加自身的方法,也不会暴露给新建类

要么为继承而设计,提供文档说明,要么禁止继承

如何确定要暴露哪些受保护的方法或者受保护的变量?

  1. 没有捷径
  2. 原则上尽量少暴露受保护的成员方法或者变量
  3. 编写子类,一般来说,编写3个子类,即可通过子类检查受保护的成员,对遗漏的成员及时补上,对没有使用到的受保护成员,考虑是否进行私有

接口优于抽象类

接口中定义的是方法,但具体的方法交由子类实现的。

在现实开发中,因为功能模块拓展,需要增加接口方法,所有的实现子类都不得不对新增的方法进行实现。那么问题来了,我们并不希望所有的子类对新增方法实现,又该如何做呢?如下,

通过对每个接口提供一个抽象的骨架实现类,把接口和抽象类的有点进行结合。

接口中定义的仍然是方法,骨架实现类接管所有与接口实现相关的工作,这样做,可以保护子类不受破坏

对于没有从骨架实现类继承,而是直接实现接口方法的子类,自然会受到破坏

注意:对接口的设计需要非常谨慎,接口一旦被公开发行,并且广泛实现之后,再想改变接口是很痛苦的事情。无论接口有小的瑕疵还是大的缺陷,都会直接影响用户

原则:
1. 接口设计完成,就需要考虑是否提供骨架实现类
2. 接口的公有方法,需要谨慎的设计,并使用多个实现类测试,确保公有方法是合理的

接口只用于定义类型

接口的实现类,是对接口中方法生命的具体体现,通过接口类型,可以调用其实现类中的方法

接口中应该避免定义常量,如果子类实现常量接口,未来子类不需要这些常量时,反而依然实现接口,有些大才小用

如何确定常量定义的位置

如果常量用在某个具体的类或者接口中,就在该类中定义常量

如果常量用在多个具体类中,就需要单独创建一个类

类层次优先于标签类

出现在switch或者if语句中,通过不同的条件匹配不同的对象

优先考虑静态成员类

什么是嵌套类?

定义在一个类内部的类,该类的存在是为外部的类提供服务

嵌套类如果被用在其他类中,该类就应该是顶层类,换句话,该类需要抽出来单独定义

嵌套类的类别

静态成员类、内部类(非静态成员类、匿名类、局部类)

静态成员类,可以访问外围类的所有成员,是外围类的一个静态成员。如果静态成员类被声明为私有的,只能在外部类的内部进行访问

静态成员类,是一种公有的辅助类,与外部类一起使用才有意义

非静态成员类的每个实例都隐藏着与外围类的一个外围实例相关联,如果嵌套类的实例可以在外围类是的实例之外独立存在,这个嵌套类就必须是静态成员类。没有外围类的实例,想要获取非静态成员类的实例是不可能的

当非静态成员类的实例被创建的时候,它和外围类的实例之间的关系就被建立。具体点,挡在外围类的某个具体的方法内部调用非静态成员类的构造函数时,这种关系自动建立。非静态的内部类持有了外部类的引用。

如果声明的成员类不要求访问外围实例,就需要static修饰符放在它的声明中,使之成为静态成员类,而不是非静态成员类。如果省略static修饰符,每个实例都将包含一个额外的指向外部对象的引用。保存这份对象,需要消耗时间和空间,

私有静态成员类的一种常见的用法是用来代表外围类所代表的对象的组件

匿名类的常见用法

  1. 动态的创建函数对象

  2. 创建过程对象,比如Thread、Runnable、TimerTask

  3. 在静态工厂方法内部

猜你喜欢

转载自blog.csdn.net/rockykou/article/details/69488405
今日推荐