三、接口优于抽象类

java提供两种机制,可以用来定义一个允许多个实现的类型:接口和抽象类。由于java只允许单继承,所以,抽象类作为类型定义受到了极大的限制。

已有的类可以很容易被更新,以实现新的接口。你所需要做的是:增加要求的方法,如果这些方法原先还不存在的话;然后在类的声明上增加一个implements子句。

接口是定义mixin(混合类型)的理想选择。一个mixin是指这样的类型:一个类除了实现它的”基本类型(primary type)"之外,还可以实现这个mixin类型,以表明它提供了某些可供选择的行为。

例如,Comparable是一个mixin接口,它允许一个类表明它的实例可以与其他的可相互比较的对象进行排序操作,这样的接口之所以被称为mixin,是因为它允许可选的功能可被混合到一个类型的基本功能中。抽象类不能被用于定义mixin,同理因为单继承原因。

接口使得我们可以构造出非层次结构的类型框架。类型层次对于组织某些事物是非常合适的,但是其他有些事物并不能被整齐地组织成一个严格的层次结构。

例如,singer(歌唱家),另一个接口代表一个songwriter(作曲家):

public interface Singer{
AudioClip Sing(Song s);
} public interface Songwriter{
Song compose(boolean hit);
}

接口使得安全性地增强一个类的功能成为可能,做法通过介绍的包装类(WrapperClass)模式。如果你用抽象类来定义类型,那么这样就使得程序员除了使用继承的手段来增加功能之外,没有别的选择途径。这样得到的类与包装类相比,功能更差,也更加脆弱。

可以把接口和抽象类的优点结合起来,对于你期望导出的每一个重要接口,都提供一个抽象的骨架实现(skeletal implementation)类。在这里接口的作用任然是定义类型,但是骨架实现类负责所有与接口实现相关的工作。骨架实现被称为AbstractInterface,这里的interface是所实现的接口的名字。

例如,Collections Framework为每个重要集合接口都提供了一个骨架实现,包括AbstractCollection为Collection接口、AbstractSet(为set接口)、AbstractList(为list接口)和AbstractMap(为Map接口).

如果设计合理的话,利用骨架实现,程序员可以很容易的提供他们自己的接口实现。例如,下面是一个静态工厂方法,它包含一个完整的、功能全面的List实现:

//List adapter for int array
static List intArrayAsList(final int[] a){
if(a==null)
throw new NullPointerException(); return new AbstractList(){
public Object get(int i){
return new Integer(a[i]);
} public int size(){
return a.length;
} public Object set(int i,Object o){
int oldVal = a[i];
a[i] = ((Integer)o).intValue();
return new Integer(oldVal);
}
}
}

这个例子是一个Adapter,它使得一个int数组可以被看做一个Integer实例列表。由于int值和Integer实例之间来回转换开销,它的性能不会非常好。注意,这个例子只提供了一个静态工厂,并且这个类是一个可被访问的匿名类(anonymous class),它被隐藏在静态工厂的内部。

骨架实现的优美之处在于,它们为抽象类提供了实现上的帮助,但又没有强加“抽象类被用作类型定义时候”所特有的严格限制。对于一个接口的大多数实现来讲,扩展骨架实现是一个很显然的选择,但也确实只是一个选择而已。如果一个预先已经定义好的类无法扩展骨架实现类,那么,这个类总是可以手工实现这个接口。尽管如此,骨架实现类仍然能够有助于接口的实现。实现了这个接口的类可以把对于接口的方法的调用,转发到一个内部私有的实例上,而这个内部私有类扩展了骨架实现类。这项技术被称为模拟多重继承(simulated multiple inheritance),这项技术具有多重继承的绝大多数优点,并且避免了相应的缺陷。

嵌套类--优先考虑静态成员类

嵌套类(nested class)是指被定义在另一个类的内部的类,嵌套类存在的目的应该只是为它的外围类提供服务。如果一个嵌套类将来可能会用于其他的某个环境中,那么它应该是顶层类(top-level class).

嵌套类有四种:

  • 静态成员类(static member class);
  • 非静态成员类(nonstatic member class);
  • 匿名类(anonymous class);
  • 局部类(local class);

Member Types(成员类型)即作为外部类的一个成员存在,与外部类的属性、方法并列同级的类型。成员内部类中不能定义静态变量,但可以访问外部类的所有成员.

除了第一种,其他三种都被称为内部类(inner class),那什么时候使用哪种嵌套类呢?为什么这样做呢?

