接口
接口的概念
接口中的所有方法都自动是public方法。因此,在接口中声明方法时,不必提供关键字public。
接口的属性
接口不是类。具体来说,不能用new运算符实例化一个接口:
1 | x =new Comparable(...);/erron |
不过,尽管不能构造接口的对象,却能声明接口的变量:
1 | Comparable x;//ok |
接下来,如同使用instanceof检查一个对象是否属于某个特定类一样,也可以使用instanceof检查一个对象是否实现了某个特定的接口:
1 | if(anObject instanceof Comparable){...} |
与建立类的继承层次一样,也可以扩展接口。。这里允许有多条接口链,从通用性较高的接口扩展到专用性较高的接口。例如,假设有一个名为Moveable的接口:
1 | public interface Moveable |
然后,可以假设一个名为Powered的接口扩展了以上接口:
1 | public interface Powered extends Moveable |
虽然在接口中不能包含实例字段,但是可以包含常量。
解决默认方法冲突
如果先在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义同样的方法,会发生什么情况?java的规则如下:
(1)超类优先。如果超类提供了一个具体方法,同名而且参数相同的默认方法会被忽略。
(2)接口冲突。如果一个接口提供了一个默认方法,另一个接口1提供了一个同名而且参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法来解决冲突。
回调
回调是一种常见的程序设计模式。在这种模式中,可以指定某个特定事件发生时应该采取的动作。
内部类
内部类是定义在另一个类中的类。使用的原因主要有两点:
(1)内部类可以对同一个包中的其他类隐藏。
(2)内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据。
代理
利用代理可以在运行时创建实现了一组给定接口的新类。只有在编译时期无法确定需要实现哪个接口时才有必要使用代理。对于编写应用程序的程序员来说,这种情况很少见,所以如果对这种高级技术不感兴趣,可以跳过本节内容。不过,对于某些系统应用程序,代带来的灵活性可能十分重要。
何时使用代理
假设你想构造一个类的对象,这个类实现了一个多个接口,但是在编译时你可能并不知道这些接口到底是什么。这个问题确实有些难度。要想构造一个具体的类,只需要使用 newInstance方法或者使用反射找出构造器。但是,不能实例化接口。需要在运行的程序中定义一个新类。
为了解决这个问题,有些程序会生成代码,将这些代码放在一个文件中,调用编译器, 然后再加载得到的类文件。很自然地,这样做速度会比较慢,并且需要将编译器连同程序 一起部署。而代理机制则是一种更好的解决方案。代理类可以在运行时创建全新的类。这样的代理类能够实现你指定的接口。具体地,代理类包含以下方法:
●指定接口所需要的全部方法。
●object类中的全部方法,例如, toString、 equals等。
不过,不能在运行时为这些方法定义新代码。实际上,必须提供一个调用处理器。调用处理器是实现了 InvocationHandler接口的类的对象。这个接口只有一个方法:
1 | Object invoke(Object proxy, Method method, Object[] args) |
无论何时调用代理对象的方法,调用处器的 invoke方法都会被调用,并向其传递Method对象和原调用的参数。之后调用处理必须确定如何处理这个调用。
创建代理对象
要想创建一个代理对象,需要使用 Proxy类 newProxyInstance方法。这个方法有三个参数:
●一个类加载器。作为java安全模型的一部分,可以对平台和应用类、 从因特网下载的类等使用不同的类加载器。
●一个 Class对象数组,每个元素对应需要实现的各个接口。
●一个调用处理器。
还有两个需要解决的问题。如何定义处理器?另外,对于得到的代理对象能够做些什么?当然,这两个问题的答案取决于我们想要用代理机制解决什么问题。使用代理可能出于很多目的,例如:
●将方法调用路由到远程服务器。
●在运行的程序中将用户界面事件与动作关联起来。
●为了调试,跟踪方法调用。