您的位置:首页 > 编程语言 > Java开发

Java(六)接口与内部类

2014-02-23 20:29 253 查看

1、接口

     接口主要用来描述类具有什么功能,并且不给出每个功能的具体实现,接口不是类。

     一个类可以实现(implement)一个或多个接口(interface),并在需要接口的地方,随时使用实现了相应接口的对象。

     接口中的所有方法自动地属于 public。

     接口绝不能含有实例域,也不能在接口中实现方法。提供实例域和方法实现的任务由实现接口的那个类完成。

     可以将接口看成是没有实例域的抽象类。

     实现一个接口,必须:1)将类声明为实现给定的接口

                                         2)对接口中的所有方法进行定义

     实例:Arrays 类中的 sort 方法可以对对象数组进行排序,但对象所属的类必须实现了 Comparable 接口,如下:

public interface Comparable<T>
{
int compareTo(T other);
}


          
              实现这个接口,必须提供 compareTo(Employee other) 方法。

              实现接口:

class Employee implements Comparable<Employee>
{
//......

public int compareTo(Employee other)
{
if (salary < other.salary) return -1;
if (salary > other.salary) return 1;
return 0;
}

//.....

}


         

             完整程序:

import java.util.*;
public class EmployeeSortTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];

staff[0] = new Employee("Ai",35000);
staff[1] = new Employee("Bi",75000);
staff[2] = new Employee("Ci",38000);

Arrays.sort(staff);

for (Employee e : staff)
System.out.println("name="+e.getName()+",salary="
+e.getSalary());
}
}

class Employee implements Comparable<Employee> //实现接口的类
{
public Employee(String n,double s)
{
name = n;
salary = s;
}

public String getName()
{
return name;
}

public double getSalary()
{
return salary;
}

public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}

public int compareTo(Employee other)
{
if (salary < other.salary) return -1;
if (salary > other.salary) return 1;
return 0;
}

private String name;
private double salary;
}


          运行结果


      



    (1)接口的特性

             接口不是类,尤其不能使用 new 运算符实例化一个接口,但尽管不能构造接口的对象,却能声明接口的变量,接口的变量必须引用实现了接口的类对象:

Comparable x;
x = new Employee(...);


            也可以使用 instance 检查一个对象是否实现了某个特定的接口:if(anObject instanceof Comparable){...}

            接口也可以被扩展(继承)。

            在接口中不能包含实例域或静态方法,但却可以包含常量。

            尽管每个类智能够拥有一个超类,但却可以实现多个接口(使用逗号隔开)。

   (2)接口与抽象类

            为何不直接使用抽象类而使用接口?

                    因为每个类智能扩展于一个类(java不支持C++中的多继承),但每个类可以实现多个接口。

2、对象克隆

     当拷贝一个变量时,原始变量与拷贝变量引用同一个对象,改变引用的对象时,会同步改变两个变量。如想要创建一个

对象的新的 copy 之后,不再相互影响,就需要使用 clone 方法。

     clone 方法是 Object 类的一个 proteced 方法,若子对象可变,则必须重新定义 clone 方法,实现克隆子对象的深拷贝。

     所有的数组类型均包含一个 clone 方法(public),可以利用这个方法创建一个包含所有数据元素拷贝的一个新数组。

     对于每一个类,都需要做出如下判断:

                              1)默认的 clone 方法是否满足要求

                              2)默认的 clone 方法是否能够通过调用可变子对象的 clone 得到修补

                              3)是否不应该使用 clone ,实际上这个选择是默认的,如果要选前两项,类必须:

                                                                           1)实现 Cloneable 接口(一种标记接口)

                                                                           2)使用 public 访问修饰符重新定义 clone 方法。

       实例:

import java.util.*;
public class CloneTest
{
public static void main(String[] args)
{
try
{
Employee original = new Employee("Ai.Public",50000);
original.setHireDay(1991,1,1);
Employee copy = original.clone(); //克隆
copy.raiseSalary(10);
copy.setHireDay(1993,12,31);
System.out.println("original="+original);
System.out.println("copy="+copy);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
}

class Employee implements Cloneable//实现接口
{
public Employee(String n,double s)
{
name = n;
salary = s;
hireDay = new Date();
}

//只要在 clone 中没有实现 Cloneable 接口的对象,Object 类的 clone 方法就会
//抛出一个 ClontNot-SupportException 异常,实现之后,还需声明异常如下:
public Employee clone() throws CloneNotSupportedException
{
Employee cloned = (Employee) super.clone();//调用 Object.clone()

cloned.hireDay = (Date) hireDay.clone();//克隆可变域

return cloned;
}

public void setHireDay(int year, int month, int day)
{
Date newHireDay = new GregorianCalendar(year,month-1,day).getTime();

hireDay.setTime(newHireDay.getTime());
}

public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}

public String toString()
{
return "Employee[name="+name+",salary="+salary
+",hireDay="+hireDay+"]";
}

private String name;
private double salary;
private Date hireDay;
}


