- 接口(interface)技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现,一个类可以实现一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。
- 克隆对象(深拷贝):是指创建一个新对象,且新对象的状态和原始对象的状态相同(当对克隆的对象进行修改时,不会影响原始对象的状态)
- 内部类(inner class):定义在另外一个类的内部, 其中的方法可以访问包含它们的外部类的域【内部类主要用于设计具有相互协作关系的类集合】(在编写处理GUI时间的代码时,使用内部类可以让代码更简洁)
- 代理(proxy):一种实现任意接口的对象【代理是一种非常专业的构造工具,用来构建系统的工具】
1.接口
在Java程序设计语言中,接口不是类,只是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义
Arrays类中的sort方法承诺可以对对象数组进行排序,但是要求满足前提:对象所示的类必须实现Comparable接口
public interface Comparable<T>
{
int compateTo(T oyher);
}
任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是一个T对象,返回一个整型数值
接口中的方法自动属于public,在接口中声明方法时不需要提供关键字public,但是在实现接口时,必须把方法声明为public
(在调用x.compareTo(y)的时候,这个方法必须比较的是两个对象的内容,并返回比较结果)
1.接口中可以定义多个方法,
2.接口中可以定义常量
3.接口中绝不能含有实例域,也不能再接口中实现方法
【可以将接口看成是没有实例域的抽象类】
让类实现一个接口的步骤:
1.将类声明为实现给定的接口
class Employee implements Comparable
2.对类中的所有方法进行定义
class Employee implements Comparable<Employee>
{
public int compareTo(Employee other(或者是otherObject))
{
//Employee other = (Employee)otherobject;进行类型转换总是感觉不爽
return Double.compare(salary,other.salary);
}
}
【静态方法Double.compare,如果第一个参数小于第二个参数,返回负值,相等为0,否则返回正值(注意比较时2个整数(或浮点数)的范围不能过大,避免造成减法运算的溢出,整数的绝对值不能超过(Iterger.MAXVALUE-1)/2)】
Java是一种强类型语言,在调用方法的时候,编译器将会检测这个方法是否存在,每个实现接口的类都必须实现接口中的方法
如果类进行了继承,情况如下:
子类在实现compareTo方法的时候要注意:
1.如果子类之间比较含义不一样,那就属于不同类对象的非法比较,每个compareTo方法在开始时要进行检测if(getClass()!=other.getClass())throw new ClassCastException();
2.如果能够对2个不同的子类对象进行比较,应该在超类提供一个声明为final的compareTo方法
1.接口特性
接口不能构造对象,却能够声明接口变量,接口变量必须引用实现了接口的类对象
Comparable X;
X = new Employee(...);
使用instanceof检查一个对象是否实现了某个接口
if(anObject instanceof Comparable){...}
接口也可以被拓展
public interface Powered extends Moveable
{
double SPEED_LIMT = 95;
}
在接口中不能保护实例域或静态方法,但是可以包含常量
【接口中的方法自动变为public,接口中的域自动变为public static final】
2.接口和抽象类
每个类只能有一个超类,却可以实现很多接口,使用逗号将实现的接口进行分隔
2.对象克隆
当拷贝一个变量时,原始变量和拷贝变量引用同一个对象,改变一个变量所引用的对象将会对另一个变量产生影响
clone方法是Object类的一个protected方法
默认的克隆操作时浅拷贝,并没有克隆包含在对象中的内部对象(如果要克隆的对象所有的数据域都属于数值或基本类型,没有问题,如果包含子对象引用,那么拷贝的结果会使得2个域引用同一个子对象)
对每个类进行如下判断:
1.默认的clone方法是否满足要求
2.默认的clone方法是否能够通过调用可变子对象的clone得到修补
3.是否不应该使用clone
实际上选项3是默认的,如果选择1或2,类必须:
1.实现Cloneable接口
2.使用public访问修饰符重新定义clone方法(子类只能调用protected的clone方法克隆自己,重新定义实现public才能让所有的方法克隆对象)
即使默认拷贝能够满足需求,也应该实现Cloneable接口,将clone重新定义为public,并调用super.clone(),为了实现深拷贝,必须克隆所有可变的实例域:
class Employee implements Cloneable
{
...
public Employee clone() throw CloneNotSupportedException
{
Employee cloned = (Employee) super.clone();
cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
只要在clone中含有没有实现Cloneable接口的对象,Object类的clone方法就会抛出CloneNotSupportedException异常
【所有的数组类型都包含一个clone方法,这个方法被设为public,而不是protected】
int[] number = {2,3,4,5,6};
int[] cloned = number.clone();
cloned[4] = 7;//不会改变number[4]的值
3.接口与回调
回调(callback):是一种常见的程序设计模式,在这种模式中可以指出某个特定时间发生时应该采取的动作
例如:可以指出在按下鼠标或者某个菜单项时采取什么行动
4.内部类
内部类(inner class)是定义在另一个类中的类
使用内部类的主要原因:
1.内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
2.内部类可以对同一个包中的其他类隐藏起来
当想要定义一个回调函数且不想编写大量代码是,使用匿名(anonymous)内部类比较便捷
内部类的对象有一个隐式引用,它引用了实例化该内部对象的外部类对象,通过这个指针,可以访问外部类对象的全部状态(static内部类没有这种附加指针)
1.使用内部类访问对象状态
public class Talking
{
private int inerval;
private boolean beep;
public Talking(int interval,boolean beep){...}
public class Time implements ActionListerner{...}
}
这里的Time类位于Talking类内部,这并不意味着每个Talking都有一个Time实例域【Time对象是由Talking类的方法构造的】
从传统意义上讲,一个方法可以引用调用这个方法的对象的数据域,内部类既可以访问自身的数据域,也可以访问创建它的外部类对象的数据域(内部类对象总有一个隐式引用,它指向了创建它的外部类对象,这个引用在内部类的定义中是不可见的)
如果内部类没有定义构造器,编译器为这个类生成一个默认的构造器:
public Time(Talking clock)
{
outer = clock;//outer不是Java的关键字
}
外部类的引用在构造器中设置,编译器修改了所有的内部类的构造器,添加一个外部类引用的参数
内部类可以声明为私有的,这样只有它的外部类才能够构造内部类的对象,只有内部类可以是私有的,常规类只可以具有包可见性,或公有可见性
2.内部类的特殊语法规则
使用外部类引用的正规语法:OuterClass.this
,表示外部类的引用
例如:
public void actionPerformed(ActionEvent event)
{
...
if(Talking.this.beep)Toolkit.getDefaultToolkit().beep();
}
可以采用如下语法格式明确地编写内部对象的构造器:outerObject.new InnerClass(construction parameters)
例如:
ActionListener listerner = this.new Time();
通过显示地命名将外部类引用设置为其他的对象(如果内部类是共有的)
例如:
Talking jabber = new Talking(1000,true);
Talking.Time listener = jabber.new Time();
在外部类的作用域之外,可以这样引用内部类:OuterClass.InnerClass
3.内部类是否有用、必要和安全
内部类是一种编译器现象,与虚拟机无关,编译器将会把内部类翻译成$(美元符号)分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知
内部类可以访问外部类的私有数据,内部类拥有访问特权,比常规类功能强大
如果内部类访问了私有数据域,就有可能通过附加在外部类所在包中的其他类访问这些私有数据域
4.局部内部类
在一个方法中定义局部类:
public void start()
{
class Time impleements ActionListener
{
public void actionPerformed(actionEvent event)
{
Data now = new Date();
System.out.println("...");
if(beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new Time();
Timer t = new Timer(interval,listener);
t.start();
}
局部类不能用public或private访问修饰符进行声明【它的作用域被限定在声明这个局部类的块中】
局部类有一个优势,对外部世界可以完全地隐藏起来,除这里的start方法之外没有方法指导局部类的存在
5.由外部方法访问final变量
与其他内部类相比,局部类有一个优点,不仅能够访问包含它们的外部类,还可以访问局部变量(这些局部变量必须被声明为final)
例如:
public void start(int interval,final boolean beep)
{
class Time impleements ActionListener
{
public void actionPerformed(actionEvent event)
{
Data now = new Date();
System.out.println("...");
if(beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new Time();
Timer t = new Timer(interval,listener);
t.start();
}
- Talking类不在需要存储实例变量beep,它只是引用start方法中的beep参数变量
上面例子中的局部类控制流程:
1.调用start方法
2.调用内部类Time的构造器,一边初始化对象变量listener
3.将listenet引用传递给Timer构造器,定时器开始计时,start方法介结束,此时start方法的beep参数变量不复存在
4.actionPerformaned方法执行if(beep)…
为了让actionPerformaned方法能够工作,Time类在beep域释放之前将beep域用start方法的局部变量进行备份
编译器必须检测对局部变量的访问,为每一个变量建立相应的数据域,并将局部变量拷贝到构造器中,以便这些数据域初始化为局部变量的副本
final关键字可以拥有局部变量,实例变量和静态变量(在创建这些变量之后,只能够对它赋值一次,此后不能再修改)
final int[] counter = new int[];
数组变量被声明为final,但是仅仅表示不可以让它引用另外一个数组,数组中的数据元素可以自由的改变
6.匿名内部类
如果只创建这个类的一个对象,就不必命名了,这种类称为匿名内部类
new SuperType(construction parameters)
{
inner class methods and data
}
SuperType可以是ActionListener这样的接口,于是内部类就要实现这个接口,SuperType可以是一个类,于是内部类就要扩展那这个类
由于构造器的名字必须与类名相同,而匿名类没有类名,所以不能有构造器,,应将构造器参数传递给超类,尤其在内部类实现接口的时候,不能有任何构造参数
new InterfaceType()
{
methods and data
}
注意:调用getClass方法是调用的是this.getClass(),而静态方法没有this,此时可以new Object(){}.getClass().getEnclosingClass()
new Object(){}会建立Object的一个匿名子类的一个匿名对象,getEnclosingClass()则得到其外部类,也就是包含这个静态方法的类
7.静态内部类
有时候,使用一个内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外部类对象,为此,可以将内部类声明为static,以便取消产生的引用
class ArrayAlg
{
public static class Pair
{
...
}
...
}
只有内部类可以声明为static,通过ArrayAlg.Pair 调用;静态内部类的对象除了没有生成它的外部类对象的引用特权外,与其他所有内部类一样
声明在接口中的内部类自动成为static和public类