静态成员类是一种最简单的嵌套类。最好把它看做是一个普通的类,只是碰巧被声明在另一个类的内部而已。它可以访问外围类的所有成员,包括那些声明为私有的成员。静态成员类是外围类的一个静态成员,与其他的静态成员一样,也遵守同样的可访问性规则。如果它被声明为私有的,那么它只能在外围类的内部才可以被访问等等。

静态成员类的一个通常用法是作为公有的辅助类,仅与它的外部类一起使用时才有意义。

非静态内部类nested inner class:

内部类隐含有一个外部类的指针this,因此,它可以访问外部类的一切资源(当然包括private)
外部类访问内部类的成员,先要取得内部类的对象,并且取决于内部类成员的封装等级。
非静态内部类不能包含任何static成员.

匿名类的适用性有一些限制。

  1. 因为它们同时被声明和实例化,所以匿名类只能被用在代码中它将被实例化的那个点上。
  2. 因为匿名类没有名字,所以在它们被实例化之后,就不能够再对它们进行引用,如果不是这种情况就不能使用匿名类。
  3. 因为匿名类出现在表达式的中间,所以它们应该非常简短,可能是20行或者更少,太长的匿名类会影响程序的可读性。

匿名类常用用法:

A、匿名类的一个通常用法是创建一个函数对象(function object),比如Comparator实例。排序:

