接口:主要用于描述类具有什么功能,而不是给出每个功能的具体实现。
一个类可以实现一个或多个接口,并在需要接口的地方,随时使用实现了相应的接口的对象。
克隆对象(深拷贝):指创建一个新对象,且新对象的状态与原始对象的状态相同
当对克隆的新对象进行修改时,不会影响原始对象的状态。
内部类机制:内部类定义在另一个类的内部,其中的方法可以访问包含它们的外部类的域,主要用于设计具有相互协作关系的类集合。
一、接口
在Java程序设计语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。
1>接口中的所有方法自动地属于public,因此,在接口中声明方法时,不必提供关键字public。
2>接口中可以定义常量,不能含有实例域,也不能在接口中实现方法。
3>类实现接口的步骤:
a>将类声明为实现给定的接口。 关键字 implements
b>对接口中的所有方法进行定义。
注:
i>在接口声明中,方法不需要声明为public,因为在接口中的所有方法都自动地是public。不过,在实现接口时,必须把方法声明为public,否则,编译器将认为访问属性为包默认,即类的默认访问属性,之后编译器就会给出试图提供更弱的访问权限的警告信息。
ii>如果需要调用底层的排序服务必须让它实现Comparable接口的compareTo方法。
例如:使用Arrays.sort()方法对数组进行排序,本质上必须对两个对象进行比较,而比较的方式就是通过调用Comparable接口的compareTo方法。所以需要实现Comparable接口,如果直接创建compareTo方法,而不继承接口的话,Java也无法调用。
( 1 )接口的特性
1>接口不是类,不能使用new运算符实例化一个接口。
2>不能构造接口的对象,却能声明接口的变量,但是接口变量必须引用实现了接口的类对象。
3>如同使用instanceof检查一个对象是否属于某个特定类一样,也可以使用instance检查一个对象是否实现了某个特定的接口。
4>接口可以被扩展,使用extends关键字类扩展某个接口。
5>接口中不能包含实例域或静态方法,但却可以包含常量。
6>接口中的方法都自动被设置为public,接口中的域将被自动设为public static final。
7>每个类只能拥有一个超类,但却可以实现多个接口。
( 2 )接口与抽象类
Java不支持多继承,因为多继承会让语言本身变得非常复杂,效率也会降低。
接口可以提供多重继承,同时能避免多重继承的复杂性和低效性。
二、对象克隆
当拷贝一个变量时,原始变量与拷贝变量引用同一个对象,改变一个变量所引用的对象将会对另一个变量产生影响。
使用clone方法可以创建一个对象的新的copy,最初状态与原来的变量引用的对象一样,而且可以各自改变自己的状态。
1>clone方法时Object类的一个protected方法,用户编写的代码中不能直接调用。
2>默认的克隆操作是浅拷贝,数据域是数值或基本类型,没有问题,但是如果包含子对象的引用,拷贝的结果会使两个域引用同一个子对象。
因此需要重新定义clone方法,以便实现克隆子对象的深拷贝。
重新定义,类必须:
a>实现Cloneable接口;
b>使用public访问修饰符重新定义clone方法。
注:
i>在这里Cloneable接口的出现与接口的正常使用没有任何关系,尤其是,它并没有指定clone方法。该接口在这里是作为一个标记,表明类设计者要进行克隆处理。如果不继承该接口,就会产生一个已检查异常。
ii>标记接口没有方法,使用它的唯一目的是可以用instanceof进行类型检查。
iii>只要在clone中含有没有实现Cloneable接口的对象,Object类的clone方法就会抛出一个CloneNot-SupportException异常。
iv>必须谨慎地实现子类的克隆,而且克隆并不像人们想象的普遍。在标准类库中,只有不到5%的类实现了clone。
三、接口与回调
回调是一种常见的程序设计模式,在这种模式中,可以指出某个特定事件发生时应该采取的动作。
例如:
package com.interfaces; /** @version 1.00 2000-04-13 @author Cay Horstmann */ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.Timer; // to resolve conflict with java.util.Timer public class TimerTest { public static void main(String[] args) { ActionListener listener = new TimePrinter(); // construct a timer that calls the listener // once every 10 seconds Timer t = new Timer(10000, listener); t.start(); JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } } class TimePrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); Toolkit.getDefaultToolkit().beep(); } }
四、内部类
内部类:定义在另一个类中的类。
使用内部类的原因:
1>内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
2>内部类可以对同一个包中的其他类隐藏起来。
3>当想定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
( 1 )使用内部类访问对象状态
内部类即可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。(在调用内部类的构造函数时,隐式的将外围类的引用作为传递)
( 2 )匿名内部类
创建一个实现接口的类的新对象,需要实现接口中的方法定义在括号{}内。
语法格式为:
其中SuperType是接口,在{}内实现相应的方法。这样内部类就实现了这个接口。new SuperType(construction parameters){ inner class methods and data }
SuperType也可以是一个类,于是内部类就扩展它。
( 3 )静态内部类
有时候,使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围类对象。
为此,可以将内部类声明为static,以便取消产生的引用。
( 4 )代理proxy
Java SE1.3新增加的特性。利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。
对于应用程序设计人员来说,遇见这种情况的机会很少,但是对于系统程序设计人员来说,代理带来灵活性却十分重要。
1>代理类可以在运行时创建全新的类,这样代理类能够实现指定的接口,而且具有下列方法:
a>指定接口所需要的全部方法。
b>Object类中的全部方法。
2>不能再运行时定义这些方法的新代码,而是要提供一个调用处理器。调用处理器是实现了InvocationHandler接口的类对象,接口中只有一个方法:Object invoke(Object proxy,Method method,Object[] args)
3>无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出处理调用的方式。
创建代理对象,需要使用Proxy类的newProxyInstance方法,有三个参数:
a>一个类加载器
b>一个Class对象数组,每个元素都是需要实现接口
c>一个调用处理器
4>代理类的特性
注:就目前而言,对于代理的部分感觉不理解,目前不常使用,所以暂且搁置,以后再详细了解。a>代理类在程序运行过程中创建的,一旦,就变成了常规类,与虚拟机中的任何其他类没有什么区别。
b>所有的代理类都扩展于Proxy类,一个代理类只有一个实例域------调用处理器,它定义在Proxy的超类中。
为了履行处理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。