接口和内部类

来源:互联网 发布:linux编程获取cpu温度 编辑:程序博客网 时间:2024/06/05 08:16
  1. 接口(interface)技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现,一个类可以实现一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。
  2. 克隆对象(深拷贝):是指创建一个新对象,且新对象的状态和原始对象的状态相同(当对克隆的对象进行修改时,不会影响原始对象的状态)
  3. 内部类(inner class):定义在另外一个类的内部, 其中的方法可以访问包含它们的外部类的域【内部类主要用于设计具有相互协作关系的类集合】(在编写处理GUI时间的代码时,使用内部类可以让代码更简洁)
  4. 代理(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类

0 0
原创粉丝点击