                   运行结果:



3、接口与回调

      回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。

      在 java.swing 包中有一个 Timer 类(定时器),使用时要求相应的类实现了 java.awt.event 包的 ActionListener 接口:

 

public interface ActionListener
{
void actionPerformed(ActionEvent event);
}


      java 有函数指针的对应物:Method 对象,但使用比较困难,因此,在任何使用 C++ 函数指针的地方,都应该考虑使用java中的接口

      完整程序:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class TimerTest
{
public static void main(String[] args)
{
ActionListener listener = new TimePrinter();

//构造一个调用 listener 的定时器,10秒钟每次
//Time(int interval,AvtionListener listener):构造
//一个定时器,每隔 interval 毫秒钟通告 listener 一次。
Timer t = new Timer(10000,listener);
t.start();//启动定时器

//显示一个包含一条消息和 OK 按钮的对话框,位于屏幕的中央
JOptionPane.showMessageDialog(null,"Quit program?");
System.exit(0);
}
}

class TimePrinter implements ActionListener //实现 ActionListener 接口的类
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone,the time is "+now);
Toolkit.getDefaultToolkit().beep();
//Toolkit.getDefaultToolkit():获得默认工具箱,beep():发出一声铃响
}
}


   程序启动后,弹出 "Quit program?" 字样对话框,每10秒显示一次定时器消息,按 OK 退出

           运行结果:



4、内部类

      内部类(inner class)是定义在另一个类中的类,使用内部类的原因:

            a)内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据

             b)内部类可以对同一个包中的其他类隐藏起来

             c)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷

   (1)使用内部类访问对象状态

            内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。为了说明这个概念

我们将外围类对象的引用称为 outer,下面的 if(beep)相当于 if(outer.beep)

               实例:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class InnerClassTest
{
public static void main(String[] agrs)
{
TalkingClock clock = new TalkingClock(1000,true);
clock.start();

JOptionPane.showMessageDialog(null,"Quit program?");
System.exit(0);
}
}

class TalkingClock
{
public TalkingClock(int interval,boolean beep)
{
this.interval = interval;
this.beep = beep;
}

public void start()
{
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval,listener);
t.start();
}

private int interval;
private boolean beep;

public class TimePrinter implements ActionListener //内部类
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone,the time is "+now);
if (beep) Toolkit.getDefaultToolkit().beep(); //这里的 beep 是引用外围类对象的数据域
}
}
}


       运行结果:



        (2)局部内部类

                上例中,TimePrinter 这个类名字只在 start 方法中创建这个类型的对象时使用了一次,因此,也可以就在此方法中定义局部类:

public void start()
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone,the time is "+now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}

ActionListener listener = new TimePrinter();
Timer t = new Timer(interval,listener);
t.start();
}


              局部类不能用 public 或 private 访问说明符进行声明,它的作用域被限定在声明这个局部类的块中。
              局部类有一个优势,即对外部世界(即使是 TalkingClock 类中的其他代码)可以完全地隐藏起来。

      (3)匿名内部类

               假如只创建这个局部内部类的一个对象,就不必命名了,这种类被称为匿名内部类。

               匿名类不能有构造器,取而代之的是将构造器参数传递给超类构造器。
               下面实例中的匿名内部类的含义是:创建一个实现 ActionListener 接口的类的新对象,需要实现的方法 actionPerformed 定义在括号内。

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class AnonymousInnerClassTest
{
public static void main(String[] agrs)
{
TalkingClock clock = new TalkingClock();
clock.start(1000,true);

JOptionPane.showMessageDialog(null,"Quit program?");
System.exit(0);
}
}

class TalkingClock
{
public void start(int interval,final boolean beep)
{
ActionListener listener = new ActionListener()//匿名内部类
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone,the time is "+now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval,listener);
t.start();
}
}


      (4)静态内部类

               有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围对象,为此,可以将内部类声明为 static。

public class StaticInnerClassTest
{
public static void main(String[] args)
{
double[] d = new double[20];
for (int i = 0; i < d.length; i++)
d[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}

class ArrayAlg
{
public static class Pair //包含最大值最小值的类
{
public Pair(double f, double s)
{
first = f;
second = s;
}

public double getFirst()
{
return first;
}

public double getSecond()
{
return second;
}

private double first;
private double second;
}

public static Pair minmax(double[] values)//计算最大最小值的方法
{
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}


        运行结果:



                              注:声明在接口中的内部类自动成为 static 和 public。

5、代理

       利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定要实现哪个接口时才有必要使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 接口