//Typical use of an anonymous class
Arrays.sort(args,new Comparator(){
public int compare(Object o1,Object o2){
return ((String)o1.length()-((String)o2).length();
}
});

B、匿名类的另一个常用的用法是创建一个过程对象(process object),比如Thread、Runnable或者TimerTask实例。

C、还有一个用法是在一个静态工厂方法的内部(参考IntArrayAsList方法)。

D、在复杂的类型安全枚举类型(它要求为每个实例提供单独的子类)中,用于公有的静态final域的初始化器中。

局部类是四种嵌套类中最少使用的类。在任何“可以声明局部变量”的地方,都可以声明局部类,并且局部类也遵守同样的作用域规则。即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。
注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。

public class Outer {
private int s = 100;
private int out_i = 1;
public void f(final int k){
final int s = 200;
int i = 1;
final int j = 10;
class Inner{ //定义在方法内部
int s = 300;//可以定义与外部类同名的变量
//static int m = 20;//不可以定义静态变量
Inner(int k){
inner_f(k);
}
int inner_i = 100;
void inner_f(int k){
System.out.println(out_i);//如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量
System.out.println(k);//*****可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的*****
//System.out.println(i);
System.out.println(s);//如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量
System.out.println(this.s);//用"this.变量名" 访问的也是内部类变量
System.out.println(Outer.this.s);//用外部"外部类类名.this.变量名" 访问的是外部类变量
}
}
new Inner(k);
} public static void main(String[] args) {
//访问局部内部类必须先有外部类对象
Outer out = new Outer();
out.f(3);
}
}

简而言之,共有四种不同的嵌套类,每一种都有自己的用途。

私有成员变量可以被子类继承吗

今天看spring源码,不经意间发现了一问题:在一个抽象类中定义了一个私有成员变量。

仔细想想:抽象类不能被实例化的,只能被子类继承。但是自从学java的继承只有,我们就知道,子类不能继承父类的私有成员变量或方法的。

问题:在该抽象方法中定义这个私有变量有什么用呢?或者说这个私有成员变量再什么地方用得到呢?

所以自己做了一个测试如下:

public abstract class Fatherclass {
private int privatenumber; public int getPrivatenumber() {
return privatenumber;
}
public void setPrivatenumber(int privatenumber) {
this.privatenumber = privatenumber;
}
} public class Childclass extends Fatherclass{ public static void main(String[] args) {
Childclass childclass=new Childclass();
Childclass1 childclass1=new Childclass1();
childclass.setPrivatenumber(125);
System.out.println(childclass.getPrivatenumber());
System.out.println(childclass1.getPrivatenumber());
}
} public class Childclass1 extends Fatherclass { }

运行结果如下:

125
0

通过测试,我们发现子类确实继承了父类的私有属性(也可以说是子类拥有一个属性,继承自父类,但是该属性的访问权限暂时不确定),但是我们无法通过子类直接访问该(继承自父类私有属性)的属性。我暂且该这种属性的访问权限起名为fatherprivate(意为继承自父类私有属性)

转自:https://www.cnblogs.com/yidaijiankuanzhongbuhui/p/8417051.html

Effective java笔记3--类和接口2的更多相关文章

  1. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  2. Java笔记---枚举类和注解

    Java笔记---枚举类和注解 一.枚举类 自定义枚举类 方式一:JDK5.0之前自定义枚举类 class Seasons { //1. 声明Seasons对象的属性 private final St ...

  3. Effective java笔记(三),类与接口

    类与接口是Java语言的核心,设计出更加有用.健壮和灵活的类与接口很重要. 13.使类和成员的可访问性最小化 设计良好的模块会隐藏起所有的实现细节,仅使用API与其他模块进行通信.这个概念称为信息隐藏 ...

  4. Effective java笔记3--类和接口1

    一.使类和成员的可访问能力最小化 要想区别一个设计良好的模块与一个设计不好的模块,最重要的因素是,这个模块对于外部的其他模块而言,是否隐藏了内部的数据和其他的实现细节.一个设计良好的模块会隐藏所有的实 ...

  5. Effective java笔记(二),所有对象的通用方法

    Object类的所有非final方法(equals.hashCode.toString.clone.finalize)都要遵守通用约定(general contract),否则其它依赖于这些约定的类( ...

  6. Java笔记:抽象类、接口

    这篇笔记主要是抽象类和接口,还有简单介绍下三种设计模式:模板模式.工厂模式.命令模式 1.抽象方法和抽象类(1)抽象方法和抽象类都必须使用abstract修饰符来定义,包含抽象方法的类只能被定义成抽象 ...

  7. effective java笔记之单例模式与序列化

    单例模式:"一个类有且仅有一个实例,并且自行实例化向整个系统提供." 单例模式实现方式有多种,例如懒汉模式(等用到时候再实例化),饿汉模式(类加载时就实例化)等,这里用饿汉模式方法 ...

  8. effective java笔记之java服务提供者框架

    博主是一名苦逼的大四实习生,现在java从业人员越来越多,面对的竞争越来越大,还没走出校园,就TM可能面临失业,而且对那些增删改查的业务毫无兴趣,于是决定提升自己,在实习期间的时间还是很充裕的,期间自 ...

  9. Effective Java 第三版—— 20. 接口优于抽象类

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  10. Effective Java 第三版——22. 接口仅用来定义类型

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

随机推荐

  1. css transform旋转属性

    将以下代码复制到本地就可以看到效果: <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  2. vmware增加共享文件夹

    增加共享文件夹 VMWare提供共享文件夹功能.前提是在虚拟机中安装VMware tools 1. 安装VMware tools 会自动在虚拟机中的/media/VMware Tools/中有个压缩包 ...

  3. flask学习(一):环境的安装

    一. 安装python2.7 从python官网下载python2.7的版本 双击python2.7,然后选择安装路径,一直下一步就可以了 设置环境变量,把python和pip的安装路径添加到PATH ...

  4. 【Python】序列的方法

    任何序列都可以引用其中的元素(item). 下面的内建函数(built-in function)可用于列表(表,定值表,字符串) #s为一个序列 len(s) 返回: 序列中包含元素的个数 min(s ...

  5. 【Java】抽象类和接口

    一.抽象类和抽象方法 1.什么是抽象类 普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法.普通方法.static方法.常量和变量等内容. 但是普通类中不能有抽象方法, ...

  6. asp.net获取URL和IP地址

    (转自:http://www.cnblogs.com/JuneZhang/archive/2010/11/26/1888863.html) HttpContext.Current.Request.Ur ...

  7. sql日期函数总结

    sql 时间转换格式 convert(varchar(10),字段名,转换格式)   convert(varchar(10),字段名,转换格式) CONVERT(nvarchar(10),count_ ...

  8. Node net模块与http模块一些研究

    这周遇到一个有意思的需求,端上同学希望通过 socket 传送表单数据(包含文件内容)到 node 端,根据表单里的文件名.手机号等信息将文件数据保存下来.于是我这样写了一下--socket_serv ...

  9. ADO.NET数据库访问技术(转)

    这几天的自学,现在总结一下关于C#中连接数据库的一些知识点: 1.使用Connection连接数据库的步骤: (1).添加命名空间 System.Data.SqlClient(注意:初学者经常会忘记) ...

  10. 序(转) · 为 React 而写 -- 废话比较多, 你也可以说丰满

    流形 2 年前 (废话比较多       从今年开始,就一直在规划技术沉淀这事. 在阿里巴巴工作的这些年,前端团队日益壮大,同时聚集了一帮志趣相投的伙伴. 作为团队负责人,一方面是借团队在技术道路上的 ...